/-
Copyright (c) 2018 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Kevin Buzzard, Scott Morrison, Johan Commelin, Chris Hughes,
  Johannes Hölzl, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.group.commute
import Mathlib.algebra.group_with_zero.defs
import Mathlib.PostPort

universes u_6 u_7 l u_1 u_2 u_3 u_4 u_5

namespace Mathlib

/-!
# monoid and group homomorphisms

This file defines the bundled structures for monoid and group homomorphisms. Namely, we define
`monoid_hom` (resp., `add_monoid_hom`) to be bundled homomorphisms between multiplicative (resp.,
additive) monoids or groups.

We also define coercion to a function, and  usual operations: composition, identity homomorphism,
pointwise multiplication and pointwise inversion.

This file also defines the lesser-used (and notation-less) homomorphism types which are used as
building blocks for other homomorphisms:

* `zero_hom`
* `one_hom`
* `add_hom`
* `mul_hom`
* `monoid_with_zero_hom`

## Notations

* `→*` for bundled monoid homs (also use for group homs)
* `→+` for bundled add_monoid homs (also use for add_group homs)

## implementation notes

There's a coercion from bundled homs to fun, and the canonical
notation is to use the bundled hom as a function via this coercion.

There is no `group_hom` -- the idea is that `monoid_hom` is used.
The constructor for `monoid_hom` needs a proof of `map_one` as well
as `map_mul`; a separate constructor `monoid_hom.mk'` will construct
group homs (i.e. monoid homs between groups) given only a proof
that multiplication is preserved,

Implicit `{}` brackets are often used instead of type class `[]` brackets.  This is done when the
instances can be inferred because they are implicit arguments to the type `monoid_hom`.  When they
can be inferred from the type it is faster to use this method than to use type class inference.

Historically this file also included definitions of unbundled homomorphism classes; they were
deprecated and moved to `deprecated/group`.

## Tags

monoid_hom, add_monoid_hom

-/

-- for easy multiple inheritance

/-- Homomorphism that preserves zero -/
structure zero_hom (M : Type u_6) (N : Type u_7) [HasZero M] [HasZero N] where
  to_fun : M → N
  map_zero' : to_fun 0 = 0

/-- Homomorphism that preserves addition -/
structure add_hom (M : Type u_6) (N : Type u_7) [Add M] [Add N] where
  to_fun : M → N
  map_add' : ∀ (x y : M), to_fun (x + y) = to_fun x + to_fun y

/-- Bundled add_monoid homomorphisms; use this for bundled add_group homomorphisms too. -/
structure add_monoid_hom (M : Type u_6) (N : Type u_7) [add_monoid M] [add_monoid N]
    extends zero_hom M N, add_hom M N where

infixr:25 " →+ " => Mathlib.add_monoid_hom

/-- Homomorphism that preserves one -/
structure one_hom (M : Type u_6) (N : Type u_7) [HasOne M] [HasOne N] where
  to_fun : M → N
  map_one' : to_fun 1 = 1

/-- Homomorphism that preserves multiplication -/
structure mul_hom (M : Type u_6) (N : Type u_7) [Mul M] [Mul N] where
  to_fun : M → N
  map_mul' : ∀ (x y : M), to_fun (x * y) = to_fun x * to_fun y

/-- Bundled monoid homomorphisms; use this for bundled group homomorphisms too. -/
structure monoid_hom (M : Type u_6) (N : Type u_7) [monoid M] [monoid N]
    extends one_hom M N, mul_hom M N where

/-- Bundled monoid with zero homomorphisms; use this for bundled group with zero homomorphisms
too. -/
structure monoid_with_zero_hom (M : Type u_6) (N : Type u_7) [monoid_with_zero M]
    [monoid_with_zero N]
    extends zero_hom M N, monoid_hom M N where

infixr:25 " →* " => Mathlib.monoid_hom

-- completely uninteresting lemmas about coercion to function, that all homs need

/-! Bundled morphisms can be down-cast to weaker bundlings -/

protected instance monoid_hom.has_coe_to_one_hom {M : Type u_1} {N : Type u_2} {mM : monoid M}
    {mN : monoid N} : has_coe (M →* N) (one_hom M N) :=
  has_coe.mk monoid_hom.to_one_hom

protected instance add_monoid_hom.has_coe_to_add_hom {M : Type u_1} {N : Type u_2}
    {mM : add_monoid M} {mN : add_monoid N} : has_coe (M →+ N) (add_hom M N) :=
  has_coe.mk add_monoid_hom.to_add_hom

protected instance monoid_with_zero_hom.has_coe_to_monoid_hom {M : Type u_1} {N : Type u_2}
    {mM : monoid_with_zero M} {mN : monoid_with_zero N} :
    has_coe (monoid_with_zero_hom M N) (M →* N) :=
  has_coe.mk monoid_with_zero_hom.to_monoid_hom

protected instance monoid_with_zero_hom.has_coe_to_zero_hom {M : Type u_1} {N : Type u_2}
    {mM : monoid_with_zero M} {mN : monoid_with_zero N} :
    has_coe (monoid_with_zero_hom M N) (zero_hom M N) :=
  has_coe.mk monoid_with_zero_hom.to_zero_hom

/-! The simp-normal form of morphism coercion is `f.to_..._hom`. This choice is primarily because
this is the way things were before the above coercions were introduced. Bundled morphisms defined
elsewhere in Mathlib may choose `↑f` as their simp-normal form instead. -/

@[simp] theorem monoid_hom.coe_eq_to_one_hom {M : Type u_1} {N : Type u_2} {mM : monoid M}
    {mN : monoid N} (f : M →* N) : ↑f = monoid_hom.to_one_hom f :=
  rfl

@[simp] theorem add_monoid_hom.coe_eq_to_add_hom {M : Type u_1} {N : Type u_2} {mM : add_monoid M}
    {mN : add_monoid N} (f : M →+ N) : ↑f = add_monoid_hom.to_add_hom f :=
  rfl

@[simp] theorem monoid_with_zero_hom.coe_eq_to_monoid_hom {M : Type u_1} {N : Type u_2}
    {mM : monoid_with_zero M} {mN : monoid_with_zero N} (f : monoid_with_zero_hom M N) :
    ↑f = monoid_with_zero_hom.to_monoid_hom f :=
  rfl

@[simp] theorem monoid_with_zero_hom.coe_eq_to_zero_hom {M : Type u_1} {N : Type u_2}
    {mM : monoid_with_zero M} {mN : monoid_with_zero N} (f : monoid_with_zero_hom M N) :
    ↑f = monoid_with_zero_hom.to_zero_hom f :=
  rfl

protected instance zero_hom.has_coe_to_fun {M : Type u_1} {N : Type u_2} {mM : HasZero M}
    {mN : HasZero N} : has_coe_to_fun (zero_hom M N) :=
  has_coe_to_fun.mk (fun (x : zero_hom M N) => M → N) zero_hom.to_fun

protected instance mul_hom.has_coe_to_fun {M : Type u_1} {N : Type u_2} {mM : Mul M} {mN : Mul N} :
    has_coe_to_fun (mul_hom M N) :=
  has_coe_to_fun.mk (fun (x : mul_hom M N) => M → N) mul_hom.to_fun

protected instance add_monoid_hom.has_coe_to_fun {M : Type u_1} {N : Type u_2} {mM : add_monoid M}
    {mN : add_monoid N} : has_coe_to_fun (M →+ N) :=
  has_coe_to_fun.mk (fun (x : M →+ N) => M → N) add_monoid_hom.to_fun

protected instance monoid_with_zero_hom.has_coe_to_fun {M : Type u_1} {N : Type u_2}
    {mM : monoid_with_zero M} {mN : monoid_with_zero N} :
    has_coe_to_fun (monoid_with_zero_hom M N) :=
  has_coe_to_fun.mk (fun (x : monoid_with_zero_hom M N) => M → N) monoid_with_zero_hom.to_fun

-- these must come after the coe_to_fun definitions

@[simp] theorem zero_hom.to_fun_eq_coe {M : Type u_1} {N : Type u_2} [HasZero M] [HasZero N]
    (f : zero_hom M N) : zero_hom.to_fun f = ⇑f :=
  rfl

@[simp] theorem mul_hom.to_fun_eq_coe {M : Type u_1} {N : Type u_2} [Mul M] [Mul N]
    (f : mul_hom M N) : mul_hom.to_fun f = ⇑f :=
  rfl

@[simp] theorem add_monoid_hom.to_fun_eq_coe {M : Type u_1} {N : Type u_2} [add_monoid M]
    [add_monoid N] (f : M →+ N) : add_monoid_hom.to_fun f = ⇑f :=
  rfl

@[simp] theorem monoid_with_zero_hom.to_fun_eq_coe {M : Type u_1} {N : Type u_2}
    [monoid_with_zero M] [monoid_with_zero N] (f : monoid_with_zero_hom M N) :
    monoid_with_zero_hom.to_fun f = ⇑f :=
  rfl

@[simp] theorem one_hom.coe_mk {M : Type u_1} {N : Type u_2} [HasOne M] [HasOne N] (f : M → N)
    (h1 : f 1 = 1) : ⇑(one_hom.mk f h1) = f :=
  rfl

@[simp] theorem add_hom.coe_mk {M : Type u_1} {N : Type u_2} [Add M] [Add N] (f : M → N)
    (hmul : ∀ (x y : M), f (x + y) = f x + f y) : ⇑(add_hom.mk f hmul) = f :=
  rfl

@[simp] theorem add_monoid_hom.coe_mk {M : Type u_1} {N : Type u_2} [add_monoid M] [add_monoid N]
    (f : M → N) (h1 : f 0 = 0) (hmul : ∀ (x y : M), f (x + y) = f x + f y) :
    ⇑(add_monoid_hom.mk f h1 hmul) = f :=
  rfl

@[simp] theorem monoid_with_zero_hom.coe_mk {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] (f : M → N) (h0 : f 0 = 0) (h1 : f 1 = 1)
    (hmul : ∀ (x y : M), f (x * y) = f x * f y) : ⇑(monoid_with_zero_hom.mk f h0 h1 hmul) = f :=
  rfl

@[simp] theorem monoid_hom.to_one_hom_coe {M : Type u_1} {N : Type u_2} [monoid M] [monoid N]
    (f : M →* N) : ⇑(monoid_hom.to_one_hom f) = ⇑f :=
  rfl

@[simp] theorem add_monoid_hom.to_add_hom_coe {M : Type u_1} {N : Type u_2} [add_monoid M]
    [add_monoid N] (f : M →+ N) : ⇑(add_monoid_hom.to_add_hom f) = ⇑f :=
  rfl

@[simp] theorem monoid_with_zero_hom.to_zero_hom_coe {M : Type u_1} {N : Type u_2}
    [monoid_with_zero M] [monoid_with_zero N] (f : monoid_with_zero_hom M N) :
    ⇑(monoid_with_zero_hom.to_zero_hom f) = ⇑f :=
  rfl

@[simp] theorem monoid_with_zero_hom.to_monoid_hom_coe {M : Type u_1} {N : Type u_2}
    [monoid_with_zero M] [monoid_with_zero N] (f : monoid_with_zero_hom M N) :
    ⇑(monoid_with_zero_hom.to_monoid_hom f) = ⇑f :=
  rfl

theorem one_hom.congr_fun {M : Type u_1} {N : Type u_2} [HasOne M] [HasOne N] {f : one_hom M N}
    {g : one_hom M N} (h : f = g) (x : M) : coe_fn f x = coe_fn g x :=
  congr_arg (fun (h : one_hom M N) => coe_fn h x) h

theorem add_hom.congr_fun {M : Type u_1} {N : Type u_2} [Add M] [Add N] {f : add_hom M N}
    {g : add_hom M N} (h : f = g) (x : M) : coe_fn f x = coe_fn g x :=
  congr_arg (fun (h : add_hom M N) => coe_fn h x) h

theorem add_monoid_hom.congr_fun {M : Type u_1} {N : Type u_2} [add_monoid M] [add_monoid N]
    {f : M →+ N} {g : M →+ N} (h : f = g) (x : M) : coe_fn f x = coe_fn g x :=
  congr_arg (fun (h : M →+ N) => coe_fn h x) h

theorem monoid_with_zero_hom.congr_fun {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] {f : monoid_with_zero_hom M N} {g : monoid_with_zero_hom M N} (h : f = g)
    (x : M) : coe_fn f x = coe_fn g x :=
  congr_arg (fun (h : monoid_with_zero_hom M N) => coe_fn h x) h

theorem one_hom.congr_arg {M : Type u_1} {N : Type u_2} [HasOne M] [HasOne N] (f : one_hom M N)
    {x : M} {y : M} (h : x = y) : coe_fn f x = coe_fn f y :=
  congr_arg (fun (x : M) => coe_fn f x) h

theorem add_hom.congr_arg {M : Type u_1} {N : Type u_2} [Add M] [Add N] (f : add_hom M N) {x : M}
    {y : M} (h : x = y) : coe_fn f x = coe_fn f y :=
  congr_arg (fun (x : M) => coe_fn f x) h

theorem add_monoid_hom.congr_arg {M : Type u_1} {N : Type u_2} [add_monoid M] [add_monoid N]
    (f : M →+ N) {x : M} {y : M} (h : x = y) : coe_fn f x = coe_fn f y :=
  congr_arg (fun (x : M) => coe_fn f x) h

theorem monoid_with_zero_hom.congr_arg {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] (f : monoid_with_zero_hom M N) {x : M} {y : M} (h : x = y) :
    coe_fn f x = coe_fn f y :=
  congr_arg (fun (x : M) => coe_fn f x) h

theorem one_hom.coe_inj {M : Type u_1} {N : Type u_2} [HasOne M] [HasOne N] {f : one_hom M N}
    {g : one_hom M N} (h : ⇑f = ⇑g) : f = g :=
  sorry

theorem mul_hom.coe_inj {M : Type u_1} {N : Type u_2} [Mul M] [Mul N] {f : mul_hom M N}
    {g : mul_hom M N} (h : ⇑f = ⇑g) : f = g :=
  sorry

theorem add_monoid_hom.coe_inj {M : Type u_1} {N : Type u_2} [add_monoid M] [add_monoid N]
    {f : M →+ N} {g : M →+ N} (h : ⇑f = ⇑g) : f = g :=
  sorry

theorem monoid_with_zero_hom.coe_inj {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] {f : monoid_with_zero_hom M N} {g : monoid_with_zero_hom M N}
    (h : ⇑f = ⇑g) : f = g :=
  sorry

theorem zero_hom.ext {M : Type u_1} {N : Type u_2} [HasZero M] [HasZero N] {f : zero_hom M N}
    {g : zero_hom M N} (h : ∀ (x : M), coe_fn f x = coe_fn g x) : f = g :=
  zero_hom.coe_inj (funext h)

theorem mul_hom.ext {M : Type u_1} {N : Type u_2} [Mul M] [Mul N] {f : mul_hom M N}
    {g : mul_hom M N} (h : ∀ (x : M), coe_fn f x = coe_fn g x) : f = g :=
  mul_hom.coe_inj (funext h)

theorem add_monoid_hom.ext {M : Type u_1} {N : Type u_2} [add_monoid M] [add_monoid N] {f : M →+ N}
    {g : M →+ N} (h : ∀ (x : M), coe_fn f x = coe_fn g x) : f = g :=
  add_monoid_hom.coe_inj (funext h)

theorem monoid_with_zero_hom.ext {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] {f : monoid_with_zero_hom M N} {g : monoid_with_zero_hom M N}
    (h : ∀ (x : M), coe_fn f x = coe_fn g x) : f = g :=
  monoid_with_zero_hom.coe_inj (funext h)

theorem zero_hom.ext_iff {M : Type u_1} {N : Type u_2} [HasZero M] [HasZero N] {f : zero_hom M N}
    {g : zero_hom M N} : f = g ↔ ∀ (x : M), coe_fn f x = coe_fn g x :=
  { mp := fun (h : f = g) (x : M) => h ▸ rfl,
    mpr := fun (h : ∀ (x : M), coe_fn f x = coe_fn g x) => zero_hom.ext h }

theorem mul_hom.ext_iff {M : Type u_1} {N : Type u_2} [Mul M] [Mul N] {f : mul_hom M N}
    {g : mul_hom M N} : f = g ↔ ∀ (x : M), coe_fn f x = coe_fn g x :=
  { mp := fun (h : f = g) (x : M) => h ▸ rfl,
    mpr := fun (h : ∀ (x : M), coe_fn f x = coe_fn g x) => mul_hom.ext h }

theorem add_monoid_hom.ext_iff {M : Type u_1} {N : Type u_2} [add_monoid M] [add_monoid N]
    {f : M →+ N} {g : M →+ N} : f = g ↔ ∀ (x : M), coe_fn f x = coe_fn g x :=
  { mp := fun (h : f = g) (x : M) => h ▸ rfl,
    mpr := fun (h : ∀ (x : M), coe_fn f x = coe_fn g x) => add_monoid_hom.ext h }

theorem monoid_with_zero_hom.ext_iff {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] {f : monoid_with_zero_hom M N} {g : monoid_with_zero_hom M N} :
    f = g ↔ ∀ (x : M), coe_fn f x = coe_fn g x :=
  { mp := fun (h : f = g) (x : M) => h ▸ rfl,
    mpr := fun (h : ∀ (x : M), coe_fn f x = coe_fn g x) => monoid_with_zero_hom.ext h }

@[simp] theorem zero_hom.mk_coe {M : Type u_1} {N : Type u_2} [HasZero M] [HasZero N]
    (f : zero_hom M N) (h1 : coe_fn f 0 = 0) : zero_hom.mk (⇑f) h1 = f :=
  zero_hom.ext fun (_x : M) => rfl

@[simp] theorem mul_hom.mk_coe {M : Type u_1} {N : Type u_2} [Mul M] [Mul N] (f : mul_hom M N)
    (hmul : ∀ (x y : M), coe_fn f (x * y) = coe_fn f x * coe_fn f y) : mul_hom.mk (⇑f) hmul = f :=
  mul_hom.ext fun (_x : M) => rfl

@[simp] theorem add_monoid_hom.mk_coe {M : Type u_1} {N : Type u_2} [add_monoid M] [add_monoid N]
    (f : M →+ N) (h1 : coe_fn f 0 = 0)
    (hmul : ∀ (x y : M), coe_fn f (x + y) = coe_fn f x + coe_fn f y) :
    add_monoid_hom.mk (⇑f) h1 hmul = f :=
  add_monoid_hom.ext fun (_x : M) => rfl

@[simp] theorem monoid_with_zero_hom.mk_coe {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] (f : monoid_with_zero_hom M N) (h0 : coe_fn f 0 = 0) (h1 : coe_fn f 1 = 1)
    (hmul : ∀ (x y : M), coe_fn f (x * y) = coe_fn f x * coe_fn f y) :
    monoid_with_zero_hom.mk (⇑f) h0 h1 hmul = f :=
  monoid_with_zero_hom.ext fun (_x : M) => rfl

/-- If `f` is a monoid homomorphism then `f 1 = 1`. -/
@[simp] theorem one_hom.map_one {M : Type u_1} {N : Type u_2} [HasOne M] [HasOne N]
    (f : one_hom M N) : coe_fn f 1 = 1 :=
  one_hom.map_one' f

@[simp] theorem monoid_hom.map_one {M : Type u_1} {N : Type u_2} [monoid M] [monoid N]
    (f : M →* N) : coe_fn f 1 = 1 :=
  monoid_hom.map_one' f

@[simp] theorem monoid_with_zero_hom.map_one {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] (f : monoid_with_zero_hom M N) : coe_fn f 1 = 1 :=
  monoid_with_zero_hom.map_one' f

/-- If `f` is an additive monoid homomorphism then `f 0 = 0`. -/
@[simp] theorem monoid_with_zero_hom.map_zero {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] (f : monoid_with_zero_hom M N) : coe_fn f 0 = 0 :=
  monoid_with_zero_hom.map_zero' f

@[simp] theorem mul_hom.map_mul {M : Type u_1} {N : Type u_2} [Mul M] [Mul N] (f : mul_hom M N)
    (a : M) (b : M) : coe_fn f (a * b) = coe_fn f a * coe_fn f b :=
  mul_hom.map_mul' f a b

/-- If `f` is a monoid homomorphism then `f (a * b) = f a * f b`. -/
@[simp] theorem add_monoid_hom.map_add {M : Type u_1} {N : Type u_2} [add_monoid M] [add_monoid N]
    (f : M →+ N) (a : M) (b : M) : coe_fn f (a + b) = coe_fn f a + coe_fn f b :=
  add_monoid_hom.map_add' f a b

@[simp] theorem monoid_with_zero_hom.map_mul {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] (f : monoid_with_zero_hom M N) (a : M) (b : M) :
    coe_fn f (a * b) = coe_fn f a * coe_fn f b :=
  monoid_with_zero_hom.map_mul' f a b

/-- If `f` is an additive monoid homomorphism then `f (a + b) = f a + f b`. -/
namespace monoid_hom

theorem Mathlib.add_monoid_hom.map_add_eq_zero {M : Type u_1} {N : Type u_2} {mM : add_monoid M}
    {mN : add_monoid N} (f : M →+ N) {a : M} {b : M} (h : a + b = 0) :
    coe_fn f a + coe_fn f b = 0 :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (coe_fn f a + coe_fn f b = 0)) (Eq.symm (add_monoid_hom.map_add f a b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (coe_fn f (a + b) = 0)) h))
      (eq.mpr (id (Eq._oldrec (Eq.refl (coe_fn f 0 = 0)) (add_monoid_hom.map_zero f))) (Eq.refl 0)))

/-- Given a monoid homomorphism `f : M →* N` and an element `x : M`, if `x` has a right inverse,
then `f x` has a right inverse too. For elements invertible on both sides see `is_unit.map`. -/
theorem Mathlib.add_monoid_hom.map_exists_right_neg {M : Type u_1} {N : Type u_2}
    {mM : add_monoid M} {mN : add_monoid N} (f : M →+ N) {x : M} (hx : ∃ (y : M), x + y = 0) :
    ∃ (y : N), coe_fn f x + y = 0 :=
  sorry

/-- Given a monoid homomorphism `f : M →* N` and an element `x : M`, if `x` has a left inverse,
then `f x` has a left inverse too. For elements invertible on both sides see `is_unit.map`. -/
theorem Mathlib.add_monoid_hom.map_exists_left_neg {M : Type u_1} {N : Type u_2} {mM : add_monoid M}
    {mN : add_monoid N} (f : M →+ N) {x : M} (hx : ∃ (y : M), y + x = 0) :
    ∃ (y : N), y + coe_fn f x = 0 :=
  sorry

end monoid_hom

/-- The identity map from a type with 1 to itself. -/
def one_hom.id (M : Type u_1) [HasOne M] : one_hom M M := one_hom.mk id sorry

/-- The identity map from a type with multiplication to itself. -/
def add_hom.id (M : Type u_1) [Add M] : add_hom M M := add_hom.mk id sorry

/-- The identity map from a monoid to itself. -/
def add_monoid_hom.id (M : Type u_1) [add_monoid M] : M →+ M := add_monoid_hom.mk id sorry sorry

/-- The identity map from a monoid_with_zero to itself. -/
def monoid_with_zero_hom.id (M : Type u_1) [monoid_with_zero M] : monoid_with_zero_hom M M :=
  monoid_with_zero_hom.mk id sorry sorry sorry

/-- The identity map from an type with zero to itself. -/
/-- The identity map from an type with addition to itself. -/
/-- The identity map from an additive monoid to itself. -/
@[simp] theorem zero_hom.id_apply {M : Type u_1} [HasZero M] (x : M) :
    coe_fn (zero_hom.id M) x = x :=
  rfl

@[simp] theorem mul_hom.id_apply {M : Type u_1} [Mul M] (x : M) : coe_fn (mul_hom.id M) x = x := rfl

@[simp] theorem monoid_hom.id_apply {M : Type u_1} [monoid M] (x : M) :
    coe_fn (monoid_hom.id M) x = x :=
  rfl

@[simp] theorem monoid_with_zero_hom.id_apply {M : Type u_1} [monoid_with_zero M] (x : M) :
    coe_fn (monoid_with_zero_hom.id M) x = x :=
  rfl

/-- Composition of `one_hom`s as a `one_hom`. -/
def zero_hom.comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [HasZero M] [HasZero N] [HasZero P]
    (hnp : zero_hom N P) (hmn : zero_hom M N) : zero_hom M P :=
  zero_hom.mk (⇑hnp ∘ ⇑hmn) sorry

/-- Composition of `mul_hom`s as a `mul_hom`. -/
def mul_hom.comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [Mul M] [Mul N] [Mul P]
    (hnp : mul_hom N P) (hmn : mul_hom M N) : mul_hom M P :=
  mul_hom.mk (⇑hnp ∘ ⇑hmn) sorry

/-- Composition of monoid morphisms as a monoid morphism. -/
def monoid_hom.comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [monoid M] [monoid N] [monoid P]
    (hnp : N →* P) (hmn : M →* N) : M →* P :=
  monoid_hom.mk (⇑hnp ∘ ⇑hmn) sorry sorry

/-- Composition of `monoid_with_zero_hom`s as a `monoid_with_zero_hom`. -/
def monoid_with_zero_hom.comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [monoid_with_zero M]
    [monoid_with_zero N] [monoid_with_zero P] (hnp : monoid_with_zero_hom N P)
    (hmn : monoid_with_zero_hom M N) : monoid_with_zero_hom M P :=
  monoid_with_zero_hom.mk (⇑hnp ∘ ⇑hmn) sorry sorry sorry

/-- Composition of `zero_hom`s as a `zero_hom`. -/
/-- Composition of `add_hom`s as a `add_hom`. -/
/-- Composition of additive monoid morphisms as an additive monoid morphism. -/
@[simp] theorem one_hom.coe_comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [HasOne M] [HasOne N]
    [HasOne P] (g : one_hom N P) (f : one_hom M N) : ⇑(one_hom.comp g f) = ⇑g ∘ ⇑f :=
  rfl

@[simp] theorem add_hom.coe_comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [Add M] [Add N]
    [Add P] (g : add_hom N P) (f : add_hom M N) : ⇑(add_hom.comp g f) = ⇑g ∘ ⇑f :=
  rfl

@[simp] theorem add_monoid_hom.coe_comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [add_monoid M]
    [add_monoid N] [add_monoid P] (g : N →+ P) (f : M →+ N) :
    ⇑(add_monoid_hom.comp g f) = ⇑g ∘ ⇑f :=
  rfl

@[simp] theorem monoid_with_zero_hom.coe_comp {M : Type u_1} {N : Type u_2} {P : Type u_3}
    [monoid_with_zero M] [monoid_with_zero N] [monoid_with_zero P] (g : monoid_with_zero_hom N P)
    (f : monoid_with_zero_hom M N) : ⇑(monoid_with_zero_hom.comp g f) = ⇑g ∘ ⇑f :=
  rfl

theorem one_hom.comp_apply {M : Type u_1} {N : Type u_2} {P : Type u_3} [HasOne M] [HasOne N]
    [HasOne P] (g : one_hom N P) (f : one_hom M N) (x : M) :
    coe_fn (one_hom.comp g f) x = coe_fn g (coe_fn f x) :=
  rfl

theorem add_hom.comp_apply {M : Type u_1} {N : Type u_2} {P : Type u_3} [Add M] [Add N] [Add P]
    (g : add_hom N P) (f : add_hom M N) (x : M) :
    coe_fn (add_hom.comp g f) x = coe_fn g (coe_fn f x) :=
  rfl

theorem monoid_hom.comp_apply {M : Type u_1} {N : Type u_2} {P : Type u_3} [monoid M] [monoid N]
    [monoid P] (g : N →* P) (f : M →* N) (x : M) :
    coe_fn (monoid_hom.comp g f) x = coe_fn g (coe_fn f x) :=
  rfl

theorem monoid_with_zero_hom.comp_apply {M : Type u_1} {N : Type u_2} {P : Type u_3}
    [monoid_with_zero M] [monoid_with_zero N] [monoid_with_zero P] (g : monoid_with_zero_hom N P)
    (f : monoid_with_zero_hom M N) (x : M) :
    coe_fn (monoid_with_zero_hom.comp g f) x = coe_fn g (coe_fn f x) :=
  rfl

/-- Composition of monoid homomorphisms is associative. -/
theorem zero_hom.comp_assoc {M : Type u_1} {N : Type u_2} {P : Type u_3} {Q : Type u_4} [HasZero M]
    [HasZero N] [HasZero P] [HasZero Q] (f : zero_hom M N) (g : zero_hom N P) (h : zero_hom P Q) :
    zero_hom.comp (zero_hom.comp h g) f = zero_hom.comp h (zero_hom.comp g f) :=
  rfl

theorem mul_hom.comp_assoc {M : Type u_1} {N : Type u_2} {P : Type u_3} {Q : Type u_4} [Mul M]
    [Mul N] [Mul P] [Mul Q] (f : mul_hom M N) (g : mul_hom N P) (h : mul_hom P Q) :
    mul_hom.comp (mul_hom.comp h g) f = mul_hom.comp h (mul_hom.comp g f) :=
  rfl

theorem add_monoid_hom.comp_assoc {M : Type u_1} {N : Type u_2} {P : Type u_3} {Q : Type u_4}
    [add_monoid M] [add_monoid N] [add_monoid P] [add_monoid Q] (f : M →+ N) (g : N →+ P)
    (h : P →+ Q) :
    add_monoid_hom.comp (add_monoid_hom.comp h g) f =
        add_monoid_hom.comp h (add_monoid_hom.comp g f) :=
  rfl

theorem monoid_with_zero_hom.comp_assoc {M : Type u_1} {N : Type u_2} {P : Type u_3} {Q : Type u_4}
    [monoid_with_zero M] [monoid_with_zero N] [monoid_with_zero P] [monoid_with_zero Q]
    (f : monoid_with_zero_hom M N) (g : monoid_with_zero_hom N P) (h : monoid_with_zero_hom P Q) :
    monoid_with_zero_hom.comp (monoid_with_zero_hom.comp h g) f =
        monoid_with_zero_hom.comp h (monoid_with_zero_hom.comp g f) :=
  rfl

theorem one_hom.cancel_right {M : Type u_1} {N : Type u_2} {P : Type u_3} [HasOne M] [HasOne N]
    [HasOne P] {g₁ : one_hom N P} {g₂ : one_hom N P} {f : one_hom M N}
    (hf : function.surjective ⇑f) : one_hom.comp g₁ f = one_hom.comp g₂ f ↔ g₁ = g₂ :=
  sorry

theorem add_hom.cancel_right {M : Type u_1} {N : Type u_2} {P : Type u_3} [Add M] [Add N] [Add P]
    {g₁ : add_hom N P} {g₂ : add_hom N P} {f : add_hom M N} (hf : function.surjective ⇑f) :
    add_hom.comp g₁ f = add_hom.comp g₂ f ↔ g₁ = g₂ :=
  sorry

theorem add_monoid_hom.cancel_right {M : Type u_1} {N : Type u_2} {P : Type u_3} [add_monoid M]
    [add_monoid N] [add_monoid P] {g₁ : N →+ P} {g₂ : N →+ P} {f : M →+ N}
    (hf : function.surjective ⇑f) : add_monoid_hom.comp g₁ f = add_monoid_hom.comp g₂ f ↔ g₁ = g₂ :=
  sorry

theorem monoid_with_zero_hom.cancel_right {M : Type u_1} {N : Type u_2} {P : Type u_3}
    [monoid_with_zero M] [monoid_with_zero N] [monoid_with_zero P] {g₁ : monoid_with_zero_hom N P}
    {g₂ : monoid_with_zero_hom N P} {f : monoid_with_zero_hom M N} (hf : function.surjective ⇑f) :
    monoid_with_zero_hom.comp g₁ f = monoid_with_zero_hom.comp g₂ f ↔ g₁ = g₂ :=
  sorry

theorem one_hom.cancel_left {M : Type u_1} {N : Type u_2} {P : Type u_3} [HasOne M] [HasOne N]
    [HasOne P] {g : one_hom N P} {f₁ : one_hom M N} {f₂ : one_hom M N}
    (hg : function.injective ⇑g) : one_hom.comp g f₁ = one_hom.comp g f₂ ↔ f₁ = f₂ :=
  sorry

theorem add_hom.cancel_left {M : Type u_1} {N : Type u_2} {P : Type u_3} [HasZero M] [HasZero N]
    [HasZero P] {g : zero_hom N P} {f₁ : zero_hom M N} {f₂ : zero_hom M N}
    (hg : function.injective ⇑g) : zero_hom.comp g f₁ = zero_hom.comp g f₂ ↔ f₁ = f₂ :=
  sorry

theorem monoid_hom.cancel_left {M : Type u_1} {N : Type u_2} {P : Type u_3} [monoid M] [monoid N]
    [monoid P] {g : N →* P} {f₁ : M →* N} {f₂ : M →* N} (hg : function.injective ⇑g) :
    monoid_hom.comp g f₁ = monoid_hom.comp g f₂ ↔ f₁ = f₂ :=
  sorry

theorem monoid_with_zero_hom.cancel_left {M : Type u_1} {N : Type u_2} {P : Type u_3}
    [monoid_with_zero M] [monoid_with_zero N] [monoid_with_zero P] {g : monoid_with_zero_hom N P}
    {f₁ : monoid_with_zero_hom M N} {f₂ : monoid_with_zero_hom M N} (hg : function.injective ⇑g) :
    monoid_with_zero_hom.comp g f₁ = monoid_with_zero_hom.comp g f₂ ↔ f₁ = f₂ :=
  sorry

@[simp] theorem one_hom.comp_id {M : Type u_1} {N : Type u_2} [HasOne M] [HasOne N]
    (f : one_hom M N) : one_hom.comp f (one_hom.id M) = f :=
  one_hom.ext fun (x : M) => rfl

@[simp] theorem mul_hom.comp_id {M : Type u_1} {N : Type u_2} [Mul M] [Mul N] (f : mul_hom M N) :
    mul_hom.comp f (mul_hom.id M) = f :=
  mul_hom.ext fun (x : M) => rfl

@[simp] theorem add_monoid_hom.comp_id {M : Type u_1} {N : Type u_2} [add_monoid M] [add_monoid N]
    (f : M →+ N) : add_monoid_hom.comp f (add_monoid_hom.id M) = f :=
  add_monoid_hom.ext fun (x : M) => rfl

@[simp] theorem monoid_with_zero_hom.comp_id {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] (f : monoid_with_zero_hom M N) :
    monoid_with_zero_hom.comp f (monoid_with_zero_hom.id M) = f :=
  monoid_with_zero_hom.ext fun (x : M) => rfl

@[simp] theorem zero_hom.id_comp {M : Type u_1} {N : Type u_2} [HasZero M] [HasZero N]
    (f : zero_hom M N) : zero_hom.comp (zero_hom.id N) f = f :=
  zero_hom.ext fun (x : M) => rfl

@[simp] theorem mul_hom.id_comp {M : Type u_1} {N : Type u_2} [Mul M] [Mul N] (f : mul_hom M N) :
    mul_hom.comp (mul_hom.id N) f = f :=
  mul_hom.ext fun (x : M) => rfl

@[simp] theorem monoid_hom.id_comp {M : Type u_1} {N : Type u_2} [monoid M] [monoid N]
    (f : M →* N) : monoid_hom.comp (monoid_hom.id N) f = f :=
  monoid_hom.ext fun (x : M) => rfl

@[simp] theorem monoid_with_zero_hom.id_comp {M : Type u_1} {N : Type u_2} [monoid_with_zero M]
    [monoid_with_zero N] (f : monoid_with_zero_hom M N) :
    monoid_with_zero_hom.comp (monoid_with_zero_hom.id N) f = f :=
  monoid_with_zero_hom.ext fun (x : M) => rfl

namespace monoid

/-- The monoid of endomorphisms. -/
protected def End (M : Type u_1) [monoid M] := M →* M

namespace End

protected instance monoid (M : Type u_1) [monoid M] : monoid (monoid.End M) :=
  mk monoid_hom.comp sorry (monoid_hom.id M) monoid_hom.id_comp monoid_hom.comp_id

protected instance inhabited (M : Type u_1) [monoid M] : Inhabited (monoid.End M) :=
  { default := 1 }

protected instance has_coe_to_fun (M : Type u_1) [monoid M] : has_coe_to_fun (monoid.End M) :=
  has_coe_to_fun.mk (fun (x : monoid.End M) => M → M) monoid_hom.to_fun

end End

@[simp] theorem coe_one (M : Type u_1) [monoid M] : ⇑1 = id := rfl

@[simp] theorem coe_mul (M : Type u_1) [monoid M] (f : monoid.End M) (g : monoid.End M) :
    ⇑(f * g) = ⇑f ∘ ⇑g :=
  rfl

end monoid

namespace add_monoid

/-- The monoid of endomorphisms. -/
protected def End (A : Type u_6) [add_monoid A] := A →+ A

namespace End

protected instance monoid (A : Type u_6) [add_monoid A] : monoid (add_monoid.End A) :=
  monoid.mk add_monoid_hom.comp sorry (add_monoid_hom.id A) add_monoid_hom.id_comp
    add_monoid_hom.comp_id

protected instance inhabited (A : Type u_6) [add_monoid A] : Inhabited (add_monoid.End A) :=
  { default := 1 }

protected instance has_coe_to_fun (A : Type u_6) [add_monoid A] :
    has_coe_to_fun (add_monoid.End A) :=
  has_coe_to_fun.mk (fun (x : add_monoid.End A) => A → A) add_monoid_hom.to_fun

end End

@[simp] theorem coe_one (A : Type u_6) [add_monoid A] : ⇑1 = id := rfl

@[simp] theorem coe_mul (A : Type u_6) [add_monoid A] (f : add_monoid.End A)
    (g : add_monoid.End A) : ⇑(f * g) = ⇑f ∘ ⇑g :=
  rfl

end add_monoid

/-- `1` is the homomorphism sending all elements to `1`. -/
/-- `1` is the multiplicative homomorphism sending all elements to `1`. -/
protected instance zero_hom.has_zero {M : Type u_1} {N : Type u_2} [HasZero M] [HasZero N] :
    HasZero (zero_hom M N) :=
  { zero := zero_hom.mk (fun (_x : M) => 0) sorry }

/-- `1` is the monoid homomorphism sending all elements to `1`. -/
protected instance add_hom.has_zero {M : Type u_1} {N : Type u_2} [Add M] [add_monoid N] :
    HasZero (add_hom M N) :=
  { zero := add_hom.mk (fun (_x : M) => 0) sorry }

protected instance add_monoid_hom.has_zero {M : Type u_1} {N : Type u_2} [add_monoid M]
    [add_monoid N] : HasZero (M →+ N) :=
  { zero := add_monoid_hom.mk (fun (_x : M) => 0) sorry sorry }

/-- `0` is the homomorphism sending all elements to `0`. -/
/-- `0` is the additive homomorphism sending all elements to `0`. -/
/-- `0` is the additive monoid homomorphism sending all elements to `0`. -/
@[simp] theorem one_hom.one_apply {M : Type u_1} {N : Type u_2} [HasOne M] [HasOne N] (x : M) :
    coe_fn 1 x = 1 :=
  rfl

@[simp] theorem add_monoid_hom.zero_apply {M : Type u_1} {N : Type u_2} [add_monoid M]
    [add_monoid N] (x : M) : coe_fn 0 x = 0 :=
  rfl

@[simp] theorem zero_hom.zero_comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [HasZero M]
    [HasZero N] [HasZero P] (f : zero_hom M N) : zero_hom.comp 0 f = 0 :=
  rfl

@[simp] theorem zero_hom.comp_zero {M : Type u_1} {N : Type u_2} {P : Type u_3} [HasZero M]
    [HasZero N] [HasZero P] (f : zero_hom N P) : zero_hom.comp f 0 = 0 :=
  sorry

protected instance one_hom.inhabited {M : Type u_1} {N : Type u_2} [HasOne M] [HasOne N] :
    Inhabited (one_hom M N) :=
  { default := 1 }

protected instance add_hom.inhabited {M : Type u_1} {N : Type u_2} [Add M] [add_monoid N] :
    Inhabited (add_hom M N) :=
  { default := 0 }

-- unlike the other homs, `monoid_with_zero_hom` does not have a `1` or `0`

protected instance add_monoid_hom.inhabited {M : Type u_1} {N : Type u_2} [add_monoid M]
    [add_monoid N] : Inhabited (M →+ N) :=
  { default := 0 }

protected instance monoid_with_zero_hom.inhabited {M : Type u_1} [monoid_with_zero M] :
    Inhabited (monoid_with_zero_hom M M) :=
  { default := monoid_with_zero_hom.id M }

namespace monoid_hom

/-- Given two monoid morphisms `f`, `g` to a commutative monoid, `f * g` is the monoid morphism
sending `x` to `f x * g x`. -/
protected instance has_mul {M : Type u_1} {N : Type u_2} {mM : monoid M} [comm_monoid N] :
    Mul (M →* N) :=
  { mul := fun (f g : M →* N) => mk (fun (m : M) => coe_fn f m * coe_fn g m) sorry sorry }

/-- Given two additive monoid morphisms `f`, `g` to an additive commutative monoid, `f + g` is the
additive monoid morphism sending `x` to `f x + g x`. -/
@[simp] theorem Mathlib.add_monoid_hom.add_apply {M : Type u_1} {N : Type u_2} {mM : add_monoid M}
    {mN : add_comm_monoid N} (f : M →+ N) (g : M →+ N) (x : M) :
    coe_fn (f + g) x = coe_fn f x + coe_fn g x :=
  rfl

@[simp] theorem one_comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [monoid M] [monoid N]
    [monoid P] (f : M →* N) : comp 1 f = 1 :=
  rfl

@[simp] theorem Mathlib.add_monoid_hom.comp_zero {M : Type u_1} {N : Type u_2} {P : Type u_3}
    [add_monoid M] [add_monoid N] [add_monoid P] (f : N →+ P) : add_monoid_hom.comp f 0 = 0 :=
  sorry

theorem mul_comp {M : Type u_1} {N : Type u_2} {P : Type u_3} [monoid M] [comm_monoid N]
    [comm_monoid P] (g₁ : N →* P) (g₂ : N →* P) (f : M →* N) :
    comp (g₁ * g₂) f = comp g₁ f * comp g₂ f :=
  rfl

theorem comp_mul {M : Type u_1} {N : Type u_2} {P : Type u_3} [monoid M] [comm_monoid N]
    [comm_monoid P] (g : N →* P) (f₁ : M →* N) (f₂ : M →* N) :
    comp g (f₁ * f₂) = comp g f₁ * comp g f₂ :=
  sorry

/-- (M →* N) is a comm_monoid if N is commutative. -/
protected instance comm_monoid {M : Type u_1} {N : Type u_2} [monoid M] [comm_monoid N] :
    comm_monoid (M →* N) :=
  comm_monoid.mk Mul.mul sorry 1 sorry sorry sorry

/-- `flip` arguments of `f : M →* N →* P` -/
def Mathlib.add_monoid_hom.flip {M : Type u_1} {N : Type u_2} {P : Type u_3} {mM : add_monoid M}
    {mN : add_monoid N} {mP : add_comm_monoid P} (f : M →+ N →+ P) : N →+ M →+ P :=
  add_monoid_hom.mk
    (fun (y : N) => add_monoid_hom.mk (fun (x : M) => coe_fn (coe_fn f x) y) sorry sorry) sorry
    sorry

@[simp] theorem flip_apply {M : Type u_1} {N : Type u_2} {P : Type u_3} {mM : monoid M}
    {mN : monoid N} {mP : comm_monoid P} (f : M →* N →* P) (x : M) (y : N) :
    coe_fn (coe_fn (flip f) y) x = coe_fn (coe_fn f x) y :=
  rfl

/-- Evaluation of a `monoid_hom` at a point as a monoid homomorphism. See also `monoid_hom.apply`
for the evaluation of any function at a point. -/
def Mathlib.add_monoid_hom.eval {M : Type u_1} {N : Type u_2} [add_monoid M] [add_comm_monoid N] :
    M →+ (M →+ N) →+ N :=
  add_monoid_hom.flip (add_monoid_hom.id (M →+ N))

@[simp] theorem Mathlib.add_monoid_hom.eval_apply {M : Type u_1} {N : Type u_2} [add_monoid M]
    [add_comm_monoid N] (x : M) (f : M →+ N) :
    coe_fn (coe_fn add_monoid_hom.eval x) f = coe_fn f x :=
  rfl

/-- Composition of monoid morphisms (`monoid_hom.comp`) as a monoid morphism. -/
def Mathlib.add_monoid_hom.comp_hom {M : Type u_1} {N : Type u_2} {P : Type u_3} [add_monoid M]
    [add_comm_monoid N] [add_comm_monoid P] : (N →+ P) →+ (M →+ N) →+ M →+ P :=
  add_monoid_hom.mk
    (fun (g : N →+ P) =>
      add_monoid_hom.mk (add_monoid_hom.comp g) sorry (add_monoid_hom.comp_add g))
    sorry sorry

/-- If two homomorphism from a group to a monoid are equal at `x`, then they are equal at `x⁻¹`. -/
theorem eq_on_inv {M : Type u_1} {G : Type u_2} [group G] [monoid M] {f : G →* M} {g : G →* M}
    {x : G} (h : coe_fn f x = coe_fn g x) : coe_fn f (x⁻¹) = coe_fn g (x⁻¹) :=
  left_inv_eq_right_inv (map_mul_eq_one f (inv_mul_self x))
    (Eq.subst (Eq.symm h) (map_mul_eq_one g) (mul_inv_self x))

/-- Group homomorphisms preserve inverse. -/
@[simp] theorem Mathlib.add_monoid_hom.map_neg {G : Type u_1} {H : Type u_2} [add_group G]
    [add_group H] (f : G →+ H) (g : G) : coe_fn f (-g) = -coe_fn f g :=
  eq_neg_of_add_eq_zero (add_monoid_hom.map_add_eq_zero f (neg_add_self g))

/-- Group homomorphisms preserve division. -/
@[simp] theorem Mathlib.add_monoid_hom.map_add_neg {G : Type u_1} {H : Type u_2} [add_group G]
    [add_group H] (f : G →+ H) (g : G) (h : G) : coe_fn f (g + -h) = coe_fn f g + -coe_fn f h :=
  sorry

/-- A homomorphism from a group to a monoid is injective iff its kernel is trivial. -/
theorem injective_iff {G : Type u_1} {H : Type u_2} [group G] [monoid H] (f : G →* H) :
    function.injective ⇑f ↔ ∀ (a : G), coe_fn f a = 1 → a = 1 :=
  sorry

/-- Makes a group homomorphism from a proof that the map preserves multiplication. -/
def Mathlib.add_monoid_hom.mk' {M : Type u_1} {G : Type u_4} [mM : add_monoid M] [add_group G]
    (f : M → G) (map_mul : ∀ (a b : M), f (a + b) = f a + f b) : M →+ G :=
  add_monoid_hom.mk f sorry map_mul

@[simp] theorem Mathlib.add_monoid_hom.coe_mk' {M : Type u_1} {G : Type u_4} [mM : add_monoid M]
    [add_group G] {f : M → G} (map_mul : ∀ (a b : M), f (a + b) = f a + f b) :
    ⇑(add_monoid_hom.mk' f map_mul) = f :=
  rfl

/-- Makes a group homomorphism from a proof that the map preserves right division `λ x y, x * y⁻¹`.
-/
def of_map_mul_inv {G : Type u_4} [group G] {H : Type u_1} [group H] (f : G → H)
    (map_div : ∀ (a b : G), f (a * (b⁻¹)) = f a * (f b⁻¹)) : G →* H :=
  mk' f sorry

@[simp] theorem Mathlib.add_monoid_hom.coe_of_map_add_neg {G : Type u_4} [add_group G]
    {H : Type u_1} [add_group H] (f : G → H) (map_div : ∀ (a b : G), f (a + -b) = f a + -f b) :
    ⇑(add_monoid_hom.of_map_add_neg f map_div) = f :=
  rfl

/-- If `f` is a monoid homomorphism to a commutative group, then `f⁻¹` is the homomorphism sending
`x` to `(f x)⁻¹`. -/
protected instance Mathlib.add_monoid_hom.has_neg {M : Type u_1} {G : Type u_2} [add_monoid M]
    [add_comm_group G] : Neg (M →+ G) :=
  { neg := fun (f : M →+ G) => add_monoid_hom.mk' (fun (g : M) => -coe_fn f g) sorry }

/-- If `f` is an additive monoid homomorphism to an additive commutative group, then `-f` is the
homomorphism sending `x` to `-(f x)`. -/
@[simp] theorem inv_apply {M : Type u_1} {G : Type u_2} {mM : monoid M} {gG : comm_group G}
    (f : M →* G) (x : M) : coe_fn (f⁻¹) x = (coe_fn f x⁻¹) :=
  rfl

/-- If `f` and `g` are monoid homomorphisms to a commutative group, then `f / g` is the homomorphism
sending `x` to `(f x) / (g x). -/
protected instance has_div {M : Type u_1} {G : Type u_2} [monoid M] [comm_group G] : Div (M →* G) :=
  { div := fun (f g : M →* G) => mk' (fun (x : M) => coe_fn f x / coe_fn g x) sorry }

/-- If `f` and `g` are monoid homomorphisms to an additive commutative group, then `f - g`
is the homomorphism sending `x` to `(f x) - (g x). -/
@[simp] theorem Mathlib.add_monoid_hom.sub_apply {M : Type u_1} {G : Type u_2} {mM : add_monoid M}
    {gG : add_comm_group G} (f : M →+ G) (g : M →+ G) (x : M) :
    coe_fn (f - g) x = coe_fn f x - coe_fn g x :=
  rfl

/-- If `G` is a commutative group, then `M →* G` a commutative group too. -/
protected instance Mathlib.add_monoid_hom.add_comm_group {M : Type u_1} {G : Type u_2}
    [add_monoid M] [add_comm_group G] : add_comm_group (M →+ G) :=
  add_comm_group.mk add_comm_monoid.add sorry add_comm_monoid.zero sorry sorry Neg.neg Sub.sub sorry
    sorry

/-- If `G` is an additive commutative group, then `M →+ G` an additive commutative group too. -/
end monoid_hom

namespace add_monoid_hom

/-- Additive group homomorphisms preserve subtraction. -/
@[simp] theorem map_sub {G : Type u_4} {H : Type u_5} [add_group G] [add_group H] (f : G →+ H)
    (g : G) (h : G) : coe_fn f (g - h) = coe_fn f g - coe_fn f h :=
  sorry

/-- Define a morphism of additive groups given a map which respects difference. -/
def of_map_sub {G : Type u_4} {H : Type u_5} [add_group G] [add_group H] (f : G → H)
    (hf : ∀ (x y : G), f (x - y) = f x - f y) : G →+ H :=
  of_map_add_neg f sorry

@[simp] theorem coe_of_map_sub {G : Type u_4} {H : Type u_5} [add_group G] [add_group H] (f : G → H)
    (hf : ∀ (x y : G), f (x - y) = f x - f y) : ⇑(of_map_sub f hf) = f :=
  rfl

end add_monoid_hom

@[simp] protected theorem add_semiconj_by.map {M : Type u_1} {N : Type u_2} [add_monoid M]
    [add_monoid N] {a : M} {x : M} {y : M} (h : add_semiconj_by a x y) (f : M →+ N) :
    add_semiconj_by (coe_fn f a) (coe_fn f x) (coe_fn f y) :=
  sorry

@[simp] protected theorem add_commute.map {M : Type u_1} {N : Type u_2} [add_monoid M]
    [add_monoid N] {x : M} {y : M} (h : add_commute x y) (f : M →+ N) :
    add_commute (coe_fn f x) (coe_fn f y) :=
  add_semiconj_by.map h f

end Mathlib


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

! This file was ported from Lean 3 source module ring_theory.perfection
! leanprover-community/mathlib commit b1d911acd60ab198808e853292106ee352b648ea
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.CharP.Pi
import Mathbin.Algebra.CharP.Quotient
import Mathbin.Algebra.CharP.Subring
import Mathbin.Algebra.Ring.Pi
import Mathbin.Analysis.SpecialFunctions.Pow
import Mathbin.FieldTheory.PerfectClosure
import Mathbin.RingTheory.Localization.FractionRing
import Mathbin.RingTheory.Subring.Basic
import Mathbin.RingTheory.Valuation.Integers

/-!
# Ring Perfection and Tilt

In this file we define the perfection of a ring of characteristic p, and the tilt of a field
given a valuation to `ℝ≥0`.

## TODO

Define the valuation on the tilt, and define a characteristic predicate for the tilt.

-/

universe u₁ u₂ u₃ u₄

open NNReal

/-- The perfection of a monoid `M`, defined to be the projective limit of `M`
using the `p`-th power maps `M → M` indexed by the natural numbers, implemented as
`{ f : ℕ → M | ∀ n, f (n + 1) ^ p = f n }`. -/
def Monoid.perfection (M : Type u₁) [CommMonoid M] (p : ℕ) : Submonoid (ℕ → M)
    where
  carrier := { f | ∀ n, f (n + 1) ^ p = f n }
  one_mem' n := one_pow _
  mul_mem' f g hf hg n := (mul_pow _ _ _).trans <| congr_arg₂ _ (hf n) (hg n)
#align monoid.perfection Monoid.perfection

/-- The perfection of a ring `R` with characteristic `p`, as a subsemiring,
defined to be the projective limit of `R` using the Frobenius maps `R → R`
indexed by the natural numbers, implemented as `{ f : ℕ → R | ∀ n, f (n + 1) ^ p = f n }`. -/
def Ring.perfectionSubsemiring (R : Type u₁) [CommSemiring R] (p : ℕ) [hp : Fact p.Prime]
    [CharP R p] : Subsemiring (ℕ → R) :=
  { Monoid.perfection R p with
    zero_mem' := fun n => zero_pow <| hp.1.Pos
    add_mem' := fun f g hf hg n => (frobenius_add R p _ _).trans <| congr_arg₂ _ (hf n) (hg n) }
#align ring.perfection_subsemiring Ring.perfectionSubsemiring

/-- The perfection of a ring `R` with characteristic `p`, as a subring,
defined to be the projective limit of `R` using the Frobenius maps `R → R`
indexed by the natural numbers, implemented as `{ f : ℕ → R | ∀ n, f (n + 1) ^ p = f n }`. -/
def Ring.perfectionSubring (R : Type u₁) [CommRing R] (p : ℕ) [hp : Fact p.Prime] [CharP R p] :
    Subring (ℕ → R) :=
  (Ring.perfectionSubsemiring R p).toSubring fun n => by
    simp_rw [← frobenius_def, Pi.neg_apply, Pi.one_apply, RingHom.map_neg, RingHom.map_one]
#align ring.perfection_subring Ring.perfectionSubring

/-- The perfection of a ring `R` with characteristic `p`,
defined to be the projective limit of `R` using the Frobenius maps `R → R`
indexed by the natural numbers, implemented as `{f : ℕ → R // ∀ n, f (n + 1) ^ p = f n}`. -/
def Ring.Perfection (R : Type u₁) [CommSemiring R] (p : ℕ) : Type u₁ :=
  { f // ∀ n : ℕ, (f : ℕ → R) (n + 1) ^ p = f n }
#align ring.perfection Ring.Perfection

namespace Perfection

variable (R : Type u₁) [CommSemiring R] (p : ℕ) [hp : Fact p.Prime] [CharP R p]

include hp

instance : CommSemiring (Ring.Perfection R p) :=
  (Ring.perfectionSubsemiring R p).toCommSemiring

instance : CharP (Ring.Perfection R p) p :=
  CharP.subsemiring (ℕ → R) p (Ring.perfectionSubsemiring R p)

instance ring (R : Type u₁) [CommRing R] [CharP R p] : Ring (Ring.Perfection R p) :=
  (Ring.perfectionSubring R p).toRing
#align perfection.ring Perfection.ring

instance commRing (R : Type u₁) [CommRing R] [CharP R p] : CommRing (Ring.Perfection R p) :=
  (Ring.perfectionSubring R p).toCommRing
#align perfection.comm_ring Perfection.commRing

instance : Inhabited (Ring.Perfection R p) :=
  ⟨0⟩

/-- The `n`-th coefficient of an element of the perfection. -/
def coeff (n : ℕ) : Ring.Perfection R p →+* R
    where
  toFun f := f.1 n
  map_one' := rfl
  map_mul' f g := rfl
  map_zero' := rfl
  map_add' f g := rfl
#align perfection.coeff Perfection.coeff

variable {R p}

@[ext]
theorem ext {f g : Ring.Perfection R p} (h : ∀ n, coeff R p n f = coeff R p n g) : f = g :=
  Subtype.eq <| funext h
#align perfection.ext Perfection.ext

variable (R p)

/-- The `p`-th root of an element of the perfection. -/
def pthRoot : Ring.Perfection R p →+* Ring.Perfection R p
    where
  toFun f := ⟨fun n => coeff R p (n + 1) f, fun n => f.2 _⟩
  map_one' := rfl
  map_mul' f g := rfl
  map_zero' := rfl
  map_add' f g := rfl
#align perfection.pth_root Perfection.pthRoot

variable {R p}

@[simp]
theorem coeff_mk (f : ℕ → R) (hf) (n : ℕ) : coeff R p n ⟨f, hf⟩ = f n :=
  rfl
#align perfection.coeff_mk Perfection.coeff_mk

theorem coeff_pthRoot (f : Ring.Perfection R p) (n : ℕ) :
    coeff R p n (pthRoot R p f) = coeff R p (n + 1) f :=
  rfl
#align perfection.coeff_pth_root Perfection.coeff_pthRoot

theorem coeff_pow_p (f : Ring.Perfection R p) (n : ℕ) : coeff R p (n + 1) (f ^ p) = coeff R p n f :=
  by
  rw [RingHom.map_pow]
  exact f.2 n
#align perfection.coeff_pow_p Perfection.coeff_pow_p

theorem coeff_pow_p' (f : Ring.Perfection R p) (n : ℕ) : coeff R p (n + 1) f ^ p = coeff R p n f :=
  f.2 n
#align perfection.coeff_pow_p' Perfection.coeff_pow_p'

theorem coeff_frobenius (f : Ring.Perfection R p) (n : ℕ) :
    coeff R p (n + 1) (frobenius _ p f) = coeff R p n f := by apply coeff_pow_p f n
#align perfection.coeff_frobenius Perfection.coeff_frobenius

-- `coeff_pow_p f n` also works but is slow!
theorem coeff_iterate_frobenius (f : Ring.Perfection R p) (n m : ℕ) :
    coeff R p (n + m) ((frobenius _ p^[m]) f) = coeff R p n f :=
  Nat.recOn m rfl fun m ih => by erw [Function.iterate_succ_apply', coeff_frobenius, ih]
#align perfection.coeff_iterate_frobenius Perfection.coeff_iterate_frobenius

theorem coeff_iterate_frobenius' (f : Ring.Perfection R p) (n m : ℕ) (hmn : m ≤ n) :
    coeff R p n ((frobenius _ p^[m]) f) = coeff R p (n - m) f :=
  Eq.symm <| (coeff_iterate_frobenius _ _ m).symm.trans <| (tsub_add_cancel_of_le hmn).symm ▸ rfl
#align perfection.coeff_iterate_frobenius' Perfection.coeff_iterate_frobenius'

theorem pthRoot_frobenius : (pthRoot R p).comp (frobenius _ p) = RingHom.id _ :=
  RingHom.ext fun x =>
    ext fun n => by rw [RingHom.comp_apply, RingHom.id_apply, coeff_pth_root, coeff_frobenius]
#align perfection.pth_root_frobenius Perfection.pthRoot_frobenius

theorem frobenius_pthRoot : (frobenius _ p).comp (pthRoot R p) = RingHom.id _ :=
  RingHom.ext fun x =>
    ext fun n => by
      rw [RingHom.comp_apply, RingHom.id_apply, RingHom.map_frobenius, coeff_pth_root, ←
        RingHom.map_frobenius, coeff_frobenius]
#align perfection.frobenius_pth_root Perfection.frobenius_pthRoot

theorem coeff_add_ne_zero {f : Ring.Perfection R p} {n : ℕ} (hfn : coeff R p n f ≠ 0) (k : ℕ) :
    coeff R p (n + k) f ≠ 0 :=
  Nat.recOn k hfn fun k ih h => ih <| by erw [← coeff_pow_p, RingHom.map_pow, h, zero_pow hp.1.Pos]
#align perfection.coeff_add_ne_zero Perfection.coeff_add_ne_zero

theorem coeff_ne_zero_of_le {f : Ring.Perfection R p} {m n : ℕ} (hfm : coeff R p m f ≠ 0)
    (hmn : m ≤ n) : coeff R p n f ≠ 0 :=
  let ⟨k, hk⟩ := Nat.exists_eq_add_of_le hmn
  hk.symm ▸ coeff_add_ne_zero hfm k
#align perfection.coeff_ne_zero_of_le Perfection.coeff_ne_zero_of_le

variable (R p)

instance perfectRing : PerfectRing (Ring.Perfection R p) p
    where
  pthRoot' := pthRoot R p
  frobenius_pthRoot' := congr_fun <| congr_arg RingHom.toFun <| @frobenius_pthRoot R _ p _ _
  pth_root_frobenius' := congr_fun <| congr_arg RingHom.toFun <| @pthRoot_frobenius R _ p _ _
#align perfection.perfect_ring Perfection.perfectRing

/-- Given rings `R` and `S` of characteristic `p`, with `R` being perfect,
any homomorphism `R →+* S` can be lifted to a homomorphism `R →+* perfection S p`. -/
@[simps]
def lift (R : Type u₁) [CommSemiring R] [CharP R p] [PerfectRing R p] (S : Type u₂) [CommSemiring S]
    [CharP S p] : (R →+* S) ≃ (R →+* Ring.Perfection S p)
    where
  toFun f :=
    { toFun := fun r =>
        ⟨fun n => f <| (pthRoot R p^[n]) r, fun n => by
          rw [← f.map_pow, Function.iterate_succ_apply', pthRoot_pow_p]⟩
      map_one' := ext fun n => (congr_arg f <| RingHom.iterate_map_one _ _).trans f.map_one
      map_mul' := fun x y =>
        ext fun n => (congr_arg f <| RingHom.iterate_map_mul _ _ _ _).trans <| f.map_mul _ _
      map_zero' := ext fun n => (congr_arg f <| RingHom.iterate_map_zero _ _).trans f.map_zero
      map_add' := fun x y =>
        ext fun n => (congr_arg f <| RingHom.iterate_map_add _ _ _ _).trans <| f.map_add _ _ }
  invFun := RingHom.comp <| coeff S p 0
  left_inv f := RingHom.ext fun r => rfl
  right_inv f :=
    RingHom.ext fun r =>
      ext fun n =>
        show coeff S p 0 (f ((pthRoot R p^[n]) r)) = coeff S p n (f r) by
          rw [← coeff_iterate_frobenius _ 0 n, zero_add, ← RingHom.map_iterate_frobenius,
            right_inverse_pth_root_frobenius.iterate]
#align perfection.lift Perfection.lift

theorem hom_ext {R : Type u₁} [CommSemiring R] [CharP R p] [PerfectRing R p] {S : Type u₂}
    [CommSemiring S] [CharP S p] {f g : R →+* Ring.Perfection S p}
    (hfg : ∀ x, coeff S p 0 (f x) = coeff S p 0 (g x)) : f = g :=
  (lift p R S).symm.Injective <| RingHom.ext hfg
#align perfection.hom_ext Perfection.hom_ext

variable {R} {S : Type u₂} [CommSemiring S] [CharP S p]

/-- A ring homomorphism `R →+* S` induces `perfection R p →+* perfection S p` -/
@[simps]
def map (φ : R →+* S) : Ring.Perfection R p →+* Ring.Perfection S p
    where
  toFun f := ⟨fun n => φ (coeff R p n f), fun n => by rw [← φ.map_pow, coeff_pow_p']⟩
  map_one' := Subtype.eq <| funext fun n => φ.map_one
  map_mul' f g := Subtype.eq <| funext fun n => φ.map_mul _ _
  map_zero' := Subtype.eq <| funext fun n => φ.map_zero
  map_add' f g := Subtype.eq <| funext fun n => φ.map_add _ _
#align perfection.map Perfection.map

theorem coeff_map (φ : R →+* S) (f : Ring.Perfection R p) (n : ℕ) :
    coeff S p n (map p φ f) = φ (coeff R p n f) :=
  rfl
#align perfection.coeff_map Perfection.coeff_map

end Perfection

/-- A perfection map to a ring of characteristic `p` is a map that is isomorphic
to its perfection. -/
@[nolint has_nonempty_instance]
structure PerfectionMap (p : ℕ) [Fact p.Prime] {R : Type u₁} [CommSemiring R] [CharP R p]
  {P : Type u₂} [CommSemiring P] [CharP P p] [PerfectRing P p] (π : P →+* R) : Prop where
  Injective : ∀ ⦃x y : P⦄, (∀ n, π ((pthRoot P p^[n]) x) = π ((pthRoot P p^[n]) y)) → x = y
  Surjective : ∀ f : ℕ → R, (∀ n, f (n + 1) ^ p = f n) → ∃ x : P, ∀ n, π ((pthRoot P p^[n]) x) = f n
#align perfection_map PerfectionMap

namespace PerfectionMap

variable {p : ℕ} [Fact p.Prime]

variable {R : Type u₁} [CommSemiring R] [CharP R p]

variable {P : Type u₃} [CommSemiring P] [CharP P p] [PerfectRing P p]

/-- Create a `perfection_map` from an isomorphism to the perfection. -/
@[simps]
theorem mk' {f : P →+* R} (g : P ≃+* Ring.Perfection R p) (hfg : Perfection.lift p P R f = g) :
    PerfectionMap p f :=
  { Injective := fun x y hxy =>
      g.Injective <|
        (RingHom.ext_iff.1 hfg x).symm.trans <|
          Eq.symm <| (RingHom.ext_iff.1 hfg y).symm.trans <| Perfection.ext fun n => (hxy n).symm
    Surjective := fun y hy =>
      let ⟨x, hx⟩ := g.Surjective ⟨y, hy⟩
      ⟨x, fun n =>
        show Perfection.coeff R p n (Perfection.lift p P R f x) = Perfection.coeff R p n ⟨y, hy⟩ by
          rw [hfg, ← coeFn_coeBase, hx]⟩ }
#align perfection_map.mk' PerfectionMap.mk'

variable (p R P)

/-- The canonical perfection map from the perfection of a ring. -/
theorem of : PerfectionMap p (Perfection.coeff R p 0) :=
  mk' (RingEquiv.refl _) <| (Equiv.apply_eq_iff_eq_symm_apply _).2 rfl
#align perfection_map.of PerfectionMap.of

/-- For a perfect ring, it itself is the perfection. -/
theorem id [PerfectRing R p] : PerfectionMap p (RingHom.id R) :=
  { Injective := fun x y hxy => hxy 0
    Surjective := fun f hf =>
      ⟨f 0, fun n =>
        show (pthRoot R p^[n]) (f 0) = f n from
          Nat.recOn n rfl fun n ih =>
            injective_pow_p p <| by rw [Function.iterate_succ_apply', pthRoot_pow_p _, ih, hf]⟩ }
#align perfection_map.id PerfectionMap.id

variable {p R P}

/-- A perfection map induces an isomorphism to the prefection. -/
noncomputable def equiv {π : P →+* R} (m : PerfectionMap p π) : P ≃+* Ring.Perfection R p :=
  RingEquiv.ofBijective (Perfection.lift p P R π)
    ⟨fun x y hxy => m.Injective fun n => (congr_arg (Perfection.coeff R p n) hxy : _), fun f =>
      let ⟨x, hx⟩ := m.Surjective f.1 f.2
      ⟨x, Perfection.ext <| hx⟩⟩
#align perfection_map.equiv PerfectionMap.equiv

theorem equiv_apply {π : P →+* R} (m : PerfectionMap p π) (x : P) :
    m.Equiv x = Perfection.lift p P R π x :=
  rfl
#align perfection_map.equiv_apply PerfectionMap.equiv_apply

theorem comp_equiv {π : P →+* R} (m : PerfectionMap p π) (x : P) :
    Perfection.coeff R p 0 (m.Equiv x) = π x :=
  rfl
#align perfection_map.comp_equiv PerfectionMap.comp_equiv

theorem comp_equiv' {π : P →+* R} (m : PerfectionMap p π) :
    (Perfection.coeff R p 0).comp ↑m.Equiv = π :=
  RingHom.ext fun x => rfl
#align perfection_map.comp_equiv' PerfectionMap.comp_equiv'

theorem comp_symm_equiv {π : P →+* R} (m : PerfectionMap p π) (f : Ring.Perfection R p) :
    π (m.Equiv.symm f) = Perfection.coeff R p 0 f :=
  (m.comp_equiv _).symm.trans <| congr_arg _ <| m.Equiv.apply_symm_apply f
#align perfection_map.comp_symm_equiv PerfectionMap.comp_symm_equiv

theorem comp_symm_equiv' {π : P →+* R} (m : PerfectionMap p π) :
    π.comp ↑m.Equiv.symm = Perfection.coeff R p 0 :=
  RingHom.ext m.comp_symm_equiv
#align perfection_map.comp_symm_equiv' PerfectionMap.comp_symm_equiv'

variable (p R P)

/-- Given rings `R` and `S` of characteristic `p`, with `R` being perfect,
any homomorphism `R →+* S` can be lifted to a homomorphism `R →+* P`,
where `P` is any perfection of `S`. -/
@[simps]
noncomputable def lift [PerfectRing R p] (S : Type u₂) [CommSemiring S] [CharP S p] (P : Type u₃)
    [CommSemiring P] [CharP P p] [PerfectRing P p] (π : P →+* S) (m : PerfectionMap p π) :
    (R →+* S) ≃ (R →+* P)
    where
  toFun f := RingHom.comp ↑m.Equiv.symm <| Perfection.lift p R S f
  invFun f := π.comp f
  left_inv f := by
    simp_rw [← RingHom.comp_assoc, comp_symm_equiv']
    exact (Perfection.lift p R S).symm_apply_apply f
  right_inv f :=
    RingHom.ext fun x =>
      m.Equiv.Injective <|
        (m.Equiv.apply_symm_apply _).trans <|
          show Perfection.lift p R S (π.comp f) x = RingHom.comp (↑m.Equiv) f x from
            RingHom.ext_iff.1 ((Perfection.lift p R S).apply_eq_iff_eq_symm_apply.2 rfl) _
#align perfection_map.lift PerfectionMap.lift

variable {R p}

theorem hom_ext [PerfectRing R p] {S : Type u₂} [CommSemiring S] [CharP S p] {P : Type u₃}
    [CommSemiring P] [CharP P p] [PerfectRing P p] (π : P →+* S) (m : PerfectionMap p π)
    {f g : R →+* P} (hfg : ∀ x, π (f x) = π (g x)) : f = g :=
  (lift p R S P π m).symm.Injective <| RingHom.ext hfg
#align perfection_map.hom_ext PerfectionMap.hom_ext

variable {R P} (p) {S : Type u₂} [CommSemiring S] [CharP S p]

variable {Q : Type u₄} [CommSemiring Q] [CharP Q p] [PerfectRing Q p]

/-- A ring homomorphism `R →+* S` induces `P →+* Q`, a map of the respective perfections. -/
@[nolint unused_arguments]
noncomputable def map {π : P →+* R} (m : PerfectionMap p π) {σ : Q →+* S} (n : PerfectionMap p σ)
    (φ : R →+* S) : P →+* Q :=
  lift p P S Q σ n <| φ.comp π
#align perfection_map.map PerfectionMap.map

theorem comp_map {π : P →+* R} (m : PerfectionMap p π) {σ : Q →+* S} (n : PerfectionMap p σ)
    (φ : R →+* S) : σ.comp (map p m n φ) = φ.comp π :=
  (lift p P S Q σ n).symm_apply_apply _
#align perfection_map.comp_map PerfectionMap.comp_map

theorem map_map {π : P →+* R} (m : PerfectionMap p π) {σ : Q →+* S} (n : PerfectionMap p σ)
    (φ : R →+* S) (x : P) : σ (map p m n φ x) = φ (π x) :=
  RingHom.ext_iff.1 (comp_map p m n φ) x
#align perfection_map.map_map PerfectionMap.map_map

-- Why is this slow?
theorem map_eq_map (φ : R →+* S) :
    @map p _ R _ _ _ _ _ _ S _ _ _ _ _ _ _ (of p R) _ (of p S) φ = Perfection.map p φ :=
  hom_ext _ (of p S) fun f => by rw [map_map, Perfection.coeff_map]
#align perfection_map.map_eq_map PerfectionMap.map_eq_map

end PerfectionMap

section Perfectoid

variable (K : Type u₁) [Field K] (v : Valuation K ℝ≥0)

variable (O : Type u₂) [CommRing O] [Algebra O K] (hv : v.Integers O)

variable (p : ℕ)

include hv

/-- `O/(p)` for `O`, ring of integers of `K`. -/
@[nolint unused_arguments has_nonempty_instance]
def ModP :=
  O ⧸ (Ideal.span {p} : Ideal O)
#align mod_p ModP

variable [hp : Fact p.Prime] [hvp : Fact (v p ≠ 1)]

namespace ModP

instance : CommRing (ModP K v O hv p) :=
  Ideal.Quotient.commRing _

include hp hvp

instance : CharP (ModP K v O hv p) p :=
  CharP.quotient O p <| mt hv.one_of_isUnit <| (map_natCast (algebraMap O K) p).symm ▸ hvp.1

instance : Nontrivial (ModP K v O hv p) :=
  CharP.nontrivial_of_char_ne_one hp.1.ne_one

section Classical

attribute [local instance] Classical.dec

omit hp hvp

/-- For a field `K` with valuation `v : K → ℝ≥0` and ring of integers `O`,
a function `O/(p) → ℝ≥0` that sends `0` to `0` and `x + (p)` to `v(x)` as long as `x ∉ (p)`. -/
noncomputable def preVal (x : ModP K v O hv p) : ℝ≥0 :=
  if x = 0 then 0 else v (algebraMap O K x.out')
#align mod_p.pre_val ModP.preVal

variable {K v O hv p}

theorem preVal_mk {x : O} (hx : (Ideal.Quotient.mk _ x : ModP K v O hv p) ≠ 0) :
    preVal K v O hv p (Ideal.Quotient.mk _ x) = v (algebraMap O K x) :=
  by
  obtain ⟨r, hr⟩ :=
    Ideal.mem_span_singleton'.1
      (Ideal.Quotient.eq.1 <|
        Quotient.sound' <| @Quotient.mk_out' O (Ideal.span {p} : Ideal O).quotientRel x)
  refine' (if_neg hx).trans (v.map_eq_of_sub_lt <| lt_of_not_le _)
  erw [← RingHom.map_sub, ← hr, hv.le_iff_dvd]
  exact fun hprx =>
    hx (Ideal.Quotient.eq_zero_iff_mem.2 <| Ideal.mem_span_singleton.2 <| dvd_of_mul_left_dvd hprx)
#align mod_p.pre_val_mk ModP.preVal_mk

theorem preVal_zero : preVal K v O hv p 0 = 0 :=
  if_pos rfl
#align mod_p.pre_val_zero ModP.preVal_zero

theorem preVal_mul {x y : ModP K v O hv p} (hxy0 : x * y ≠ 0) :
    preVal K v O hv p (x * y) = preVal K v O hv p x * preVal K v O hv p y :=
  by
  have hx0 : x ≠ 0 :=
    mt
      (by
        rintro rfl
        rw [MulZeroClass.zero_mul])
      hxy0
  have hy0 : y ≠ 0 :=
    mt
      (by
        rintro rfl
        rw [MulZeroClass.mul_zero])
      hxy0
  obtain ⟨r, rfl⟩ := Ideal.Quotient.mk_surjective x
  obtain ⟨s, rfl⟩ := Ideal.Quotient.mk_surjective y
  rw [← RingHom.map_mul] at hxy0⊢
  rw [pre_val_mk hx0, pre_val_mk hy0, pre_val_mk hxy0, RingHom.map_mul, v.map_mul]
#align mod_p.pre_val_mul ModP.preVal_mul

theorem preVal_add (x y : ModP K v O hv p) :
    preVal K v O hv p (x + y) ≤ max (preVal K v O hv p x) (preVal K v O hv p y) :=
  by
  by_cases hx0 : x = 0;
  · rw [hx0, zero_add]
    exact le_max_right _ _
  by_cases hy0 : y = 0;
  · rw [hy0, add_zero]
    exact le_max_left _ _
  by_cases hxy0 : x + y = 0;
  · rw [hxy0, pre_val_zero]
    exact zero_le _
  obtain ⟨r, rfl⟩ := Ideal.Quotient.mk_surjective x
  obtain ⟨s, rfl⟩ := Ideal.Quotient.mk_surjective y
  rw [← RingHom.map_add] at hxy0⊢
  rw [pre_val_mk hx0, pre_val_mk hy0, pre_val_mk hxy0, RingHom.map_add]; exact v.map_add _ _
#align mod_p.pre_val_add ModP.preVal_add

theorem v_p_lt_preVal {x : ModP K v O hv p} : v p < preVal K v O hv p x ↔ x ≠ 0 :=
  by
  refine'
    ⟨fun h hx => by
      rw [hx, pre_val_zero] at h
      exact not_lt_zero' h, fun h => lt_of_not_le fun hp => h _⟩
  obtain ⟨r, rfl⟩ := Ideal.Quotient.mk_surjective x
  rw [pre_val_mk h, ← map_natCast (algebraMap O K) p, hv.le_iff_dvd] at hp
  rw [Ideal.Quotient.eq_zero_iff_mem, Ideal.mem_span_singleton]; exact hp
#align mod_p.v_p_lt_pre_val ModP.v_p_lt_preVal

theorem preVal_eq_zero {x : ModP K v O hv p} : preVal K v O hv p x = 0 ↔ x = 0 :=
  ⟨fun hvx =>
    by_contradiction fun hx0 : x ≠ 0 =>
      by
      rw [← v_p_lt_pre_val, hvx] at hx0
      exact not_lt_zero' hx0,
    fun hx => hx.symm ▸ preVal_zero⟩
#align mod_p.pre_val_eq_zero ModP.preVal_eq_zero

variable (hv hvp)

theorem v_p_lt_val {x : O} :
    v p < v (algebraMap O K x) ↔ (Ideal.Quotient.mk _ x : ModP K v O hv p) ≠ 0 := by
  rw [lt_iff_not_le, not_iff_not, ← map_natCast (algebraMap O K) p, hv.le_iff_dvd,
    Ideal.Quotient.eq_zero_iff_mem, Ideal.mem_span_singleton]
#align mod_p.v_p_lt_val ModP.v_p_lt_val

open NNReal

variable {hv} (hvp)

include hp

theorem mul_ne_zero_of_pow_p_ne_zero {x y : ModP K v O hv p} (hx : x ^ p ≠ 0) (hy : y ^ p ≠ 0) :
    x * y ≠ 0 := by
  obtain ⟨r, rfl⟩ := Ideal.Quotient.mk_surjective x
  obtain ⟨s, rfl⟩ := Ideal.Quotient.mk_surjective y
  have h1p : (0 : ℝ) < 1 / p := one_div_pos.2 (Nat.cast_pos.2 hp.1.Pos)
  rw [← RingHom.map_mul]; rw [← RingHom.map_pow] at hx hy
  rw [← v_p_lt_val hv] at hx hy⊢
  rw [RingHom.map_pow, v.map_pow, ← rpow_lt_rpow_iff h1p, ← rpow_nat_cast, ← rpow_mul,
    mul_one_div_cancel (Nat.cast_ne_zero.2 hp.1.NeZero : (p : ℝ) ≠ 0), rpow_one] at hx hy
  rw [RingHom.map_mul, v.map_mul]; refine' lt_of_le_of_lt _ (mul_lt_mul₀ hx hy)
  by_cases hvp : v p = 0;
  · rw [hvp]
    exact zero_le _; replace hvp := zero_lt_iff.2 hvp
  conv_lhs => rw [← rpow_one (v p)]; rw [← rpow_add (ne_of_gt hvp)]
  refine' rpow_le_rpow_of_exponent_ge hvp (map_natCast (algebraMap O K) p ▸ hv.2 _) _
  rw [← add_div, div_le_one (Nat.cast_pos.2 hp.1.Pos : 0 < (p : ℝ))]; exact_mod_cast hp.1.two_le
#align mod_p.mul_ne_zero_of_pow_p_ne_zero ModP.mul_ne_zero_of_pow_p_ne_zero

end Classical

end ModP

/-- Perfection of `O/(p)` where `O` is the ring of integers of `K`. -/
@[nolint has_nonempty_instance]
def PreTilt :=
  Ring.Perfection (ModP K v O hv p) p
#align pre_tilt PreTilt

include hp hvp

namespace PreTilt

instance : CommRing (PreTilt K v O hv p) :=
  Perfection.commRing p _

instance : CharP (PreTilt K v O hv p) p :=
  Perfection.charP (ModP K v O hv p) p

section Classical

open Classical

open Perfection

/-- The valuation `Perfection(O/(p)) → ℝ≥0` as a function.
Given `f ∈ Perfection(O/(p))`, if `f = 0` then output `0`;
otherwise output `pre_val(f(n))^(p^n)` for any `n` such that `f(n) ≠ 0`. -/
noncomputable def valAux (f : PreTilt K v O hv p) : ℝ≥0 :=
  if h : ∃ n, coeff _ _ n f ≠ 0 then
    ModP.preVal K v O hv p (coeff _ _ (Nat.find h) f) ^ p ^ Nat.find h
  else 0
#align pre_tilt.val_aux PreTilt.valAux

variable {K v O hv p}

theorem coeff_nat_find_add_ne_zero {f : PreTilt K v O hv p} {h : ∃ n, coeff _ _ n f ≠ 0} (k : ℕ) :
    coeff _ _ (Nat.find h + k) f ≠ 0 :=
  coeff_add_ne_zero (Nat.find_spec h) k
#align pre_tilt.coeff_nat_find_add_ne_zero PreTilt.coeff_nat_find_add_ne_zero

theorem valAux_eq {f : PreTilt K v O hv p} {n : ℕ} (hfn : coeff _ _ n f ≠ 0) :
    valAux K v O hv p f = ModP.preVal K v O hv p (coeff _ _ n f) ^ p ^ n :=
  by
  have h : ∃ n, coeff _ _ n f ≠ 0 := ⟨n, hfn⟩
  rw [val_aux, dif_pos h]
  obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_le (Nat.find_min' h hfn)
  induction' k with k ih
  · rfl
  obtain ⟨x, hx⟩ := Ideal.Quotient.mk_surjective (coeff _ _ (Nat.find h + k + 1) f)
  have h1 : (Ideal.Quotient.mk _ x : ModP K v O hv p) ≠ 0 := hx.symm ▸ hfn
  have h2 : (Ideal.Quotient.mk _ (x ^ p) : ModP K v O hv p) ≠ 0 :=
    by
    erw [RingHom.map_pow, hx, ← RingHom.map_pow, coeff_pow_p]
    exact coeff_nat_find_add_ne_zero k
  erw [ih (coeff_nat_find_add_ne_zero k), ← hx, ← coeff_pow_p, RingHom.map_pow, ← hx, ←
    RingHom.map_pow, ModP.preVal_mk h1, ModP.preVal_mk h2, RingHom.map_pow, v.map_pow, ← pow_mul,
    pow_succ]
  rfl
#align pre_tilt.val_aux_eq PreTilt.valAux_eq

theorem valAux_zero : valAux K v O hv p 0 = 0 :=
  dif_neg fun ⟨n, hn⟩ => hn rfl
#align pre_tilt.val_aux_zero PreTilt.valAux_zero

theorem valAux_one : valAux K v O hv p 1 = 1 :=
  (valAux_eq <| show coeff (ModP K v O hv p) p 0 1 ≠ 0 from one_ne_zero).trans <|
    by
    rw [pow_zero, pow_one, RingHom.map_one, ← (Ideal.Quotient.mk _).map_one, ModP.preVal_mk,
      RingHom.map_one, v.map_one]
    change (1 : ModP K v O hv p) ≠ 0
    exact one_ne_zero
#align pre_tilt.val_aux_one PreTilt.valAux_one

theorem valAux_mul (f g : PreTilt K v O hv p) :
    valAux K v O hv p (f * g) = valAux K v O hv p f * valAux K v O hv p g :=
  by
  by_cases hf : f = 0
  · rw [hf, MulZeroClass.zero_mul, val_aux_zero, MulZeroClass.zero_mul]
  by_cases hg : g = 0
  · rw [hg, MulZeroClass.mul_zero, val_aux_zero, MulZeroClass.mul_zero]
  obtain ⟨m, hm⟩ : ∃ n, coeff _ _ n f ≠ 0 := not_forall.1 fun h => hf <| Perfection.ext h
  obtain ⟨n, hn⟩ : ∃ n, coeff _ _ n g ≠ 0 := not_forall.1 fun h => hg <| Perfection.ext h
  replace hm := coeff_ne_zero_of_le hm (le_max_left m n)
  replace hn := coeff_ne_zero_of_le hn (le_max_right m n)
  have hfg : coeff _ _ (max m n + 1) (f * g) ≠ 0 :=
    by
    rw [RingHom.map_mul]
    refine' ModP.mul_ne_zero_of_pow_p_ne_zero _ _
    · rw [← RingHom.map_pow, coeff_pow_p f]
      assumption
    · rw [← RingHom.map_pow, coeff_pow_p g]
      assumption
  rw [val_aux_eq (coeff_add_ne_zero hm 1), val_aux_eq (coeff_add_ne_zero hn 1), val_aux_eq hfg]
  rw [RingHom.map_mul] at hfg⊢
  rw [ModP.preVal_mul hfg, mul_pow]
#align pre_tilt.val_aux_mul PreTilt.valAux_mul

theorem valAux_add (f g : PreTilt K v O hv p) :
    valAux K v O hv p (f + g) ≤ max (valAux K v O hv p f) (valAux K v O hv p g) :=
  by
  by_cases hf : f = 0;
  · rw [hf, zero_add, val_aux_zero, max_eq_right]
    exact zero_le _
  by_cases hg : g = 0;
  · rw [hg, add_zero, val_aux_zero, max_eq_left]
    exact zero_le _
  by_cases hfg : f + g = 0;
  · rw [hfg, val_aux_zero]
    exact zero_le _
  replace hf : ∃ n, coeff _ _ n f ≠ 0 := not_forall.1 fun h => hf <| Perfection.ext h
  replace hg : ∃ n, coeff _ _ n g ≠ 0 := not_forall.1 fun h => hg <| Perfection.ext h
  replace hfg : ∃ n, coeff _ _ n (f + g) ≠ 0 := not_forall.1 fun h => hfg <| Perfection.ext h
  obtain ⟨m, hm⟩ := hf; obtain ⟨n, hn⟩ := hg; obtain ⟨k, hk⟩ := hfg
  replace hm := coeff_ne_zero_of_le hm (le_trans (le_max_left m n) (le_max_left _ k))
  replace hn := coeff_ne_zero_of_le hn (le_trans (le_max_right m n) (le_max_left _ k))
  replace hk := coeff_ne_zero_of_le hk (le_max_right (max m n) k)
  rw [val_aux_eq hm, val_aux_eq hn, val_aux_eq hk, RingHom.map_add]
  cases'
    le_max_iff.1
      (ModP.preVal_add (coeff _ _ (max (max m n) k) f) (coeff _ _ (max (max m n) k) g)) with
    h h
  · exact le_max_of_le_left (pow_le_pow_of_le_left' h _)
  · exact le_max_of_le_right (pow_le_pow_of_le_left' h _)
#align pre_tilt.val_aux_add PreTilt.valAux_add

variable (K v O hv p)

/-- The valuation `Perfection(O/(p)) → ℝ≥0`.
Given `f ∈ Perfection(O/(p))`, if `f = 0` then output `0`;
otherwise output `pre_val(f(n))^(p^n)` for any `n` such that `f(n) ≠ 0`. -/
noncomputable def val : Valuation (PreTilt K v O hv p) ℝ≥0
    where
  toFun := valAux K v O hv p
  map_one' := valAux_one
  map_mul' := valAux_mul
  map_zero' := valAux_zero
  map_add_le_max' := valAux_add
#align pre_tilt.val PreTilt.val

variable {K v O hv p}

theorem map_eq_zero {f : PreTilt K v O hv p} : val K v O hv p f = 0 ↔ f = 0 :=
  by
  by_cases hf0 : f = 0;
  · rw [hf0]
    exact iff_of_true (Valuation.map_zero _) rfl
  obtain ⟨n, hn⟩ : ∃ n, coeff _ _ n f ≠ 0 := not_forall.1 fun h => hf0 <| Perfection.ext h
  show val_aux K v O hv p f = 0 ↔ f = 0; refine' iff_of_false (fun hvf => hn _) hf0
  rw [val_aux_eq hn] at hvf; replace hvf := pow_eq_zero hvf; rwa [ModP.preVal_eq_zero] at hvf
#align pre_tilt.map_eq_zero PreTilt.map_eq_zero

end Classical

instance : IsDomain (PreTilt K v O hv p) :=
  by
  haveI : Nontrivial (PreTilt K v O hv p) := ⟨(CharP.nontrivial_of_char_ne_one hp.1.ne_one).1⟩
  haveI : NoZeroDivisors (PreTilt K v O hv p) :=
    ⟨fun f g hfg => by
      simp_rw [← map_eq_zero] at hfg⊢
      contrapose! hfg
      rw [Valuation.map_mul]
      exact mul_ne_zero hfg.1 hfg.2⟩
  exact NoZeroDivisors.to_isDomain _

end PreTilt

/-- The tilt of a field, as defined in Perfectoid Spaces by Peter Scholze, as in
[scholze2011perfectoid]. Given a field `K` with valuation `K → ℝ≥0` and ring of integers `O`,
this is implemented as the fraction field of the perfection of `O/(p)`. -/
@[nolint has_nonempty_instance]
def Tilt :=
  FractionRing (PreTilt K v O hv p)
#align tilt Tilt

namespace Tilt

noncomputable instance : Field (Tilt K v O hv p) :=
  FractionRing.field

end Tilt

end Perfectoid


/-
Copyright (c) 2022 Yaël Dillies, Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Bhavik Mehta
-/
import algebra.big_operators.basic
import order.atoms
import order.locally_finite
import order.sup_indep

/-!
# Finite partitions

In this file, we define finite partitions. A finpartition of `a : α` is a finite set of pairwise
disjoint parts `parts : finset α` which does not contain `⊥` and whose supremum is `a`.

Finpartitions of a finset are at the heart of Szemerédi's regularity lemma. They are also studied
purely order theoretically in Sperner theory.

## Constructions

We provide many ways to build finpartitions:
* `finpartition.of_erase`: Builds a finpartition by erasing `⊥` for you.
* `finpartition.of_subset`: Builds a finpartition from a subset of the parts of a previous
  finpartition.
* `finpartition.empty`: The empty finpartition of `⊥`.
* `finpartition.indiscrete`: The indiscrete, aka trivial, aka pure, finpartition made of a single
  part.
* `finpartition.discrete`: The discrete finpartition of `s : finset α` made of singletons.
* `finpartition.bind`: Puts together the finpartitions of the parts of a finpartition into a new
  finpartition.
* `finpartition.atomise`: Makes a finpartition of `s : finset α` by breaking `s` along all finsets
  in `F : finset (finset α)`. Two elements of `s` belong to the same part iff they belong to the
  same elements of `F`.

`finpartition.indiscrete` and `finpartition.bind` together form the monadic structure of
`finpartition`.

## Implementation notes

Forbidding `⊥` as a part follows mathematical tradition and is a pragmatic choice concerning
operations on `finpartition`. Not caring about `⊥` being a part or not breaks extensionality (it's
not because the parts of `P` and the parts of `Q` have the same elements that `P = Q`). Enforcing
`⊥` to be a part makes `finpartition.bind` uglier and doesn't rid us of the need of
`finpartition.of_erase`.

## TODO

Link `finpartition` and `setoid.is_partition`.

The order is the wrong way around to make `finpartition a` a graded order. Is it bad to depart from
the literature and turn the order around?
-/

open finset function
open_locale big_operators

variables {α : Type*}

/-- A finite partition of `a : α` is a pairwise disjoint finite set of elements whose supremum is
`a`. We forbid `⊥` as a part. -/
@[ext, derive decidable_eq] structure finpartition [lattice α] [order_bot α] (a : α) :=
(parts : finset α)
(sup_indep : parts.sup_indep id)
(sup_parts : parts.sup id = a)
(not_bot_mem : ⊥ ∉ parts)

attribute [protected] finpartition.sup_indep

namespace finpartition
section lattice
variables [lattice α] [order_bot α]

/-- A `finpartition` constructor which does not insist on `⊥` not being a part. -/
@[simps] def of_erase [decidable_eq α] {a : α} (parts : finset α)
  (sup_indep : parts.sup_indep id) (sup_parts : parts.sup id = a) :
  finpartition a :=
{ parts := parts.erase ⊥,
  sup_indep := sup_indep.subset (erase_subset _ _),
  sup_parts := (sup_erase_bot _).trans sup_parts,
  not_bot_mem := not_mem_erase _ _ }

/-- A `finpartition` constructor from a bigger existing finpartition. -/
@[simps] def of_subset {a b : α} (P : finpartition a) {parts : finset α}
  (subset : parts ⊆ P.parts) (sup_parts : parts.sup id = b) :
  finpartition b :=
{ parts := parts,
  sup_indep := P.sup_indep.subset subset,
  sup_parts := sup_parts,
  not_bot_mem := λ h, P.not_bot_mem (subset h) }

/-- Changes the type of a finpartition to an equal one. -/
@[simps] def copy {a b : α} (P : finpartition a) (h : a = b) : finpartition b :=
{ parts := P.parts,
  sup_indep := P.sup_indep,
  sup_parts := h ▸ P.sup_parts,
  not_bot_mem := P.not_bot_mem }

variables (α)

/-- The empty finpartition. -/
@[simps] protected def empty : finpartition (⊥ : α) :=
{ parts := ∅,
  sup_indep := sup_indep_empty _,
  sup_parts := finset.sup_empty,
  not_bot_mem := not_mem_empty ⊥ }

instance : inhabited (finpartition (⊥ : α)) := ⟨finpartition.empty α⟩

@[simp] lemma default_eq_empty : (default : finpartition (⊥ : α)) = finpartition.empty α := rfl

variables {α} {a : α}

/-- The finpartition in one part, aka indiscrete finpartition. -/
@[simps] def indiscrete (ha : a ≠ ⊥) : finpartition a :=
{ parts := {a},
  sup_indep := sup_indep_singleton _ _,
  sup_parts := finset.sup_singleton,
  not_bot_mem := λ h, ha (mem_singleton.1 h).symm }

variables (P : finpartition a)

protected lemma le {b : α} (hb : b ∈ P.parts) : b ≤ a := (le_sup hb).trans P.sup_parts.le

lemma ne_bot {b : α} (hb : b ∈ P.parts) : b ≠ ⊥ := λ h, P.not_bot_mem $ h.subst hb

protected lemma disjoint : (P.parts : set α).pairwise_disjoint id := P.sup_indep.pairwise_disjoint

variables {P}

lemma parts_eq_empty_iff : P.parts = ∅ ↔ a = ⊥ :=
begin
  simp_rw ←P.sup_parts,
  refine ⟨λ h, _, λ h, eq_empty_iff_forall_not_mem.2 (λ b hb, P.not_bot_mem _)⟩,
  { rw h,
    exact finset.sup_empty },
  { rwa ←le_bot_iff.1 ((le_sup hb).trans h.le) }
end

lemma parts_nonempty_iff : P.parts.nonempty ↔ a ≠ ⊥ :=
by rw [nonempty_iff_ne_empty, not_iff_not, parts_eq_empty_iff]

lemma parts_nonempty (P : finpartition a) (ha : a ≠ ⊥) : P.parts.nonempty := parts_nonempty_iff.2 ha

instance : unique (finpartition (⊥ : α)) :=
{ uniq := λ P,
    by { ext a, exact iff_of_false (λ h, P.ne_bot h $ le_bot_iff.1 $ P.le h) (not_mem_empty a) },
  ..finpartition.inhabited α }

/-- There's a unique partition of an atom. -/
@[reducible] -- See note [reducible non instances]
def _root_.is_atom.unique_finpartition (ha : is_atom a) : unique (finpartition a) :=
{ default := indiscrete ha.1,
  uniq := λ P, begin
    have h : ∀ b ∈ P.parts, b = a,
    { exact λ b hb, (eq_bot_or_eq_of_le_atom ha $ P.le hb).resolve_left (P.ne_bot hb) },
    ext b,
    refine iff.trans ⟨h b, _⟩ mem_singleton.symm,
    rintro rfl,
    obtain ⟨c, hc⟩ := P.parts_nonempty ha.1,
    simp_rw ←h c hc,
    exact hc,
  end }

instance [fintype α] [decidable_eq α] (a : α) :
  fintype (finpartition a) :=
@fintype.of_surjective {p : finset α // p.sup_indep id ∧ p.sup id = a ∧ ⊥ ∉ p} (finpartition a) _
  (subtype.fintype _) (λ i, ⟨i.1, i.2.1, i.2.2.1, i.2.2.2⟩) (λ ⟨_, y, z, w⟩, ⟨⟨_, y, z, w⟩, rfl⟩)

/-! ### Refinement order -/

section order

/-- We say that `P ≤ Q` if `P` refines `Q`: each part of `P` is less than some part of `Q`. -/
instance : has_le (finpartition a) := ⟨λ P Q, ∀ ⦃b⦄, b ∈ P.parts → ∃ c ∈ Q.parts, b ≤ c⟩

instance : partial_order (finpartition a) :=
{ le_refl := λ P b hb, ⟨b, hb, le_rfl⟩,
  le_trans := λ P Q R hPQ hQR b hb, begin
    obtain ⟨c, hc, hbc⟩ := hPQ hb,
    obtain ⟨d, hd, hcd⟩ := hQR hc,
    exact ⟨d, hd, hbc.trans hcd⟩,
  end,
  le_antisymm := λ P Q hPQ hQP, begin
    ext b,
    refine ⟨λ hb, _, λ hb, _⟩,
    { obtain ⟨c, hc, hbc⟩ := hPQ hb,
      obtain ⟨d, hd, hcd⟩ := hQP hc,
      rwa hbc.antisymm,
      rwa P.disjoint.eq_of_le hb hd (P.ne_bot hb) (hbc.trans hcd) },
    { obtain ⟨c, hc, hbc⟩ := hQP hb,
      obtain ⟨d, hd, hcd⟩ := hPQ hc,
      rwa hbc.antisymm,
      rwa Q.disjoint.eq_of_le hb hd (Q.ne_bot hb) (hbc.trans hcd) }
  end,
  ..finpartition.has_le }

instance [decidable (a = ⊥)] : order_top (finpartition a) :=
{ top := if ha : a = ⊥ then (finpartition.empty α).copy ha.symm else indiscrete ha,
  le_top := λ P,
  begin
    split_ifs,
    { intros x hx,
      simpa [h, P.ne_bot hx] using P.le hx },
    { exact λ b hb, ⟨a, mem_singleton_self _, P.le hb⟩ }
  end }

lemma parts_top_subset (a : α) [decidable (a = ⊥)] : (⊤ : finpartition a).parts ⊆ {a} :=
begin
  intros b hb,
  change b ∈ finpartition.parts (dite _ _ _) at hb,
  split_ifs at hb,
  { simp only [copy_parts, empty_parts, not_mem_empty] at hb,
    exact hb.elim },
  { exact hb }
end

lemma parts_top_subsingleton (a : α) [decidable (a = ⊥)] :
  ((⊤ : finpartition a).parts : set α).subsingleton :=
set.subsingleton_of_subset_singleton $ λ b hb, mem_singleton.1 $ parts_top_subset _ hb

end order
end lattice

section distrib_lattice
variables [distrib_lattice α] [order_bot α]

section inf
variables [decidable_eq α] {a b c : α}

instance : has_inf (finpartition a) :=
⟨λ P Q, of_erase ((P.parts.product Q.parts).image $ λ bc, bc.1 ⊓ bc.2)
  begin
    rw sup_indep_iff_disjoint_erase,
    simp only [mem_image, and_imp, exists_prop, forall_exists_index, id.def, prod.exists,
      mem_product, finset.disjoint_sup_right, mem_erase, ne.def],
    rintro _ x₁ y₁ hx₁ hy₁ rfl _ h x₂ y₂ hx₂ hy₂ rfl,
    rcases eq_or_ne x₁ x₂ with rfl | xdiff,
    { refine disjoint.mono inf_le_right inf_le_right (Q.disjoint hy₁ hy₂ _),
      intro t,
      simpa [t] using h },
    exact disjoint.mono inf_le_left inf_le_left (P.disjoint hx₁ hx₂ xdiff),
  end
  begin
    rw [sup_image, comp.left_id, sup_product_left],
    transitivity P.parts.sup id ⊓ Q.parts.sup id,
    { simp_rw [finset.sup_inf_distrib_right, finset.sup_inf_distrib_left],
      refl },
    { rw [P.sup_parts, Q.sup_parts, inf_idem] }
  end⟩

@[simp] lemma parts_inf (P Q : finpartition a) :
  (P ⊓ Q).parts = ((P.parts.product Q.parts).image $ λ bc : α × α, bc.1 ⊓ bc.2).erase ⊥ := rfl

instance : semilattice_inf (finpartition a) :=
{ inf_le_left := λ P Q b hb, begin
    obtain ⟨c, hc, rfl⟩ := mem_image.1 (mem_of_mem_erase hb),
    rw mem_product at hc,
    exact ⟨c.1, hc.1, inf_le_left⟩,
  end,
  inf_le_right := λ P Q b hb, begin
    obtain ⟨c, hc, rfl⟩ := mem_image.1 (mem_of_mem_erase hb),
    rw mem_product at hc,
    exact ⟨c.2, hc.2, inf_le_right⟩,
  end,
  le_inf := λ P Q R hPQ hPR b hb, begin
    obtain ⟨c, hc, hbc⟩ := hPQ hb,
    obtain ⟨d, hd, hbd⟩ := hPR hb,
    have h := _root_.le_inf hbc hbd,
    refine ⟨c ⊓ d, mem_erase_of_ne_of_mem (ne_bot_of_le_ne_bot (P.ne_bot hb) h)
      (mem_image.2 ⟨(c, d), mem_product.2 ⟨hc, hd⟩, rfl⟩), h⟩,
  end,
  ..finpartition.partial_order, ..finpartition.has_inf }

end inf

lemma exists_le_of_le {a b : α} {P Q : finpartition a} (h : P ≤ Q) (hb : b ∈ Q.parts) :
  ∃ c ∈ P.parts, c ≤ b :=
begin
  by_contra' H,
  refine Q.ne_bot hb (disjoint_self.1 $ disjoint.mono_right (Q.le hb) _),
  rw [←P.sup_parts, finset.disjoint_sup_right],
  rintro c hc,
  obtain ⟨d, hd, hcd⟩ := h hc,
  refine (Q.disjoint hb hd _).mono_right hcd,
  rintro rfl,
  exact H _ hc hcd,
end

lemma card_mono {a : α} {P Q : finpartition a} (h : P ≤ Q) : Q.parts.card ≤ P.parts.card :=
begin
  classical,
  have : ∀ b ∈ Q.parts, ∃ c ∈ P.parts, c ≤ b := λ b, exists_le_of_le h,
  choose f hP hf using this,
  rw ←card_attach,
  refine card_le_card_of_inj_on (λ b, f _ b.2) (λ b _, hP _ b.2) (λ b hb c hc h, _),
  exact subtype.coe_injective (Q.disjoint.elim b.2 c.2 $ λ H, P.ne_bot (hP _ b.2) $
    disjoint_self.1 $ H.mono (hf _ b.2) $ h.le.trans $ hf _ c.2),
end

variables [decidable_eq α] {a b c : α}

section bind
variables {P : finpartition a} {Q : Π i ∈ P.parts, finpartition i}

/-- Given a finpartition `P` of `a` and finpartitions of each part of `P`, this yields the
finpartition of `a` obtained by juxtaposing all the subpartitions. -/
@[simps] def bind (P : finpartition a) (Q : Π i ∈ P.parts, finpartition i) : finpartition a :=
{ parts := P.parts.attach.bUnion (λ i, (Q i.1 i.2).parts),
  sup_indep := begin
    rw sup_indep_iff_pairwise_disjoint,
    rintro a ha b hb h,
    rw [finset.mem_coe, finset.mem_bUnion] at ha hb,
    obtain ⟨⟨A, hA⟩, -, ha⟩ := ha,
    obtain ⟨⟨B, hB⟩, -, hb⟩ := hb,
    obtain rfl | hAB := eq_or_ne A B,
    { exact (Q A hA).disjoint ha hb h },
    { exact (P.disjoint hA hB hAB).mono ((Q A hA).le ha) ((Q B hB).le hb) }
  end,
  sup_parts := begin
    simp_rw [sup_bUnion, ←P.sup_parts],
    rw [eq_comm, ←finset.sup_attach],
    exact sup_congr rfl (λ b hb, (Q b.1 b.2).sup_parts.symm),
  end,
  not_bot_mem := λ h, begin
    rw finset.mem_bUnion at h,
    obtain ⟨⟨A, hA⟩, -, h⟩ := h,
    exact (Q A hA).not_bot_mem h,
  end }

lemma card_bind (Q : Π i ∈ P.parts, finpartition i) :
  (P.bind Q).parts.card = ∑ A in P.parts.attach, (Q _ A.2).parts.card :=
begin
  apply card_bUnion,
  rintro ⟨b, hb⟩ - ⟨c, hc⟩ - hbc d,
  rw [inf_eq_inter, mem_inter],
  rintro ⟨hdb, hdc⟩,
  rw [ne.def, subtype.mk_eq_mk] at hbc,
  exact (Q b hb).ne_bot hdb (eq_bot_iff.2 $
    (le_inf ((Q b hb).le hdb) $ (Q c hc).le hdc).trans $ P.disjoint hb hc hbc),
end

end bind

/-- Adds `b` to a finpartition of `a` to make a finpartition of `a ⊔ b`. -/
@[simps] def extend (P : finpartition a) (hb : b ≠ ⊥) (hab : disjoint a b) (hc : a ⊔ b = c) :
  finpartition c :=
{ parts := insert b P.parts,
  sup_indep :=
  begin
    rw [sup_indep_iff_pairwise_disjoint, coe_insert],
    exact P.disjoint.insert (λ d hd hbd, hab.symm.mono_right $ P.le hd),
  end,
  sup_parts := by rwa [sup_insert, P.sup_parts, id, _root_.sup_comm],
  not_bot_mem := λ h, (mem_insert.1 h).elim hb.symm P.not_bot_mem }

lemma card_extend (P : finpartition a) (b c : α) {hb : b ≠ ⊥} {hab : disjoint a b}
  {hc : a ⊔ b = c} :
  (P.extend hb hab hc).parts.card = P.parts.card + 1 :=
card_insert_of_not_mem $ λ h, hb $ hab.symm.eq_bot_of_le $ P.le h

end distrib_lattice

section generalized_boolean_algebra
variables [generalized_boolean_algebra α] [decidable_eq α] {a : α} (P : finpartition a)

/-- Restricts a finpartition to avoid a given element. -/
@[simps] def avoid (b : α) : finpartition (a \ b) :=
of_erase
  (P.parts.image (\ b))
  (P.disjoint.image_finset_of_le $ λ a, sdiff_le).sup_indep
  (begin
    rw [sup_image, comp.left_id, finset.sup_sdiff_right],
    congr,
    exact P.sup_parts,
  end)

end generalized_boolean_algebra
end finpartition

/-! ### Finite partitions of finsets -/

namespace finpartition
variables [decidable_eq α] {s t : finset α} (P : finpartition s)

lemma nonempty_of_mem_parts {a : finset α} (ha : a ∈ P.parts) : a.nonempty :=
nonempty_iff_ne_empty.2 $ P.ne_bot ha

lemma exists_mem {a : α} (ha : a ∈ s) : ∃ t ∈ P.parts, a ∈ t :=
by { simp_rw ←P.sup_parts at ha, exact mem_sup.1 ha }

lemma bUnion_parts : P.parts.bUnion id = s := (sup_eq_bUnion _ _).symm.trans P.sup_parts

lemma sum_card_parts : ∑ i in P.parts, i.card = s.card :=
begin
  convert congr_arg finset.card P.bUnion_parts,
  rw card_bUnion P.sup_indep.pairwise_disjoint,
  refl,
end

/-- `⊥` is the partition in singletons, aka discrete partition. -/
instance (s : finset α) : has_bot (finpartition s) :=
⟨{ parts := s.map ⟨singleton, singleton_injective⟩,
  sup_indep := set.pairwise_disjoint.sup_indep begin
      rw finset.coe_map,
      exact finset.pairwise_disjoint_range_singleton.subset (set.image_subset_range _ _),
    end,
  sup_parts := by rw [sup_map, comp.left_id, embedding.coe_fn_mk, finset.sup_singleton'],
  not_bot_mem := by simp }⟩

@[simp] lemma parts_bot (s : finset α) :
  (⊥ : finpartition s).parts = s.map ⟨singleton, singleton_injective⟩ := rfl

lemma card_bot (s : finset α) : (⊥ : finpartition s).parts.card = s.card := finset.card_map _
lemma mem_bot_iff : t ∈ (⊥ : finpartition s).parts ↔ ∃ a ∈ s, {a} = t := mem_map

instance (s : finset α) : order_bot (finpartition s) :=
{ bot_le := λ P t ht, begin
    rw mem_bot_iff at ht,
    obtain ⟨a, ha, rfl⟩ := ht,
    obtain ⟨t, ht, hat⟩ := P.exists_mem ha,
    exact ⟨t, ht, singleton_subset_iff.2 hat⟩,
  end,
  ..finpartition.has_bot s }

lemma card_parts_le_card (P : finpartition s) : P.parts.card ≤ s.card :=
by { rw ←card_bot s, exact card_mono bot_le }

section atomise

/-- Cuts `s` along the finsets in `F`: Two elements of `s` will be in the same part if they are
in the same finsets of `F`. -/
def atomise (s : finset α) (F : finset (finset α)) : finpartition s :=
of_erase
  (F.powerset.image $ λ Q, s.filter (λ i, ∀ t ∈ F, t ∈ Q ↔ i ∈ t))
  (set.pairwise_disjoint.sup_indep $ λ x hx y hy h z hz, h begin
    rw [mem_coe, mem_image] at hx hy,
    obtain ⟨Q, hQ, rfl⟩ := hx,
    obtain ⟨R, hR, rfl⟩ := hy,
    suffices h : Q = R,
    { subst h },
    rw [id, id, inf_eq_inter, mem_inter, mem_filter, mem_filter] at hz,
    rw mem_powerset at hQ hR,
    ext i,
    refine ⟨λ hi, _, λ hi, _⟩,
    { rwa [hz.2.2 _ (hQ hi), ←hz.1.2 _ (hQ hi)] },
    { rwa [hz.1.2 _ (hR hi), ←hz.2.2 _ (hR hi)] }
  end)
  (begin
    refine (finset.sup_le $ λ t ht, _).antisymm (λ a ha, _),
    { rw mem_image at ht,
      obtain ⟨A, hA, rfl⟩ := ht,
      exact s.filter_subset _ },
    { rw [mem_sup],
      refine ⟨s.filter (λ i, ∀ t, t ∈ F → (t ∈ F.filter (λ u, a ∈ u) ↔ i ∈ t)),
        mem_image_of_mem _ (mem_powerset.2 $ filter_subset _ _), mem_filter.2 ⟨ha, λ t ht, _⟩⟩,
      rw mem_filter,
      exact and_iff_right ht }
  end)

variables {F : finset (finset α)}

lemma mem_atomise {t : finset α} :
  t ∈ (atomise s F).parts ↔ t.nonempty ∧ ∃ (Q ⊆ F), s.filter (λ i, ∀ u ∈ F, u ∈ Q ↔ i ∈ u) = t :=
by simp only [atomise, of_erase, bot_eq_empty, mem_erase, mem_image, nonempty_iff_ne_empty,
  mem_singleton, and_comm, mem_powerset, exists_prop]

lemma atomise_empty (hs : s.nonempty) : (atomise s ∅).parts = {s} :=
begin
  simp only [atomise, powerset_empty, image_singleton, not_mem_empty, forall_false_left,
    implies_true_iff, filter_true],
  exact erase_eq_of_not_mem (not_mem_singleton.2 hs.ne_empty.symm),
end

lemma card_atomise_le : (atomise s F).parts.card ≤ 2^F.card :=
(card_le_of_subset $ erase_subset _ _).trans $ finset.card_image_le.trans (card_powerset _).le

lemma bUnion_filter_atomise (t : finset α) (ht : t ∈ F) (hts : t ⊆ s) :
  ((atomise s F).parts.filter $ λ u, u ⊆ t).bUnion id = t :=
begin
  ext a,
  rw mem_bUnion,
  refine ⟨λ ⟨u, hu, ha⟩, (mem_filter.1 hu).2 ha, λ ha, _⟩,
  obtain ⟨u, hu, hau⟩ := (atomise s F).exists_mem (hts ha),
  refine ⟨u, mem_filter.2 ⟨hu, λ b hb, _⟩, hau⟩,
  obtain ⟨Q, hQ, rfl⟩ := (mem_atomise.1 hu).2,
  rw mem_filter at hau hb,
  rwa [←hb.2 _ ht, hau.2 _ ht]
end

end atomise
end finpartition


/-
Copyright (c) 2018 Kevin Buzzard and Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Buzzard, Patrick Massot.

This file is to a certain extent based on `quotient_module.lean` by Johannes Hölzl.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.group_theory.coset
import Mathlib.PostPort

universes u u_1 v

namespace Mathlib

namespace quotient_group

-- Define the `div_inv_monoid` before the `group` structure,

-- to make sure we have `inv` fully defined before we show `mul_left_inv`.

-- TODO: is there a non-invasive way of defining this in one declaration?

protected instance Mathlib.quotient_add_group.div_inv_monoid {G : Type u} [add_group G]
    (N : add_subgroup G) [nN : add_subgroup.normal N] :
    sub_neg_monoid (quotient_add_group.quotient N) :=
  sub_neg_monoid.mk (quotient.map₂' Add.add sorry) sorry ↑0 sorry sorry
    (fun (a : quotient_add_group.quotient N) => quotient.lift_on' a (fun (a : G) => ↑(-a)) sorry)
    fun (a b : quotient_add_group.quotient N) =>
      quotient.map₂' Add.add sorry a (quotient.lift_on' b (fun (a : G) => ↑(-a)) sorry)

protected instance Mathlib.quotient_add_group.add_group {G : Type u} [add_group G]
    (N : add_subgroup G) [nN : add_subgroup.normal N] : add_group (quotient_add_group.quotient N) :=
  add_group.mk sub_neg_monoid.add sorry sub_neg_monoid.zero sorry sorry sub_neg_monoid.neg
    sub_neg_monoid.sub sorry

/-- The group homomorphism from `G` to `G/N`. -/
def mk' {G : Type u} [group G] (N : subgroup G) [nN : subgroup.normal N] : G →* quotient N :=
  monoid_hom.mk' mk sorry

@[simp] theorem Mathlib.quotient_add_group.ker_mk {G : Type u} [add_group G] (N : add_subgroup G)
    [nN : add_subgroup.normal N] : add_monoid_hom.ker (quotient_add_group.mk' N) = N :=
  sorry

-- for commutative groups we don't need normality assumption

protected instance Mathlib.quotient_add_group.add_comm_group {G : Type u_1} [add_comm_group G]
    (N : add_subgroup G) : add_comm_group (quotient_add_group.quotient N) :=
  add_comm_group.mk add_group.add sorry add_group.zero sorry sorry add_group.neg add_group.sub sorry
    sorry

@[simp] theorem Mathlib.quotient_add_group.coe_zero {G : Type u} [add_group G] (N : add_subgroup G)
    [nN : add_subgroup.normal N] : ↑0 = 0 :=
  rfl

@[simp] theorem coe_mul {G : Type u} [group G] (N : subgroup G) [nN : subgroup.normal N] (a : G)
    (b : G) : ↑(a * b) = ↑a * ↑b :=
  rfl

@[simp] theorem Mathlib.quotient_add_group.coe_neg {G : Type u} [add_group G] (N : add_subgroup G)
    [nN : add_subgroup.normal N] (a : G) : ↑(-a) = -↑a :=
  rfl

@[simp] theorem coe_pow {G : Type u} [group G] (N : subgroup G) [nN : subgroup.normal N] (a : G)
    (n : ℕ) : ↑(a ^ n) = ↑a ^ n :=
  monoid_hom.map_pow (mk' N) a n

@[simp] theorem coe_gpow {G : Type u} [group G] (N : subgroup G) [nN : subgroup.normal N] (a : G)
    (n : ℤ) : ↑(a ^ n) = ↑a ^ n :=
  monoid_hom.map_gpow (mk' N) a n

/-- A group homomorphism `φ : G →* H` with `N ⊆ ker(φ)` descends (i.e. `lift`s) to a
group homomorphism `G/N →* H`. -/
def lift {G : Type u} [group G] (N : subgroup G) [nN : subgroup.normal N] {H : Type v} [group H]
    (φ : G →* H) (HN : ∀ (x : G), x ∈ N → coe_fn φ x = 1) : quotient N →* H :=
  monoid_hom.mk' (fun (q : quotient N) => quotient.lift_on' q ⇑φ sorry) sorry

@[simp] theorem Mathlib.quotient_add_group.lift_mk {G : Type u} [add_group G] (N : add_subgroup G)
    [nN : add_subgroup.normal N] {H : Type v} [add_group H] {φ : G →+ H}
    (HN : ∀ (x : G), x ∈ N → coe_fn φ x = 0) (g : G) :
    coe_fn (quotient_add_group.lift N φ HN) ↑g = coe_fn φ g :=
  rfl

@[simp] theorem lift_mk' {G : Type u} [group G] (N : subgroup G) [nN : subgroup.normal N]
    {H : Type v} [group H] {φ : G →* H} (HN : ∀ (x : G), x ∈ N → coe_fn φ x = 1) (g : G) :
    coe_fn (lift N φ HN) (mk g) = coe_fn φ g :=
  rfl

@[simp] theorem Mathlib.quotient_add_group.lift_quot_mk {G : Type u} [add_group G]
    (N : add_subgroup G) [nN : add_subgroup.normal N] {H : Type v} [add_group H] {φ : G →+ H}
    (HN : ∀ (x : G), x ∈ N → coe_fn φ x = 0) (g : G) :
    coe_fn (quotient_add_group.lift N φ HN) (Quot.mk setoid.r g) = coe_fn φ g :=
  rfl

/-- A group homomorphism `f : G →* H` induces a map `G/N →* H/M` if `N ⊆ f⁻¹(M)`. -/
def Mathlib.quotient_add_group.map {G : Type u} [add_group G] (N : add_subgroup G)
    [nN : add_subgroup.normal N] {H : Type v} [add_group H] (M : add_subgroup H)
    [add_subgroup.normal M] (f : G →+ H) (h : N ≤ add_subgroup.comap f M) :
    quotient_add_group.quotient N →+ quotient_add_group.quotient M :=
  quotient_add_group.lift N (add_monoid_hom.comp (quotient_add_group.mk' M) f) sorry

/-- The induced map from the quotient by the kernel to the codomain. -/
def ker_lift {G : Type u} [group G] {H : Type v} [group H] (φ : G →* H) :
    quotient (monoid_hom.ker φ) →* H :=
  lift (monoid_hom.ker φ) φ sorry

@[simp] theorem ker_lift_mk {G : Type u} [group G] {H : Type v} [group H] (φ : G →* H) (g : G) :
    coe_fn (ker_lift φ) ↑g = coe_fn φ g :=
  lift_mk (monoid_hom.ker φ) (ker_lift._proof_1 φ) g

@[simp] theorem Mathlib.quotient_add_group.ker_lift_mk' {G : Type u} [add_group G] {H : Type v}
    [add_group H] (φ : G →+ H) (g : G) :
    coe_fn (quotient_add_group.ker_lift φ) (quotient_add_group.mk g) = coe_fn φ g :=
  quotient_add_group.lift_mk' (add_monoid_hom.ker φ) (quotient_add_group.ker_lift._proof_1 φ) g

theorem ker_lift_injective {G : Type u} [group G] {H : Type v} [group H] (φ : G →* H) :
    function.injective ⇑(ker_lift φ) :=
  sorry

-- Note that ker φ isn't definitionally ker (to_range φ)

-- so there is a bit of annoying code duplication here

/-- The induced map from the quotient by the kernel to the range. -/
def Mathlib.quotient_add_group.range_ker_lift {G : Type u} [add_group G] {H : Type v} [add_group H]
    (φ : G →+ H) :
    quotient_add_group.quotient (add_monoid_hom.ker φ) →+ ↥(add_monoid_hom.range φ) :=
  quotient_add_group.lift (add_monoid_hom.ker φ) (add_monoid_hom.to_range φ) sorry

theorem range_ker_lift_injective {G : Type u} [group G] {H : Type v} [group H] (φ : G →* H) :
    function.injective ⇑(range_ker_lift φ) :=
  sorry

theorem Mathlib.quotient_add_group.range_ker_lift_surjective {G : Type u} [add_group G] {H : Type v}
    [add_group H] (φ : G →+ H) : function.surjective ⇑(quotient_add_group.range_ker_lift φ) :=
  sorry

/-- The first isomorphism theorem (a definition): the canonical isomorphism between
`G/(ker φ)` to `range φ`. -/
def Mathlib.quotient_add_group.quotient_ker_equiv_range {G : Type u} [add_group G] {H : Type v}
    [add_group H] (φ : G →+ H) :
    quotient_add_group.quotient (add_monoid_hom.ker φ) ≃+ ↥(add_monoid_hom.range φ) :=
  add_equiv.of_bijective (quotient_add_group.range_ker_lift φ) sorry

/-- The canonical isomorphism `G/(ker φ) ≃* H` induced by a surjection `φ : G →* H`. -/
def Mathlib.quotient_add_group.quotient_ker_equiv_of_surjective {G : Type u} [add_group G]
    {H : Type v} [add_group H] (φ : G →+ H) (hφ : function.surjective ⇑φ) :
    quotient_add_group.quotient (add_monoid_hom.ker φ) ≃+ H :=
  add_equiv.of_bijective (quotient_add_group.ker_lift φ) sorry

end Mathlib