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

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

/-!
# Lifting groups with zero along injective/surjective maps

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

open Function

variable {M₀ G₀ M₀' G₀' : Type _}

section MulZeroClass

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

/- warning: function.injective.mul_zero_class -> Function.Injective.mulZeroClass is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : Zero.{u2} M₀'] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_3)))) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) -> (forall (a : M₀') (b : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) a b)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) (f a) (f b))) -> (MulZeroClass.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : Zero.{u2} M₀'] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_3))) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) -> (forall (a : M₀') (b : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) a b)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) (f a) (f b))) -> (MulZeroClass.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.injective.mul_zero_class Function.Injective.mulZeroClassₓ'. -/
/-- Pullback a `mul_zero_class` instance along an injective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Injective.mulZeroClass [Mul M₀'] [Zero M₀'] (f : M₀' → M₀) (hf : Injective f)
    (zero : f 0 = 0) (mul : ∀ a b, f (a * b) = f a * f b) : MulZeroClass M₀'
    where
  mul := (· * ·)
  zero := 0
  zero_mul a := hf <| by simp only [mul, zero, MulZeroClass.zero_mul]
  mul_zero a := hf <| by simp only [mul, zero, MulZeroClass.mul_zero]
#align function.injective.mul_zero_class Function.Injective.mulZeroClass

/- warning: function.surjective.mul_zero_class -> Function.Surjective.mulZeroClass is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : Zero.{u2} M₀'] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ _inst_1))))) (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_3)))) -> (forall (a : M₀) (b : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ _inst_1)) a b)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) (f a) (f b))) -> (MulZeroClass.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroClass.{u1} M₀] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : Zero.{u2} M₀'] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroClass.toZero.{u1} M₀ _inst_1)))) (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_3))) -> (forall (a : M₀) (b : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ _inst_1)) a b)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) (f a) (f b))) -> (MulZeroClass.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.surjective.mul_zero_class Function.Surjective.mulZeroClassₓ'. -/
/-- Pushforward a `mul_zero_class` instance along an surjective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Surjective.mulZeroClass [Mul M₀'] [Zero M₀'] (f : M₀ → M₀')
    (hf : Surjective f) (zero : f 0 = 0) (mul : ∀ a b, f (a * b) = f a * f b) : MulZeroClass M₀'
    where
  mul := (· * ·)
  zero := 0
  mul_zero := hf.forall.2 fun x => by simp only [← zero, ← mul, MulZeroClass.mul_zero]
  zero_mul := hf.forall.2 fun x => by simp only [← zero, ← mul, MulZeroClass.zero_mul]
#align function.surjective.mul_zero_class Function.Surjective.mulZeroClass

end MulZeroClass

section NoZeroDivisors

/- warning: function.injective.no_zero_divisors -> Function.Injective.noZeroDivisors is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Mul.{u1} M₀] [_inst_2 : Zero.{u1} M₀] [_inst_3 : Mul.{u2} M₀'] [_inst_4 : Zero.{u2} M₀'] [_inst_5 : NoZeroDivisors.{u2} M₀' _inst_3 _inst_4] (f : M₀ -> M₀'), (Function.Injective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ _inst_2)))) (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_4)))) -> (forall (x : M₀) (y : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ _inst_1) x y)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_3) (f x) (f y))) -> (NoZeroDivisors.{u1} M₀ _inst_1 _inst_2)
but is expected to have type
  forall {M₀ : Type.{u2}} {M₀' : Type.{u1}} [_inst_1 : Mul.{u2} M₀] [_inst_2 : Zero.{u2} M₀] [_inst_3 : Mul.{u1} M₀'] [_inst_4 : Zero.{u1} M₀'] [_inst_5 : NoZeroDivisors.{u1} M₀' _inst_3 _inst_4] (f : M₀ -> M₀'), (Function.Injective.{succ u2, succ u1} M₀ M₀' f) -> (Eq.{succ u1} M₀' (f (OfNat.ofNat.{u2} M₀ 0 (Zero.toOfNat0.{u2} M₀ _inst_2))) (OfNat.ofNat.{u1} M₀' 0 (Zero.toOfNat0.{u1} M₀' _inst_4))) -> (forall (x : M₀) (y : M₀), Eq.{succ u1} M₀' (f (HMul.hMul.{u2, u2, u2} M₀ M₀ M₀ (instHMul.{u2} M₀ _inst_1) x y)) (HMul.hMul.{u1, u1, u1} M₀' M₀' M₀' (instHMul.{u1} M₀' _inst_3) (f x) (f y))) -> (NoZeroDivisors.{u2} M₀ _inst_1 _inst_2)
Case conversion may be inaccurate. Consider using '#align function.injective.no_zero_divisors Function.Injective.noZeroDivisorsₓ'. -/
/-- Pushforward a `no_zero_divisors` instance along an injective function. -/
protected theorem Function.Injective.noZeroDivisors [Mul M₀] [Zero M₀] [Mul M₀'] [Zero M₀']
    [NoZeroDivisors M₀'] (f : M₀ → M₀') (hf : Injective f) (zero : f 0 = 0)
    (mul : ∀ x y, f (x * y) = f x * f y) : NoZeroDivisors M₀ :=
  {
    eq_zero_or_eq_zero_of_mul_eq_zero := fun x y H =>
      have : f x * f y = 0 := by rw [← mul, H, zero]
      (eq_zero_or_eq_zero_of_mul_eq_zero this).imp (fun H => hf <| by rwa [zero]) fun H =>
        hf <| by rwa [zero] }
#align function.injective.no_zero_divisors Function.Injective.noZeroDivisors

end NoZeroDivisors

section MulZeroOneClass

variable [MulZeroOneClass M₀]

/- warning: function.injective.mul_zero_one_class -> Function.Injective.mulZeroOneClass is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroOneClass.{u1} M₀] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : Zero.{u2} M₀'] [_inst_4 : One.{u2} M₀'] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_3)))) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ _inst_1)))))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (OfNat.mk.{u2} M₀' 1 (One.one.{u2} M₀' _inst_4)))) (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ _inst_1)))))) -> (forall (a : M₀') (b : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) a b)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ _inst_1))) (f a) (f b))) -> (MulZeroOneClass.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroOneClass.{u1} M₀] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : Zero.{u2} M₀'] [_inst_4 : One.{u2} M₀'] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_3))) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroOneClass.toZero.{u1} M₀ _inst_1)))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (One.toOfNat1.{u2} M₀' _inst_4))) (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (MulOneClass.toOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ _inst_1))))) -> (forall (a : M₀') (b : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) a b)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ _inst_1))) (f a) (f b))) -> (MulZeroOneClass.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.injective.mul_zero_one_class Function.Injective.mulZeroOneClassₓ'. -/
/-- Pullback a `mul_zero_one_class` instance along an injective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Injective.mulZeroOneClass [Mul M₀'] [Zero M₀'] [One M₀'] (f : M₀' → M₀)
    (hf : Injective f) (zero : f 0 = 0) (one : f 1 = 1) (mul : ∀ a b, f (a * b) = f a * f b) :
    MulZeroOneClass M₀' :=
  { hf.MulZeroClass f zero mul, hf.MulOneClass f one mul with }
#align function.injective.mul_zero_one_class Function.Injective.mulZeroOneClass

/- warning: function.surjective.mul_zero_one_class -> Function.Surjective.mulZeroOneClass is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroOneClass.{u1} M₀] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : Zero.{u2} M₀'] [_inst_4 : One.{u2} M₀'] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ _inst_1)))))) (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_3)))) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ _inst_1)))))) (OfNat.ofNat.{u2} M₀' 1 (OfNat.mk.{u2} M₀' 1 (One.one.{u2} M₀' _inst_4)))) -> (forall (a : M₀) (b : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ _inst_1))) a b)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) (f a) (f b))) -> (MulZeroOneClass.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : MulZeroOneClass.{u1} M₀] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : Zero.{u2} M₀'] [_inst_4 : One.{u2} M₀'] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MulZeroOneClass.toZero.{u1} M₀ _inst_1)))) (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_3))) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (MulOneClass.toOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ _inst_1))))) (OfNat.ofNat.{u2} M₀' 1 (One.toOfNat1.{u2} M₀' _inst_4))) -> (forall (a : M₀) (b : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ _inst_1))) a b)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) (f a) (f b))) -> (MulZeroOneClass.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.surjective.mul_zero_one_class Function.Surjective.mulZeroOneClassₓ'. -/
/-- Pushforward a `mul_zero_one_class` instance along an surjective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Surjective.mulZeroOneClass [Mul M₀'] [Zero M₀'] [One M₀'] (f : M₀ → M₀')
    (hf : Surjective f) (zero : f 0 = 0) (one : f 1 = 1) (mul : ∀ a b, f (a * b) = f a * f b) :
    MulZeroOneClass M₀' :=
  { hf.MulZeroClass f zero mul, hf.MulOneClass f one mul with }
#align function.surjective.mul_zero_one_class Function.Surjective.mulZeroOneClass

end MulZeroOneClass

section SemigroupWithZero

/- warning: function.injective.semigroup_with_zero -> Function.Injective.semigroupWithZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : SemigroupWithZero.{u1} M₀] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_1)))) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (SemigroupWithZero.toMulZeroClass.{u1} M₀ _inst_3)))))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (SemigroupWithZero.toMulZeroClass.{u1} M₀ _inst_3))) (f x) (f y))) -> (SemigroupWithZero.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : SemigroupWithZero.{u1} M₀] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_1))) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (SemigroupWithZero.toZero.{u1} M₀ _inst_3)))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (SemigroupWithZero.toMulZeroClass.{u1} M₀ _inst_3))) (f x) (f y))) -> (SemigroupWithZero.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.injective.semigroup_with_zero Function.Injective.semigroupWithZeroₓ'. -/
/-- Pullback a `semigroup_with_zero` class along an injective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Injective.semigroupWithZero [Zero M₀'] [Mul M₀'] [SemigroupWithZero M₀]
    (f : M₀' → M₀) (hf : Injective f) (zero : f 0 = 0) (mul : ∀ x y, f (x * y) = f x * f y) :
    SemigroupWithZero M₀' :=
  { hf.MulZeroClass f zero mul, ‹Zero M₀'›, hf.Semigroup f mul with }
#align function.injective.semigroup_with_zero Function.Injective.semigroupWithZero

/- warning: function.surjective.semigroup_with_zero -> Function.Surjective.semigroupWithZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : SemigroupWithZero.{u1} M₀] [_inst_2 : Zero.{u2} M₀'] [_inst_3 : Mul.{u2} M₀'] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (SemigroupWithZero.toMulZeroClass.{u1} M₀ _inst_1)))))) (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_2)))) -> (forall (x : M₀) (y : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (SemigroupWithZero.toMulZeroClass.{u1} M₀ _inst_1))) x y)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_3) (f x) (f y))) -> (SemigroupWithZero.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : SemigroupWithZero.{u1} M₀] [_inst_2 : Zero.{u2} M₀'] [_inst_3 : Mul.{u2} M₀'] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (SemigroupWithZero.toZero.{u1} M₀ _inst_1)))) (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_2))) -> (forall (x : M₀) (y : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (SemigroupWithZero.toMulZeroClass.{u1} M₀ _inst_1))) x y)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_3) (f x) (f y))) -> (SemigroupWithZero.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.surjective.semigroup_with_zero Function.Surjective.semigroupWithZeroₓ'. -/
/-- Pushforward a `semigroup_with_zero` class along an surjective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Surjective.semigroupWithZero [SemigroupWithZero M₀] [Zero M₀'] [Mul M₀']
    (f : M₀ → M₀') (hf : Surjective f) (zero : f 0 = 0) (mul : ∀ x y, f (x * y) = f x * f y) :
    SemigroupWithZero M₀' :=
  { hf.MulZeroClass f zero mul, ‹Zero M₀'›, hf.Semigroup f mul with }
#align function.surjective.semigroup_with_zero Function.Surjective.semigroupWithZero

end SemigroupWithZero

section MonoidWithZero

/- warning: function.injective.monoid_with_zero -> Function.Injective.monoidWithZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : One.{u2} M₀'] [_inst_4 : Pow.{u2, 0} M₀' Nat] [_inst_5 : MonoidWithZero.{u1} M₀] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_1)))) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ _inst_5))))))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (OfNat.mk.{u2} M₀' 1 (One.one.{u2} M₀' _inst_3)))) (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ _inst_5))))))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ _inst_5)))) (f x) (f y))) -> (forall (x : M₀') (n : Nat), Eq.{succ u1} M₀ (f (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_4) x n)) (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ _inst_5))) (f x) n)) -> (MonoidWithZero.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : One.{u2} M₀'] [_inst_4 : Pow.{u2, 0} M₀' Nat] [_inst_5 : MonoidWithZero.{u1} M₀] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_1))) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MonoidWithZero.toZero.{u1} M₀ _inst_5)))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (One.toOfNat1.{u2} M₀' _inst_3))) (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (Monoid.toOne.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ _inst_5))))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ _inst_5)))) (f x) (f y))) -> (forall (x : M₀') (n : Nat), Eq.{succ u1} M₀ (f (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_4) x n)) (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ _inst_5))) (f x) n)) -> (MonoidWithZero.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.injective.monoid_with_zero Function.Injective.monoidWithZeroₓ'. -/
/-- Pullback a `monoid_with_zero` class along an injective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Injective.monoidWithZero [Zero M₀'] [Mul M₀'] [One M₀'] [Pow M₀' ℕ]
    [MonoidWithZero M₀] (f : M₀' → M₀) (hf : Injective f) (zero : f 0 = 0) (one : f 1 = 1)
    (mul : ∀ x y, f (x * y) = f x * f y) (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n) :
    MonoidWithZero M₀' :=
  { hf.Monoid f one mul npow, hf.MulZeroClass f zero mul with }
#align function.injective.monoid_with_zero Function.Injective.monoidWithZero

/- warning: function.surjective.monoid_with_zero -> Function.Surjective.monoidWithZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : One.{u2} M₀'] [_inst_4 : Pow.{u2, 0} M₀' Nat] [_inst_5 : MonoidWithZero.{u1} M₀] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ _inst_5))))))) (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_1)))) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ _inst_5))))))) (OfNat.ofNat.{u2} M₀' 1 (OfNat.mk.{u2} M₀' 1 (One.one.{u2} M₀' _inst_3)))) -> (forall (x : M₀) (y : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ _inst_5)))) x y)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) (f x) (f y))) -> (forall (x : M₀) (n : Nat), Eq.{succ u2} M₀' (f (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ _inst_5))) x n)) (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_4) (f x) n)) -> (MonoidWithZero.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : One.{u2} M₀'] [_inst_4 : Pow.{u2, 0} M₀' Nat] [_inst_5 : MonoidWithZero.{u1} M₀] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MonoidWithZero.toZero.{u1} M₀ _inst_5)))) (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_1))) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (Monoid.toOne.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ _inst_5))))) (OfNat.ofNat.{u2} M₀' 1 (One.toOfNat1.{u2} M₀' _inst_3))) -> (forall (x : M₀) (y : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ _inst_5)))) x y)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) (f x) (f y))) -> (forall (x : M₀) (n : Nat), Eq.{succ u2} M₀' (f (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ _inst_5))) x n)) (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_4) (f x) n)) -> (MonoidWithZero.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.surjective.monoid_with_zero Function.Surjective.monoidWithZeroₓ'. -/
/-- Pushforward a `monoid_with_zero` class along a surjective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Surjective.monoidWithZero [Zero M₀'] [Mul M₀'] [One M₀'] [Pow M₀' ℕ]
    [MonoidWithZero M₀] (f : M₀ → M₀') (hf : Surjective f) (zero : f 0 = 0) (one : f 1 = 1)
    (mul : ∀ x y, f (x * y) = f x * f y) (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n) :
    MonoidWithZero M₀' :=
  { hf.Monoid f one mul npow, hf.MulZeroClass f zero mul with }
#align function.surjective.monoid_with_zero Function.Surjective.monoidWithZero

/- warning: function.injective.comm_monoid_with_zero -> Function.Injective.commMonoidWithZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : One.{u2} M₀'] [_inst_4 : Pow.{u2, 0} M₀' Nat] [_inst_5 : CommMonoidWithZero.{u1} M₀] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_1)))) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))))))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (OfNat.mk.{u2} M₀' 1 (One.one.{u2} M₀' _inst_3)))) (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))))))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5))))) (f x) (f y))) -> (forall (x : M₀') (n : Nat), Eq.{succ u1} M₀ (f (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_4) x n)) (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))) (f x) n)) -> (CommMonoidWithZero.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : One.{u2} M₀'] [_inst_4 : Pow.{u2, 0} M₀' Nat] [_inst_5 : CommMonoidWithZero.{u1} M₀] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_1))) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (CommMonoidWithZero.toZero.{u1} M₀ _inst_5)))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (One.toOfNat1.{u2} M₀' _inst_3))) (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (Monoid.toOne.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5))))) (f x) (f y))) -> (forall (x : M₀') (n : Nat), Eq.{succ u1} M₀ (f (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_4) x n)) (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))) (f x) n)) -> (CommMonoidWithZero.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.injective.comm_monoid_with_zero Function.Injective.commMonoidWithZeroₓ'. -/
/-- Pullback a `monoid_with_zero` class along an injective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Injective.commMonoidWithZero [Zero M₀'] [Mul M₀'] [One M₀'] [Pow M₀' ℕ]
    [CommMonoidWithZero M₀] (f : M₀' → M₀) (hf : Injective f) (zero : f 0 = 0) (one : f 1 = 1)
    (mul : ∀ x y, f (x * y) = f x * f y) (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n) :
    CommMonoidWithZero M₀' :=
  { hf.CommMonoid f one mul npow, hf.MulZeroClass f zero mul with }
#align function.injective.comm_monoid_with_zero Function.Injective.commMonoidWithZero

/- warning: function.surjective.comm_monoid_with_zero -> Function.Surjective.commMonoidWithZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : One.{u2} M₀'] [_inst_4 : Pow.{u2, 0} M₀' Nat] [_inst_5 : CommMonoidWithZero.{u1} M₀] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))))))) (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_1)))) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))))))) (OfNat.ofNat.{u2} M₀' 1 (OfNat.mk.{u2} M₀' 1 (One.one.{u2} M₀' _inst_3)))) -> (forall (x : M₀) (y : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5))))) x y)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) (f x) (f y))) -> (forall (x : M₀) (n : Nat), Eq.{succ u2} M₀' (f (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))) x n)) (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_4) (f x) n)) -> (CommMonoidWithZero.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : Zero.{u2} M₀'] [_inst_2 : Mul.{u2} M₀'] [_inst_3 : One.{u2} M₀'] [_inst_4 : Pow.{u2, 0} M₀' Nat] [_inst_5 : CommMonoidWithZero.{u1} M₀] (f : M₀ -> M₀'), (Function.Surjective.{succ u1, succ u2} M₀ M₀' f) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (CommMonoidWithZero.toZero.{u1} M₀ _inst_5)))) (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_1))) -> (Eq.{succ u2} M₀' (f (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (Monoid.toOne.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))))) (OfNat.ofNat.{u2} M₀' 1 (One.toOfNat1.{u2} M₀' _inst_3))) -> (forall (x : M₀) (y : M₀), Eq.{succ u2} M₀' (f (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5))))) x y)) (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_2) (f x) (f y))) -> (forall (x : M₀) (n : Nat), Eq.{succ u2} M₀' (f (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_5)))) x n)) (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_4) (f x) n)) -> (CommMonoidWithZero.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.surjective.comm_monoid_with_zero Function.Surjective.commMonoidWithZeroₓ'. -/
/-- Pushforward a `monoid_with_zero` class along a surjective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Surjective.commMonoidWithZero [Zero M₀'] [Mul M₀'] [One M₀'] [Pow M₀' ℕ]
    [CommMonoidWithZero M₀] (f : M₀ → M₀') (hf : Surjective f) (zero : f 0 = 0) (one : f 1 = 1)
    (mul : ∀ x y, f (x * y) = f x * f y) (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n) :
    CommMonoidWithZero M₀' :=
  { hf.CommMonoid f one mul npow, hf.MulZeroClass f zero mul with }
#align function.surjective.comm_monoid_with_zero Function.Surjective.commMonoidWithZero

end MonoidWithZero

section CancelMonoidWithZero

variable [CancelMonoidWithZero M₀] {a b c : M₀}

/- warning: function.injective.cancel_monoid_with_zero -> Function.Injective.cancelMonoidWithZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : CancelMonoidWithZero.{u1} M₀] [_inst_2 : Zero.{u2} M₀'] [_inst_3 : Mul.{u2} M₀'] [_inst_4 : One.{u2} M₀'] [_inst_5 : Pow.{u2, 0} M₀' Nat] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_2)))) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CancelMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_1)))))))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (OfNat.mk.{u2} M₀' 1 (One.one.{u2} M₀' _inst_4)))) (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CancelMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_1)))))))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_3) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CancelMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_1))))) (f x) (f y))) -> (forall (x : M₀') (n : Nat), Eq.{succ u1} M₀ (f (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_5) x n)) (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CancelMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_1)))) (f x) n)) -> (CancelMonoidWithZero.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : CancelMonoidWithZero.{u1} M₀] [_inst_2 : Zero.{u2} M₀'] [_inst_3 : Mul.{u2} M₀'] [_inst_4 : One.{u2} M₀'] [_inst_5 : Pow.{u2, 0} M₀' Nat] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_2))) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (MonoidWithZero.toZero.{u1} M₀ (CancelMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_1))))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (One.toOfNat1.{u2} M₀' _inst_4))) (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (Monoid.toOne.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CancelMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_1)))))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_3) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CancelMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_1))))) (f x) (f y))) -> (forall (x : M₀') (n : Nat), Eq.{succ u1} M₀ (f (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_5) x n)) (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CancelMonoidWithZero.toMonoidWithZero.{u1} M₀ _inst_1)))) (f x) n)) -> (CancelMonoidWithZero.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.injective.cancel_monoid_with_zero Function.Injective.cancelMonoidWithZeroₓ'. -/
/-- Pullback a `monoid_with_zero` class along an injective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Injective.cancelMonoidWithZero [Zero M₀'] [Mul M₀'] [One M₀'] [Pow M₀' ℕ]
    (f : M₀' → M₀) (hf : Injective f) (zero : f 0 = 0) (one : f 1 = 1)
    (mul : ∀ x y, f (x * y) = f x * f y) (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n) :
    CancelMonoidWithZero M₀' :=
  { hf.Monoid f one mul npow,
    hf.MulZeroClass f zero
      mul with
    mul_left_cancel_of_ne_zero := fun x y z hx H =>
      hf <| mul_left_cancel₀ ((hf.ne_iff' zero).2 hx) <| by erw [← mul, ← mul, H] <;> rfl
    mul_right_cancel_of_ne_zero := fun x y z hx H =>
      hf <| mul_right_cancel₀ ((hf.ne_iff' zero).2 hx) <| by erw [← mul, ← mul, H] <;> rfl }
#align function.injective.cancel_monoid_with_zero Function.Injective.cancelMonoidWithZero

end CancelMonoidWithZero

section CancelCommMonoidWithZero

variable [CancelCommMonoidWithZero M₀] {a b c : M₀}

/- warning: function.injective.cancel_comm_monoid_with_zero -> Function.Injective.cancelCommMonoidWithZero is a dubious translation:
lean 3 declaration is
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : CancelCommMonoidWithZero.{u1} M₀] [_inst_2 : Zero.{u2} M₀'] [_inst_3 : Mul.{u2} M₀'] [_inst_4 : One.{u2} M₀'] [_inst_5 : Pow.{u2, 0} M₀' Nat] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (OfNat.mk.{u2} M₀' 0 (Zero.zero.{u2} M₀' _inst_2)))) (OfNat.ofNat.{u1} M₀ 0 (OfNat.mk.{u1} M₀ 0 (Zero.zero.{u1} M₀ (MulZeroClass.toHasZero.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ (CancelCommMonoidWithZero.toCommMonoidWithZero.{u1} M₀ _inst_1))))))))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (OfNat.mk.{u2} M₀' 1 (One.one.{u2} M₀' _inst_4)))) (OfNat.ofNat.{u1} M₀ 1 (OfNat.mk.{u1} M₀ 1 (One.one.{u1} M₀ (MulOneClass.toHasOne.{u1} M₀ (MulZeroOneClass.toMulOneClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ (CancelCommMonoidWithZero.toCommMonoidWithZero.{u1} M₀ _inst_1))))))))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_3) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toHasMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ (CancelCommMonoidWithZero.toCommMonoidWithZero.{u1} M₀ _inst_1)))))) (f x) (f y))) -> (forall (x : M₀') (n : Nat), Eq.{succ u1} M₀ (f (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_5) x n)) (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ (CancelCommMonoidWithZero.toCommMonoidWithZero.{u1} M₀ _inst_1))))) (f x) n)) -> (CancelCommMonoidWithZero.{u2} M₀')
but is expected to have type
  forall {M₀ : Type.{u1}} {M₀' : Type.{u2}} [_inst_1 : CancelCommMonoidWithZero.{u1} M₀] [_inst_2 : Zero.{u2} M₀'] [_inst_3 : Mul.{u2} M₀'] [_inst_4 : One.{u2} M₀'] [_inst_5 : Pow.{u2, 0} M₀' Nat] (f : M₀' -> M₀), (Function.Injective.{succ u2, succ u1} M₀' M₀ f) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 0 (Zero.toOfNat0.{u2} M₀' _inst_2))) (OfNat.ofNat.{u1} M₀ 0 (Zero.toOfNat0.{u1} M₀ (CommMonoidWithZero.toZero.{u1} M₀ (CancelCommMonoidWithZero.toCommMonoidWithZero.{u1} M₀ _inst_1))))) -> (Eq.{succ u1} M₀ (f (OfNat.ofNat.{u2} M₀' 1 (One.toOfNat1.{u2} M₀' _inst_4))) (OfNat.ofNat.{u1} M₀ 1 (One.toOfNat1.{u1} M₀ (Monoid.toOne.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ (CancelCommMonoidWithZero.toCommMonoidWithZero.{u1} M₀ _inst_1))))))) -> (forall (x : M₀') (y : M₀'), Eq.{succ u1} M₀ (f (HMul.hMul.{u2, u2, u2} M₀' M₀' M₀' (instHMul.{u2} M₀' _inst_3) x y)) (HMul.hMul.{u1, u1, u1} M₀ M₀ M₀ (instHMul.{u1} M₀ (MulZeroClass.toMul.{u1} M₀ (MulZeroOneClass.toMulZeroClass.{u1} M₀ (MonoidWithZero.toMulZeroOneClass.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ (CancelCommMonoidWithZero.toCommMonoidWithZero.{u1} M₀ _inst_1)))))) (f x) (f y))) -> (forall (x : M₀') (n : Nat), Eq.{succ u1} M₀ (f (HPow.hPow.{u2, 0, u2} M₀' Nat M₀' (instHPow.{u2, 0} M₀' Nat _inst_5) x n)) (HPow.hPow.{u1, 0, u1} M₀ Nat M₀ (instHPow.{u1, 0} M₀ Nat (Monoid.Pow.{u1} M₀ (MonoidWithZero.toMonoid.{u1} M₀ (CommMonoidWithZero.toMonoidWithZero.{u1} M₀ (CancelCommMonoidWithZero.toCommMonoidWithZero.{u1} M₀ _inst_1))))) (f x) n)) -> (CancelCommMonoidWithZero.{u2} M₀')
Case conversion may be inaccurate. Consider using '#align function.injective.cancel_comm_monoid_with_zero Function.Injective.cancelCommMonoidWithZeroₓ'. -/
/-- Pullback a `cancel_comm_monoid_with_zero` class along an injective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Injective.cancelCommMonoidWithZero [Zero M₀'] [Mul M₀'] [One M₀'] [Pow M₀' ℕ]
    (f : M₀' → M₀) (hf : Injective f) (zero : f 0 = 0) (one : f 1 = 1)
    (mul : ∀ x y, f (x * y) = f x * f y) (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n) :
    CancelCommMonoidWithZero M₀' :=
  { hf.CommMonoidWithZero f zero one mul npow, hf.CancelMonoidWithZero f zero one mul npow with }
#align function.injective.cancel_comm_monoid_with_zero Function.Injective.cancelCommMonoidWithZero

end CancelCommMonoidWithZero

section GroupWithZero

variable [GroupWithZero G₀] {a b c g h x : G₀}

/- warning: function.injective.group_with_zero -> Function.Injective.groupWithZero is a dubious translation:
lean 3 declaration is
  forall {G₀ : Type.{u1}} {G₀' : Type.{u2}} [_inst_1 : GroupWithZero.{u1} G₀] [_inst_2 : Zero.{u2} G₀'] [_inst_3 : Mul.{u2} G₀'] [_inst_4 : One.{u2} G₀'] [_inst_5 : Inv.{u2} G₀'] [_inst_6 : Div.{u2} G₀'] [_inst_7 : Pow.{u2, 0} G₀' Nat] [_inst_8 : Pow.{u2, 0} G₀' Int] (f : G₀' -> G₀), (Function.Injective.{succ u2, succ u1} G₀' G₀ f) -> (Eq.{succ u1} G₀ (f (OfNat.ofNat.{u2} G₀' 0 (OfNat.mk.{u2} G₀' 0 (Zero.zero.{u2} G₀' _inst_2)))) (OfNat.ofNat.{u1} G₀ 0 (OfNat.mk.{u1} G₀ 0 (Zero.zero.{u1} G₀ (MulZeroClass.toHasZero.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))))))) -> (Eq.{succ u1} G₀ (f (OfNat.ofNat.{u2} G₀' 1 (OfNat.mk.{u2} G₀' 1 (One.one.{u2} G₀' _inst_4)))) (OfNat.ofNat.{u1} G₀ 1 (OfNat.mk.{u1} G₀ 1 (One.one.{u1} G₀ (MulOneClass.toHasOne.{u1} G₀ (MulZeroOneClass.toMulOneClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))))))) -> (forall (x : G₀') (y : G₀'), Eq.{succ u1} G₀ (f (HMul.hMul.{u2, u2, u2} G₀' G₀' G₀' (instHMul.{u2} G₀' _inst_3) x y)) (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toHasMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))) (f x) (f y))) -> (forall (x : G₀'), Eq.{succ u1} G₀ (f (Inv.inv.{u2} G₀' _inst_5 x)) (Inv.inv.{u1} G₀ (DivInvMonoid.toHasInv.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ _inst_1)) (f x))) -> (forall (x : G₀') (y : G₀'), Eq.{succ u1} G₀ (f (HDiv.hDiv.{u2, u2, u2} G₀' G₀' G₀' (instHDiv.{u2} G₀' _inst_6) x y)) (HDiv.hDiv.{u1, u1, u1} G₀ G₀ G₀ (instHDiv.{u1} G₀ (DivInvMonoid.toHasDiv.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ _inst_1))) (f x) (f y))) -> (forall (x : G₀') (n : Nat), Eq.{succ u1} G₀ (f (HPow.hPow.{u2, 0, u2} G₀' Nat G₀' (instHPow.{u2, 0} G₀' Nat _inst_7) x n)) (HPow.hPow.{u1, 0, u1} G₀ Nat G₀ (instHPow.{u1, 0} G₀ Nat (Monoid.Pow.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))) (f x) n)) -> (forall (x : G₀') (n : Int), Eq.{succ u1} G₀ (f (HPow.hPow.{u2, 0, u2} G₀' Int G₀' (instHPow.{u2, 0} G₀' Int _inst_8) x n)) (HPow.hPow.{u1, 0, u1} G₀ Int G₀ (instHPow.{u1, 0} G₀ Int (DivInvMonoid.Pow.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ _inst_1))) (f x) n)) -> (GroupWithZero.{u2} G₀')
but is expected to have type
  forall {G₀ : Type.{u1}} {G₀' : Type.{u2}} [_inst_1 : GroupWithZero.{u1} G₀] [_inst_2 : Zero.{u2} G₀'] [_inst_3 : Mul.{u2} G₀'] [_inst_4 : One.{u2} G₀'] [_inst_5 : Inv.{u2} G₀'] [_inst_6 : Div.{u2} G₀'] [_inst_7 : Pow.{u2, 0} G₀' Nat] [_inst_8 : Pow.{u2, 0} G₀' Int] (f : G₀' -> G₀), (Function.Injective.{succ u2, succ u1} G₀' G₀ f) -> (Eq.{succ u1} G₀ (f (OfNat.ofNat.{u2} G₀' 0 (Zero.toOfNat0.{u2} G₀' _inst_2))) (OfNat.ofNat.{u1} G₀ 0 (Zero.toOfNat0.{u1} G₀ (MonoidWithZero.toZero.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))) -> (Eq.{succ u1} G₀ (f (OfNat.ofNat.{u2} G₀' 1 (One.toOfNat1.{u2} G₀' _inst_4))) (OfNat.ofNat.{u1} G₀ 1 (One.toOfNat1.{u1} G₀ (Monoid.toOne.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))))) -> (forall (x : G₀') (y : G₀'), Eq.{succ u1} G₀ (f (HMul.hMul.{u2, u2, u2} G₀' G₀' G₀' (instHMul.{u2} G₀' _inst_3) x y)) (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))) (f x) (f y))) -> (forall (x : G₀'), Eq.{succ u1} G₀ (f (Inv.inv.{u2} G₀' _inst_5 x)) (Inv.inv.{u1} G₀ (GroupWithZero.toInv.{u1} G₀ _inst_1) (f x))) -> (forall (x : G₀') (y : G₀'), Eq.{succ u1} G₀ (f (HDiv.hDiv.{u2, u2, u2} G₀' G₀' G₀' (instHDiv.{u2} G₀' _inst_6) x y)) (HDiv.hDiv.{u1, u1, u1} G₀ G₀ G₀ (instHDiv.{u1} G₀ (GroupWithZero.toDiv.{u1} G₀ _inst_1)) (f x) (f y))) -> (forall (x : G₀') (n : Nat), Eq.{succ u1} G₀ (f (HPow.hPow.{u2, 0, u2} G₀' Nat G₀' (instHPow.{u2, 0} G₀' Nat _inst_7) x n)) (HPow.hPow.{u1, 0, u1} G₀ Nat G₀ (instHPow.{u1, 0} G₀ Nat (Monoid.Pow.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))) (f x) n)) -> (forall (x : G₀') (n : Int), Eq.{succ u1} G₀ (f (HPow.hPow.{u2, 0, u2} G₀' Int G₀' (instHPow.{u2, 0} G₀' Int _inst_8) x n)) (HPow.hPow.{u1, 0, u1} G₀ Int G₀ (instHPow.{u1, 0} G₀ Int (DivInvMonoid.Pow.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ _inst_1))) (f x) n)) -> (GroupWithZero.{u2} G₀')
Case conversion may be inaccurate. Consider using '#align function.injective.group_with_zero Function.Injective.groupWithZeroₓ'. -/
/-- Pullback a `group_with_zero` class along an injective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Injective.groupWithZero [Zero G₀'] [Mul G₀'] [One G₀'] [Inv G₀'] [Div G₀']
    [Pow G₀' ℕ] [Pow G₀' ℤ] (f : G₀' → G₀) (hf : Injective f) (zero : f 0 = 0) (one : f 1 = 1)
    (mul : ∀ x y, f (x * y) = f x * f y) (inv : ∀ x, f x⁻¹ = (f x)⁻¹)
    (div : ∀ x y, f (x / y) = f x / f y) (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n)
    (zpow : ∀ (x) (n : ℤ), f (x ^ n) = f x ^ n) : GroupWithZero G₀' :=
  { hf.MonoidWithZero f zero one mul npow, hf.DivInvMonoid f one mul inv div npow zpow,
    pullback_nonzero f zero
      one with
    inv_zero := hf <| by erw [inv, zero, inv_zero]
    mul_inv_cancel := fun x hx =>
      hf <| by erw [one, mul, inv, mul_inv_cancel ((hf.ne_iff' zero).2 hx)] }
#align function.injective.group_with_zero Function.Injective.groupWithZero

/- warning: function.surjective.group_with_zero -> Function.Surjective.groupWithZero is a dubious translation:
lean 3 declaration is
  forall {G₀ : Type.{u1}} {G₀' : Type.{u2}} [_inst_1 : GroupWithZero.{u1} G₀] [_inst_2 : Zero.{u2} G₀'] [_inst_3 : Mul.{u2} G₀'] [_inst_4 : One.{u2} G₀'] [_inst_5 : Inv.{u2} G₀'] [_inst_6 : Div.{u2} G₀'] [_inst_7 : Pow.{u2, 0} G₀' Nat] [_inst_8 : Pow.{u2, 0} G₀' Int], (Ne.{succ u2} G₀' (OfNat.ofNat.{u2} G₀' 0 (OfNat.mk.{u2} G₀' 0 (Zero.zero.{u2} G₀' _inst_2))) (OfNat.ofNat.{u2} G₀' 1 (OfNat.mk.{u2} G₀' 1 (One.one.{u2} G₀' _inst_4)))) -> (forall (f : G₀ -> G₀'), (Function.Surjective.{succ u1, succ u2} G₀ G₀' f) -> (Eq.{succ u2} G₀' (f (OfNat.ofNat.{u1} G₀ 0 (OfNat.mk.{u1} G₀ 0 (Zero.zero.{u1} G₀ (MulZeroClass.toHasZero.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))))))) (OfNat.ofNat.{u2} G₀' 0 (OfNat.mk.{u2} G₀' 0 (Zero.zero.{u2} G₀' _inst_2)))) -> (Eq.{succ u2} G₀' (f (OfNat.ofNat.{u1} G₀ 1 (OfNat.mk.{u1} G₀ 1 (One.one.{u1} G₀ (MulOneClass.toHasOne.{u1} G₀ (MulZeroOneClass.toMulOneClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))))))) (OfNat.ofNat.{u2} G₀' 1 (OfNat.mk.{u2} G₀' 1 (One.one.{u2} G₀' _inst_4)))) -> (forall (x : G₀) (y : G₀), Eq.{succ u2} G₀' (f (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toHasMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))) x y)) (HMul.hMul.{u2, u2, u2} G₀' G₀' G₀' (instHMul.{u2} G₀' _inst_3) (f x) (f y))) -> (forall (x : G₀), Eq.{succ u2} G₀' (f (Inv.inv.{u1} G₀ (DivInvMonoid.toHasInv.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ _inst_1)) x)) (Inv.inv.{u2} G₀' _inst_5 (f x))) -> (forall (x : G₀) (y : G₀), Eq.{succ u2} G₀' (f (HDiv.hDiv.{u1, u1, u1} G₀ G₀ G₀ (instHDiv.{u1} G₀ (DivInvMonoid.toHasDiv.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ _inst_1))) x y)) (HDiv.hDiv.{u2, u2, u2} G₀' G₀' G₀' (instHDiv.{u2} G₀' _inst_6) (f x) (f y))) -> (forall (x : G₀) (n : Nat), Eq.{succ u2} G₀' (f (HPow.hPow.{u1, 0, u1} G₀ Nat G₀ (instHPow.{u1, 0} G₀ Nat (Monoid.Pow.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))) x n)) (HPow.hPow.{u2, 0, u2} G₀' Nat G₀' (instHPow.{u2, 0} G₀' Nat _inst_7) (f x) n)) -> (forall (x : G₀) (n : Int), Eq.{succ u2} G₀' (f (HPow.hPow.{u1, 0, u1} G₀ Int G₀ (instHPow.{u1, 0} G₀ Int (DivInvMonoid.Pow.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ _inst_1))) x n)) (HPow.hPow.{u2, 0, u2} G₀' Int G₀' (instHPow.{u2, 0} G₀' Int _inst_8) (f x) n)) -> (GroupWithZero.{u2} G₀'))
but is expected to have type
  forall {G₀ : Type.{u1}} {G₀' : Type.{u2}} [_inst_1 : GroupWithZero.{u1} G₀] [_inst_2 : Zero.{u2} G₀'] [_inst_3 : Mul.{u2} G₀'] [_inst_4 : One.{u2} G₀'] [_inst_5 : Inv.{u2} G₀'] [_inst_6 : Div.{u2} G₀'] [_inst_7 : Pow.{u2, 0} G₀' Nat] [_inst_8 : Pow.{u2, 0} G₀' Int], (Ne.{succ u2} G₀' (OfNat.ofNat.{u2} G₀' 0 (Zero.toOfNat0.{u2} G₀' _inst_2)) (OfNat.ofNat.{u2} G₀' 1 (One.toOfNat1.{u2} G₀' _inst_4))) -> (forall (f : G₀ -> G₀'), (Function.Surjective.{succ u1, succ u2} G₀ G₀' f) -> (Eq.{succ u2} G₀' (f (OfNat.ofNat.{u1} G₀ 0 (Zero.toOfNat0.{u1} G₀ (MonoidWithZero.toZero.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))) (OfNat.ofNat.{u2} G₀' 0 (Zero.toOfNat0.{u2} G₀' _inst_2))) -> (Eq.{succ u2} G₀' (f (OfNat.ofNat.{u1} G₀ 1 (One.toOfNat1.{u1} G₀ (Monoid.toOne.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))))) (OfNat.ofNat.{u2} G₀' 1 (One.toOfNat1.{u2} G₀' _inst_4))) -> (forall (x : G₀) (y : G₀), Eq.{succ u2} G₀' (f (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1))))) x y)) (HMul.hMul.{u2, u2, u2} G₀' G₀' G₀' (instHMul.{u2} G₀' _inst_3) (f x) (f y))) -> (forall (x : G₀), Eq.{succ u2} G₀' (f (Inv.inv.{u1} G₀ (GroupWithZero.toInv.{u1} G₀ _inst_1) x)) (Inv.inv.{u2} G₀' _inst_5 (f x))) -> (forall (x : G₀) (y : G₀), Eq.{succ u2} G₀' (f (HDiv.hDiv.{u1, u1, u1} G₀ G₀ G₀ (instHDiv.{u1} G₀ (GroupWithZero.toDiv.{u1} G₀ _inst_1)) x y)) (HDiv.hDiv.{u2, u2, u2} G₀' G₀' G₀' (instHDiv.{u2} G₀' _inst_6) (f x) (f y))) -> (forall (x : G₀) (n : Nat), Eq.{succ u2} G₀' (f (HPow.hPow.{u1, 0, u1} G₀ Nat G₀ (instHPow.{u1, 0} G₀ Nat (Monoid.Pow.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ _inst_1)))) x n)) (HPow.hPow.{u2, 0, u2} G₀' Nat G₀' (instHPow.{u2, 0} G₀' Nat _inst_7) (f x) n)) -> (forall (x : G₀) (n : Int), Eq.{succ u2} G₀' (f (HPow.hPow.{u1, 0, u1} G₀ Int G₀ (instHPow.{u1, 0} G₀ Int (DivInvMonoid.Pow.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ _inst_1))) x n)) (HPow.hPow.{u2, 0, u2} G₀' Int G₀' (instHPow.{u2, 0} G₀' Int _inst_8) (f x) n)) -> (GroupWithZero.{u2} G₀'))
Case conversion may be inaccurate. Consider using '#align function.surjective.group_with_zero Function.Surjective.groupWithZeroₓ'. -/
/-- Pushforward a `group_with_zero` class along an surjective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Surjective.groupWithZero [Zero G₀'] [Mul G₀'] [One G₀'] [Inv G₀'] [Div G₀']
    [Pow G₀' ℕ] [Pow G₀' ℤ] (h01 : (0 : G₀') ≠ 1) (f : G₀ → G₀') (hf : Surjective f)
    (zero : f 0 = 0) (one : f 1 = 1) (mul : ∀ x y, f (x * y) = f x * f y)
    (inv : ∀ x, f x⁻¹ = (f x)⁻¹) (div : ∀ x y, f (x / y) = f x / f y)
    (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n) (zpow : ∀ (x) (n : ℤ), f (x ^ n) = f x ^ n) :
    GroupWithZero G₀' :=
  { hf.MonoidWithZero f zero one mul npow,
    hf.DivInvMonoid f one mul inv div npow
      zpow with
    inv_zero := by erw [← zero, ← inv, inv_zero]
    mul_inv_cancel :=
      hf.forall.2 fun x hx => by
        erw [← inv, ← mul, mul_inv_cancel (mt (congr_arg f) <| trans_rel_left Ne hx zero.symm)] <;>
          exact one
    exists_pair_ne := ⟨0, 1, h01⟩ }
#align function.surjective.group_with_zero Function.Surjective.groupWithZero

end GroupWithZero

section CommGroupWithZero

variable [CommGroupWithZero G₀] {a b c d : G₀}

/- warning: function.injective.comm_group_with_zero -> Function.Injective.commGroupWithZero is a dubious translation:
lean 3 declaration is
  forall {G₀ : Type.{u1}} {G₀' : Type.{u2}} [_inst_1 : CommGroupWithZero.{u1} G₀] [_inst_2 : Zero.{u2} G₀'] [_inst_3 : Mul.{u2} G₀'] [_inst_4 : One.{u2} G₀'] [_inst_5 : Inv.{u2} G₀'] [_inst_6 : Div.{u2} G₀'] [_inst_7 : Pow.{u2, 0} G₀' Nat] [_inst_8 : Pow.{u2, 0} G₀' Int] (f : G₀' -> G₀), (Function.Injective.{succ u2, succ u1} G₀' G₀ f) -> (Eq.{succ u1} G₀ (f (OfNat.ofNat.{u2} G₀' 0 (OfNat.mk.{u2} G₀' 0 (Zero.zero.{u2} G₀' _inst_2)))) (OfNat.ofNat.{u1} G₀ 0 (OfNat.mk.{u1} G₀ 0 (Zero.zero.{u1} G₀ (MulZeroClass.toHasZero.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))))))) -> (Eq.{succ u1} G₀ (f (OfNat.ofNat.{u2} G₀' 1 (OfNat.mk.{u2} G₀' 1 (One.one.{u2} G₀' _inst_4)))) (OfNat.ofNat.{u1} G₀ 1 (OfNat.mk.{u1} G₀ 1 (One.one.{u1} G₀ (MulOneClass.toHasOne.{u1} G₀ (MulZeroOneClass.toMulOneClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))))))) -> (forall (x : G₀') (y : G₀'), Eq.{succ u1} G₀ (f (HMul.hMul.{u2, u2, u2} G₀' G₀' G₀' (instHMul.{u2} G₀' _inst_3) x y)) (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toHasMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))))) (f x) (f y))) -> (forall (x : G₀'), Eq.{succ u1} G₀ (f (Inv.inv.{u2} G₀' _inst_5 x)) (Inv.inv.{u1} G₀ (DivInvMonoid.toHasInv.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))) (f x))) -> (forall (x : G₀') (y : G₀'), Eq.{succ u1} G₀ (f (HDiv.hDiv.{u2, u2, u2} G₀' G₀' G₀' (instHDiv.{u2} G₀' _inst_6) x y)) (HDiv.hDiv.{u1, u1, u1} G₀ G₀ G₀ (instHDiv.{u1} G₀ (DivInvMonoid.toHasDiv.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))) (f x) (f y))) -> (forall (x : G₀') (n : Nat), Eq.{succ u1} G₀ (f (HPow.hPow.{u2, 0, u2} G₀' Nat G₀' (instHPow.{u2, 0} G₀' Nat _inst_7) x n)) (HPow.hPow.{u1, 0, u1} G₀ Nat G₀ (instHPow.{u1, 0} G₀ Nat (Monoid.Pow.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))) (f x) n)) -> (forall (x : G₀') (n : Int), Eq.{succ u1} G₀ (f (HPow.hPow.{u2, 0, u2} G₀' Int G₀' (instHPow.{u2, 0} G₀' Int _inst_8) x n)) (HPow.hPow.{u1, 0, u1} G₀ Int G₀ (instHPow.{u1, 0} G₀ Int (DivInvMonoid.Pow.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))) (f x) n)) -> (CommGroupWithZero.{u2} G₀')
but is expected to have type
  forall {G₀ : Type.{u1}} {G₀' : Type.{u2}} [_inst_1 : CommGroupWithZero.{u1} G₀] [_inst_2 : Zero.{u2} G₀'] [_inst_3 : Mul.{u2} G₀'] [_inst_4 : One.{u2} G₀'] [_inst_5 : Inv.{u2} G₀'] [_inst_6 : Div.{u2} G₀'] [_inst_7 : Pow.{u2, 0} G₀' Nat] [_inst_8 : Pow.{u2, 0} G₀' Int] (f : G₀' -> G₀), (Function.Injective.{succ u2, succ u1} G₀' G₀ f) -> (Eq.{succ u1} G₀ (f (OfNat.ofNat.{u2} G₀' 0 (Zero.toOfNat0.{u2} G₀' _inst_2))) (OfNat.ofNat.{u1} G₀ 0 (Zero.toOfNat0.{u1} G₀ (CommMonoidWithZero.toZero.{u1} G₀ (CommGroupWithZero.toCommMonoidWithZero.{u1} G₀ _inst_1))))) -> (Eq.{succ u1} G₀ (f (OfNat.ofNat.{u2} G₀' 1 (One.toOfNat1.{u2} G₀' _inst_4))) (OfNat.ofNat.{u1} G₀ 1 (One.toOfNat1.{u1} G₀ (Monoid.toOne.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))))) -> (forall (x : G₀') (y : G₀'), Eq.{succ u1} G₀ (f (HMul.hMul.{u2, u2, u2} G₀' G₀' G₀' (instHMul.{u2} G₀' _inst_3) x y)) (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))))) (f x) (f y))) -> (forall (x : G₀'), Eq.{succ u1} G₀ (f (Inv.inv.{u2} G₀' _inst_5 x)) (Inv.inv.{u1} G₀ (CommGroupWithZero.toInv.{u1} G₀ _inst_1) (f x))) -> (forall (x : G₀') (y : G₀'), Eq.{succ u1} G₀ (f (HDiv.hDiv.{u2, u2, u2} G₀' G₀' G₀' (instHDiv.{u2} G₀' _inst_6) x y)) (HDiv.hDiv.{u1, u1, u1} G₀ G₀ G₀ (instHDiv.{u1} G₀ (CommGroupWithZero.toDiv.{u1} G₀ _inst_1)) (f x) (f y))) -> (forall (x : G₀') (n : Nat), Eq.{succ u1} G₀ (f (HPow.hPow.{u2, 0, u2} G₀' Nat G₀' (instHPow.{u2, 0} G₀' Nat _inst_7) x n)) (HPow.hPow.{u1, 0, u1} G₀ Nat G₀ (instHPow.{u1, 0} G₀ Nat (Monoid.Pow.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))) (f x) n)) -> (forall (x : G₀') (n : Int), Eq.{succ u1} G₀ (f (HPow.hPow.{u2, 0, u2} G₀' Int G₀' (instHPow.{u2, 0} G₀' Int _inst_8) x n)) (HPow.hPow.{u1, 0, u1} G₀ Int G₀ (instHPow.{u1, 0} G₀ Int (DivInvMonoid.Pow.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))) (f x) n)) -> (CommGroupWithZero.{u2} G₀')
Case conversion may be inaccurate. Consider using '#align function.injective.comm_group_with_zero Function.Injective.commGroupWithZeroₓ'. -/
/-- Pullback a `comm_group_with_zero` class along an injective function.
See note [reducible non-instances]. -/
@[reducible]
protected def Function.Injective.commGroupWithZero [Zero G₀'] [Mul G₀'] [One G₀'] [Inv G₀']
    [Div G₀'] [Pow G₀' ℕ] [Pow G₀' ℤ] (f : G₀' → G₀) (hf : Injective f) (zero : f 0 = 0)
    (one : f 1 = 1) (mul : ∀ x y, f (x * y) = f x * f y) (inv : ∀ x, f x⁻¹ = (f x)⁻¹)
    (div : ∀ x y, f (x / y) = f x / f y) (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n)
    (zpow : ∀ (x) (n : ℤ), f (x ^ n) = f x ^ n) : CommGroupWithZero G₀' :=
  { hf.GroupWithZero f zero one mul inv div npow zpow, hf.CommSemigroup f mul with }
#align function.injective.comm_group_with_zero Function.Injective.commGroupWithZero

/- warning: function.surjective.comm_group_with_zero -> Function.Surjective.commGroupWithZero is a dubious translation:
lean 3 declaration is
  forall {G₀ : Type.{u1}} {G₀' : Type.{u2}} [_inst_1 : CommGroupWithZero.{u1} G₀] [_inst_2 : Zero.{u2} G₀'] [_inst_3 : Mul.{u2} G₀'] [_inst_4 : One.{u2} G₀'] [_inst_5 : Inv.{u2} G₀'] [_inst_6 : Div.{u2} G₀'] [_inst_7 : Pow.{u2, 0} G₀' Nat] [_inst_8 : Pow.{u2, 0} G₀' Int], (Ne.{succ u2} G₀' (OfNat.ofNat.{u2} G₀' 0 (OfNat.mk.{u2} G₀' 0 (Zero.zero.{u2} G₀' _inst_2))) (OfNat.ofNat.{u2} G₀' 1 (OfNat.mk.{u2} G₀' 1 (One.one.{u2} G₀' _inst_4)))) -> (forall (f : G₀ -> G₀'), (Function.Surjective.{succ u1, succ u2} G₀ G₀' f) -> (Eq.{succ u2} G₀' (f (OfNat.ofNat.{u1} G₀ 0 (OfNat.mk.{u1} G₀ 0 (Zero.zero.{u1} G₀ (MulZeroClass.toHasZero.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))))))) (OfNat.ofNat.{u2} G₀' 0 (OfNat.mk.{u2} G₀' 0 (Zero.zero.{u2} G₀' _inst_2)))) -> (Eq.{succ u2} G₀' (f (OfNat.ofNat.{u1} G₀ 1 (OfNat.mk.{u1} G₀ 1 (One.one.{u1} G₀ (MulOneClass.toHasOne.{u1} G₀ (MulZeroOneClass.toMulOneClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))))))) (OfNat.ofNat.{u2} G₀' 1 (OfNat.mk.{u2} G₀' 1 (One.one.{u2} G₀' _inst_4)))) -> (forall (x : G₀) (y : G₀), Eq.{succ u2} G₀' (f (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toHasMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))))) x y)) (HMul.hMul.{u2, u2, u2} G₀' G₀' G₀' (instHMul.{u2} G₀' _inst_3) (f x) (f y))) -> (forall (x : G₀), Eq.{succ u2} G₀' (f (Inv.inv.{u1} G₀ (DivInvMonoid.toHasInv.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))) x)) (Inv.inv.{u2} G₀' _inst_5 (f x))) -> (forall (x : G₀) (y : G₀), Eq.{succ u2} G₀' (f (HDiv.hDiv.{u1, u1, u1} G₀ G₀ G₀ (instHDiv.{u1} G₀ (DivInvMonoid.toHasDiv.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))) x y)) (HDiv.hDiv.{u2, u2, u2} G₀' G₀' G₀' (instHDiv.{u2} G₀' _inst_6) (f x) (f y))) -> (forall (x : G₀) (n : Nat), Eq.{succ u2} G₀' (f (HPow.hPow.{u1, 0, u1} G₀ Nat G₀ (instHPow.{u1, 0} G₀ Nat (Monoid.Pow.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))) x n)) (HPow.hPow.{u2, 0, u2} G₀' Nat G₀' (instHPow.{u2, 0} G₀' Nat _inst_7) (f x) n)) -> (forall (x : G₀) (n : Int), Eq.{succ u2} G₀' (f (HPow.hPow.{u1, 0, u1} G₀ Int G₀ (instHPow.{u1, 0} G₀ Int (DivInvMonoid.Pow.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))) x n)) (HPow.hPow.{u2, 0, u2} G₀' Int G₀' (instHPow.{u2, 0} G₀' Int _inst_8) (f x) n)) -> (CommGroupWithZero.{u2} G₀'))
but is expected to have type
  forall {G₀ : Type.{u1}} {G₀' : Type.{u2}} [_inst_1 : CommGroupWithZero.{u1} G₀] [_inst_2 : Zero.{u2} G₀'] [_inst_3 : Mul.{u2} G₀'] [_inst_4 : One.{u2} G₀'] [_inst_5 : Inv.{u2} G₀'] [_inst_6 : Div.{u2} G₀'] [_inst_7 : Pow.{u2, 0} G₀' Nat] [_inst_8 : Pow.{u2, 0} G₀' Int], (Ne.{succ u2} G₀' (OfNat.ofNat.{u2} G₀' 0 (Zero.toOfNat0.{u2} G₀' _inst_2)) (OfNat.ofNat.{u2} G₀' 1 (One.toOfNat1.{u2} G₀' _inst_4))) -> (forall (f : G₀ -> G₀'), (Function.Surjective.{succ u1, succ u2} G₀ G₀' f) -> (Eq.{succ u2} G₀' (f (OfNat.ofNat.{u1} G₀ 0 (Zero.toOfNat0.{u1} G₀ (CommMonoidWithZero.toZero.{u1} G₀ (CommGroupWithZero.toCommMonoidWithZero.{u1} G₀ _inst_1))))) (OfNat.ofNat.{u2} G₀' 0 (Zero.toOfNat0.{u2} G₀' _inst_2))) -> (Eq.{succ u2} G₀' (f (OfNat.ofNat.{u1} G₀ 1 (One.toOfNat1.{u1} G₀ (Monoid.toOne.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))))) (OfNat.ofNat.{u2} G₀' 1 (One.toOfNat1.{u2} G₀' _inst_4))) -> (forall (x : G₀) (y : G₀), Eq.{succ u2} G₀' (f (HMul.hMul.{u1, u1, u1} G₀ G₀ G₀ (instHMul.{u1} G₀ (MulZeroClass.toMul.{u1} G₀ (MulZeroOneClass.toMulZeroClass.{u1} G₀ (MonoidWithZero.toMulZeroOneClass.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))))) x y)) (HMul.hMul.{u2, u2, u2} G₀' G₀' G₀' (instHMul.{u2} G₀' _inst_3) (f x) (f y))) -> (forall (x : G₀), Eq.{succ u2} G₀' (f (Inv.inv.{u1} G₀ (CommGroupWithZero.toInv.{u1} G₀ _inst_1) x)) (Inv.inv.{u2} G₀' _inst_5 (f x))) -> (forall (x : G₀) (y : G₀), Eq.{succ u2} G₀' (f (HDiv.hDiv.{u1, u1, u1} G₀ G₀ G₀ (instHDiv.{u1} G₀ (CommGroupWithZero.toDiv.{u1} G₀ _inst_1)) x y)) (HDiv.hDiv.{u2, u2, u2} G₀' G₀' G₀' (instHDiv.{u2} G₀' _inst_6) (f x) (f y))) -> (forall (x : G₀) (n : Nat), Eq.{succ u2} G₀' (f (HPow.hPow.{u1, 0, u1} G₀ Nat G₀ (instHPow.{u1, 0} G₀ Nat (Monoid.Pow.{u1} G₀ (MonoidWithZero.toMonoid.{u1} G₀ (GroupWithZero.toMonoidWithZero.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1))))) x n)) (HPow.hPow.{u2, 0, u2} G₀' Nat G₀' (instHPow.{u2, 0} G₀' Nat _inst_7) (f x) n)) -> (forall (x : G₀) (n : Int), Eq.{succ u2} G₀' (f (HPow.hPow.{u1, 0, u1} G₀ Int G₀ (instHPow.{u1, 0} G₀ Int (DivInvMonoid.Pow.{u1} G₀ (GroupWithZero.toDivInvMonoid.{u1} G₀ (CommGroupWithZero.toGroupWithZero.{u1} G₀ _inst_1)))) x n)) (HPow.hPow.{u2, 0, u2} G₀' Int G₀' (instHPow.{u2, 0} G₀' Int _inst_8) (f x) n)) -> (CommGroupWithZero.{u2} G₀'))
Case conversion may be inaccurate. Consider using '#align function.surjective.comm_group_with_zero Function.Surjective.commGroupWithZeroₓ'. -/
/-- Pushforward a `comm_group_with_zero` class along a surjective function. -/
protected def Function.Surjective.commGroupWithZero [Zero G₀'] [Mul G₀'] [One G₀'] [Inv G₀']
    [Div G₀'] [Pow G₀' ℕ] [Pow G₀' ℤ] (h01 : (0 : G₀') ≠ 1) (f : G₀ → G₀') (hf : Surjective f)
    (zero : f 0 = 0) (one : f 1 = 1) (mul : ∀ x y, f (x * y) = f x * f y)
    (inv : ∀ x, f x⁻¹ = (f x)⁻¹) (div : ∀ x y, f (x / y) = f x / f y)
    (npow : ∀ (x) (n : ℕ), f (x ^ n) = f x ^ n) (zpow : ∀ (x) (n : ℤ), f (x ^ n) = f x ^ n) :
    CommGroupWithZero G₀' :=
  { hf.GroupWithZero h01 f zero one mul inv div npow zpow, hf.CommSemigroup f mul with }
#align function.surjective.comm_group_with_zero Function.Surjective.commGroupWithZero

end CommGroupWithZero


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import category_theory.limits.types
import category_theory.functor.currying
import category_theory.limits.functor_category

/-!
# The morphism comparing a colimit of limits with the corresponding limit of colimits.

For `F : J × K ⥤ C` there is always a morphism $\colim_k \lim_j F(j,k) → \lim_j \colim_k F(j, k)$.
While it is not usually an isomorphism, with additional hypotheses on `J` and `K` it may be,
in which case we say that "colimits commute with limits".

The prototypical example, proved in `category_theory.limits.filtered_colimit_commutes_finite_limit`,
is that when `C = Type`, filtered colimits commute with finite limits.

## References
* Borceux, Handbook of categorical algebra 1, Section 2.13
* [Stacks: Filtered colimits](https://stacks.math.columbia.edu/tag/002W)
-/

universes v₂ v u

open category_theory

namespace category_theory.limits

variables {J K : Type v} [small_category J] [small_category K]
variables {C : Type u} [category.{v} C]

variables (F : J × K ⥤ C)

open category_theory.prod

lemma map_id_left_eq_curry_map {j : J} {k k' : K} {f : k ⟶ k'} :
  F.map ((𝟙 j, f) : (j, k) ⟶ (j, k')) = ((curry.obj F).obj j).map f :=
rfl

lemma map_id_right_eq_curry_swap_map {j j' : J} {f : j ⟶ j'} {k : K} :
  F.map ((f, 𝟙 k) : (j, k) ⟶ (j', k)) = ((curry.obj (swap K J ⋙ F)).obj k).map f :=
rfl

variables [has_limits_of_shape J C]
variables [has_colimits_of_shape K C]

/--
The universal morphism
$\colim_k \lim_j F(j,k) → \lim_j \colim_k F(j, k)$.
-/
noncomputable
def colimit_limit_to_limit_colimit :
  colimit ((curry.obj (swap K J ⋙ F)) ⋙ lim) ⟶ limit ((curry.obj F) ⋙ colim) :=
limit.lift ((curry.obj F) ⋙ colim)
{ X := _,
  π :=
  { app := λ j, colimit.desc ((curry.obj (swap K J ⋙ F)) ⋙ lim)
    { X := _,
      ι :=
      { app := λ k,
          limit.π ((curry.obj (swap K J ⋙ F)).obj k) j ≫ colimit.ι ((curry.obj F).obj j) k,
        naturality' :=
        begin
          dsimp,
          intros k k' f,
          simp only [functor.comp_map, curry.obj_map_app, limits.lim_map_π_assoc, swap_map,
            category.comp_id, map_id_left_eq_curry_map, colimit.w],
        end }, },
    naturality' :=
    begin
      dsimp,
      intros j j' f,
      ext k,
      simp only [limits.colimit.ι_map, curry.obj_map_app, limits.colimit.ι_desc_assoc,
        limits.colimit.ι_desc, category.id_comp, category.assoc, map_id_right_eq_curry_swap_map,
        limit.w_assoc],
    end } }

/--
Since `colimit_limit_to_limit_colimit` is a morphism from a colimit to a limit,
this lemma characterises it.
-/
@[simp, reassoc] lemma ι_colimit_limit_to_limit_colimit_π (j) (k) :
  colimit.ι _ k ≫ colimit_limit_to_limit_colimit F ≫ limit.π _ j =
    limit.π ((curry.obj (swap K J ⋙ F)).obj k) j ≫ colimit.ι ((curry.obj F).obj j) k :=
by { dsimp [colimit_limit_to_limit_colimit], simp, }

@[simp] lemma ι_colimit_limit_to_limit_colimit_π_apply (F : J × K ⥤ Type v) (j) (k) (f) :
   limit.π ((curry.obj F) ⋙ colim) j
     (colimit_limit_to_limit_colimit F (colimit.ι ((curry.obj (swap K J ⋙ F)) ⋙ lim) k f)) =
     colimit.ι ((curry.obj F).obj j) k (limit.π ((curry.obj (swap K J ⋙ F)).obj k) j f) :=
by { dsimp [colimit_limit_to_limit_colimit], simp, }

/-- The map `colimit_limit_to_limit_colimit` realized as a map of cones. -/
@[simps] noncomputable def colimit_limit_to_limit_colimit_cone (G : J ⥤ K ⥤ C) [has_limit G] :
  colim.map_cone (limit.cone G) ⟶ limit.cone (G ⋙ colim) :=
{ hom := colim.map (limit_iso_swap_comp_lim G).hom ≫
    colimit_limit_to_limit_colimit (uncurry.obj G : _) ≫
    lim.map (whisker_right (currying.unit_iso.app G).inv colim),
  w' := λ j,
  begin
    ext1 k,
    simp only [limit_obj_iso_limit_comp_evaluation_hom_π_assoc, iso.app_inv,
      ι_colimit_limit_to_limit_colimit_π_assoc, whisker_right_app,
      colimit.ι_map, functor.map_cone_π_app, category.id_comp,
      eq_to_hom_refl, eq_to_hom_app, colimit.ι_map_assoc, limit.cone_π,
      lim_map_π_assoc, lim_map_π, category.assoc, currying_unit_iso_inv_app_app_app,
      limit_iso_swap_comp_lim_hom_app, lim_map_eq_lim_map],
    dsimp,
    simp only [category.id_comp],
    erw limit_obj_iso_limit_comp_evaluation_hom_π_assoc,
  end }

end category_theory.limits


namespace DeBruijn

inductive Tp : Type where
| natural : Tp
| function : Tp → Tp → Tp

notation:max "ℕ" => Tp.natural
infixr:70 "⇒" => Tp.function

example : Tp := ℕ ⇒ ℕ

inductive TpEnv : Type where
| empty : TpEnv
| extend : TpEnv → Tp → TpEnv

notation:max "∅" => TpEnv.empty
infixl:50 "▷" => TpEnv.extend

example : TpEnv := ∅ ▷ ℕ ⇒ ℕ ▷ ℕ

inductive lookup : TpEnv → Tp → Type where
  | stop :
       ----------------
       lookup (Γ ▷ A) A
  | pop :
       lookup Γ B
       ----------------
     → lookup (Γ ▷ A) B
  deriving Repr

inductive term : TpEnv → Tp → Type where
  | var :
        lookup Γ A
        ----------
      → term Γ A
  | lambda :
        term (Γ ▷ A) B
        --------------
      → term Γ (A ⇒ B)
  | application :
        term Γ (A ⇒ B)
      → term Γ A
        --------
      → term Γ B
  | zero :
        --------
        term Γ ℕ
  | succ :
        term Γ ℕ
        --------
      → term Γ ℕ
  | case :
        term Γ ℕ
      → term Γ A
      → term (Γ ▷ ℕ) A
        ---------------
      → term Γ A
  | mu :
        term (Γ ▷ A) A
        --------------
      → term Γ A
  deriving Repr

infix:40  "∋" => lookup
infix:40  "⊢" => term

prefix:90 "S" => lookup.pop
notation:max "Z" => lookup.stop

prefix:90 "#" => term.var
prefix:50 "ƛ" => term.lambda
infixl:70 "⬝" => term.application
notation:max "o" => term.zero
postfix:80 "+1" => term.succ
notation:max "switch" => term.case
prefix:50 "μ" => term.mu

-- instance : OfNat (Γ ▷ A ∋ A) Nat.zero where
--   ofNat := lookup.stop

-- instance [OfNat (Γ ∋ B) n] : OfNat (Γ ▷ A ∋ B) (Nat.succ n) where
--   ofNat := lookup.pop (OfNat.ofNat n)

def two : ∀ {Γ}, Γ ⊢ ℕ := o +1 +1
def plus : ∀ {Γ}, Γ ⊢ ℕ ⇒ ℕ ⇒ ℕ :=
  μ ƛ ƛ (switch (# Z) (# S Z) ((# S S S Z ⬝ # S S Z ⬝ # Z) +1))
def four : ∀ {Γ}, Γ ⊢ ℕ := plus ⬝ two ⬝ two

def rmap (Γ Δ : TpEnv) : Type :=
  ∀ {A : Tp}, (Γ ∋ A) → (Δ ∋ A)

def smap (Γ Δ : TpEnv) : Type :=
  ∀ {A : Tp}, (Γ ∋ A) → (Δ ⊢ A)

def tmap (Γ Δ : TpEnv) : Type :=
  ∀ {A : Tp}, (Γ ⊢ A) → (Δ ⊢ A)

infix:30 "→ʳ" => rmap
infix:30 "→ˢ" => smap
infix:30 "→ᵗ" => tmap

def ren_ext {Γ Δ : TpEnv} {A :Tp} (ρ : Γ →ʳ Δ) : (Γ ▷ A →ʳ Δ ▷ A)
  | _ , Z  =>  Z
  | _ , S x  =>  S (ρ x)

def ren {Γ Δ : TpEnv} (ρ : Γ →ʳ Δ) : Γ →ᵗ Δ
  | _ , (# x) => # (ρ x)
  | _ , (ƛ N) => ƛ (ren (ren_ext ρ) N)
  | _ , (L ⬝ M) => ren ρ L ⬝ ren ρ M
  | _ , o => o
  | _ , M +1 => ren ρ M +1
  | _ , switch L M N =>
        switch (ren ρ L) (ren ρ M) (ren (ren_ext ρ) N)
  | _ , μ N => μ (ren (ren_ext ρ) N)

def lift {Γ : TpEnv} {A : Tp} : Γ →ᵗ Γ ▷ A := ren (fun x => S x)

def sub_ext {Γ Δ : TpEnv} {A : Tp} (σ : Γ →ˢ Δ) : (Γ ▷ A →ˢ Δ ▷ A)
| _ , Z  =>  # Z
| _ , S x  =>  lift (σ x)

def sub {Γ Δ : TpEnv} (σ : Γ →ˢ Δ) : Γ →ᵗ Δ
  | _ , (# x) => σ x
  | _ , (ƛ N) => ƛ (sub (sub_ext σ) N)
  | _ , (L ⬝ M) => sub σ L ⬝ sub σ M
  | _ , o => o
  | _ , M +1 => sub σ M +1
  | _ , switch L M N =>
        switch (sub σ L) (sub σ M) (sub (sub_ext σ) N)
  | _ , μ N => μ (sub (sub_ext σ) N)

def sigma_0 (M : Γ ⊢ A) : Γ ▷ A →ˢ Γ
  | _ , Z    =>  M
  | _ , S x  =>  # x

def subst {Γ : TpEnv} {A B : Tp} (N : Γ ▷ A ⊢ B) (M : Γ ⊢ A) : Γ ⊢ B
  := sub (sigma_0 M) N

inductive Value : Γ ⊢ A → Type where
  | lambda :
      Value (ƛ N)
  | zero :
      Value o
  | succ :
      Value V → Value (V +1)
  deriving Repr

inductive reduce : Γ ⊢ A → Γ ⊢ A → Type where
  | xi_app_1 :
      reduce L L' → reduce (L ⬝ M) (L' ⬝ M)
  | xi_app_2 :
      Value V → reduce M M' → reduce (V ⬝ M) (V ⬝ M')
  | beta :
      Value W → reduce ((ƛ N) ⬝ W) (subst N W)
  | xi_succ :
      reduce M M' → reduce (M +1) (M' +1)
  | xi_case :
      reduce L L' → reduce (switch L M N) (switch L' M N)
  | beta_zero :
      reduce (switch o M N) M
  | beta_succ :
      Value V → reduce (switch (V +1) M N) (subst N V)
  | beta_mu :
      reduce (μ N) (subst N (μ N))
  deriving Repr

open reduce

infix:20 "~>" => reduce

-- inductive star : (Γ ⊢ A) → (Γ ⊢ A) → Type where
--   | none :
--       star M M
--   | one :
--         (M ~> N)
--         ----------
--       → star M N
--   | two :
--         star L M
--       → star M N
--         ----------
--       → star L N

-- infix:20 "~>*" => star

-- instance : Trans (star : (Γ ⊢ A) → (Γ ⊢ A) → Type)
--                  (star : (Γ ⊢ A) → (Γ ⊢ A) → Type)
--                  (star : (Γ ⊢ A) → (Γ ⊢ A) → Type) where
--   trans := star.two

inductive reduce_many : Γ ⊢ A → Γ ⊢ A → Type where
  | nil :
      reduce_many M M
  | cons :
      ∀ L, reduce L M → reduce_many M N → reduce_many L N
  deriving Repr

open reduce_many

infix:20 "~>>"  => reduce_many

theorem reduce_many_trans : (L ~>> M) → (M ~>> N) → (L ~>> N)
  := by
      intros L_to_M M_to_N
      induction L_to_M with
      | nil =>
        exact M_to_N
      | cons _ L_to_P _ ih =>
        apply cons
        exact L_to_P
        apply ih
        exact M_to_N

inductive Progress : Γ ⊢ A → Type where
  | step :
      (M ~> N) → Progress M
  | done :
      Value V → Progress V
  deriving Repr

open Progress

def progress : ∀ (M : ∅ ⊢ A), Progress M
  | (# x) => by contradiction
  | (ƛ N) => done Value.lambda
  | (L ⬝ M) =>
      match progress L with
        | step L_to_L' => step (xi_app_1 L_to_L')
        | done v =>
            match progress M with
              | step M_to_M' => step (xi_app_2 v M_to_M')
              | done w =>
                  match v with
                    | Value.lambda => step (beta w)
  | o => done Value.zero
  | (M +1) =>
      match progress M with
        | step M_to_M' => step (xi_succ M_to_M')
        | done v => done (Value.succ v)
  | (switch L M N) =>
      match progress L with
        | step L_to_L' => step (xi_case L_to_L')
        | done v =>
            match v with
              | Value.zero => step beta_zero
              | Value.succ v => step (beta_succ v)
  | (μ N) => step beta_mu

inductive Finished (N : ∅ ⊢ A) : Type where
  | done : Value N → Finished N
  | out_of_gas : Finished N
  deriving Repr

open Finished

inductive Steps (L : ∅ ⊢ A) : Type where
  | steps : (L ~>> N) → Finished N → Steps L
  deriving Repr

open Steps

def evaluate (n : Nat) (L : ∅ ⊢ A) : Steps L :=
  match n with
    | Nat.zero => steps nil out_of_gas
    | Nat.succ n =>
        match progress L with
          | Progress.done v => Steps.steps nil (done v)
          | @Progress.step _ _ _ M L_to_M =>
              match evaluate n M with
                | Steps.steps M_to_N f => Steps.steps (cons L L_to_M M_to_N) f

#eval (evaluate 100 four)

-- Exercise. Add products, as detailed in
--  https://plfa.inf.ed.ac.uk/More/#products
-- Pick suitable notations for introduction and elimination
-- of products that won't conflict.


/-
Copyright (c) 2021 Anne Baanen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Anne Baanen
-/
import algebra.order.field

/-!
# Absolute values

This file defines a bundled type of absolute values `absolute_value R S`.

## Main definitions

 * `absolute_value R S` is the type of absolute values on `R` mapping to `S`.
 * `absolute_value.abs` is the "standard" absolute value on `S`, mapping negative `x` to `-x`.
 * `absolute_value.to_monoid_with_zero_hom`: absolute values mapping to a
   linear ordered field preserve `0`, `*` and `1`
 * `is_absolute_value`: a type class stating that `f : β → α` satisfies the axioms of an abs val
-/

/-- `absolute_value R S` is the type of absolute values on `R` mapping to `S`:
the maps that preserve `*`, are nonnegative, positive definite and satisfy the triangle equality. -/
structure absolute_value (R S : Type*) [semiring R] [ordered_semiring S]
  extends R →ₙ* S :=
(nonneg' : ∀ x, 0 ≤ to_fun x)
(eq_zero' : ∀ x, to_fun x = 0 ↔ x = 0)
(add_le' : ∀ x y, to_fun (x + y) ≤ to_fun x + to_fun y)

namespace absolute_value

attribute [nolint doc_blame] absolute_value.to_mul_hom

initialize_simps_projections absolute_value (to_mul_hom_to_fun → apply)

section ordered_semiring

variables {R S : Type*} [semiring R] [ordered_semiring S] (abv : absolute_value R S)

instance : has_coe_to_fun (absolute_value R S) (λ f, R → S) := ⟨λ f, f.to_fun⟩

@[simp] lemma coe_to_mul_hom : ⇑abv.to_mul_hom = abv := rfl

protected theorem nonneg (x : R) : 0 ≤ abv x := abv.nonneg' x
@[simp] protected theorem eq_zero {x : R} : abv x = 0 ↔ x = 0 := abv.eq_zero' x
protected theorem add_le (x y : R) : abv (x + y) ≤ abv x + abv y := abv.add_le' x y
@[simp] protected theorem map_mul (x y : R) : abv (x * y) = abv x * abv y := abv.map_mul' x y

protected theorem pos {x : R} (hx : x ≠ 0) : 0 < abv x :=
lt_of_le_of_ne (abv.nonneg x) (ne.symm $ mt abv.eq_zero.mp hx)

@[simp] protected theorem pos_iff {x : R} : 0 < abv x ↔ x ≠ 0 :=
⟨λ h₁, mt abv.eq_zero.mpr h₁.ne', abv.pos⟩

protected theorem ne_zero {x : R} (hx : x ≠ 0) : abv x ≠ 0 := (abv.pos hx).ne'

@[simp] protected theorem map_zero : abv 0 = 0 := abv.eq_zero.2 rfl

end ordered_semiring

section ordered_ring

variables {R S : Type*} [ring R] [ordered_ring S] (abv : absolute_value R S)

protected lemma sub_le (a b c : R) : abv (a - c) ≤ abv (a - b) + abv (b - c) :=
by simpa [sub_eq_add_neg, add_assoc] using abv.add_le (a - b) (b - c)

protected lemma le_sub (a b : R) : abv a - abv b ≤ abv (a - b) :=
sub_le_iff_le_add.2 $ by simpa using abv.add_le (a - b) b

@[simp] lemma map_sub_eq_zero_iff (a b : R) : abv (a - b) = 0 ↔ a = b :=
abv.eq_zero.trans sub_eq_zero

end ordered_ring

section linear_ordered_ring

variables {R S : Type*} [semiring R] [linear_ordered_ring S] (abv : absolute_value R S)

/-- `absolute_value.abs` is `abs` as a bundled `absolute_value`. -/
@[simps]
protected def abs : absolute_value S S :=
{ to_fun := abs,
  nonneg' := abs_nonneg,
  eq_zero' := λ _, abs_eq_zero,
  add_le' := abs_add,
  map_mul' := abs_mul }

instance : inhabited (absolute_value S S) := ⟨absolute_value.abs⟩

variables [nontrivial R]

@[simp] protected theorem map_one : abv 1 = 1 :=
(mul_right_inj' $ abv.ne_zero one_ne_zero).1 $
by rw [← abv.map_mul, mul_one, mul_one]

/-- Absolute values from a nontrivial `R` to a linear ordered ring preserve `*`, `0` and `1`. -/
def to_monoid_with_zero_hom : R →*₀ S :=
{ to_fun := abv,
  map_zero' := abv.map_zero,
  map_one' := abv.map_one,
  .. abv }

@[simp] lemma coe_to_monoid_with_zero_hom : ⇑abv.to_monoid_with_zero_hom = abv := rfl

/-- Absolute values from a nontrivial `R` to a linear ordered ring preserve `*` and `1`. -/
def to_monoid_hom : monoid_hom R S :=
{ to_fun := abv,
  map_one' := abv.map_one,
  .. abv }

@[simp] lemma coe_to_monoid_hom : ⇑abv.to_monoid_hom = abv := rfl

@[simp] protected lemma map_pow (a : R) (n : ℕ) : abv (a ^ n) = abv a ^ n :=
abv.to_monoid_hom.map_pow a n

end linear_ordered_ring

section linear_ordered_comm_ring

section ring

variables {R S : Type*} [ring R] [linear_ordered_comm_ring S] (abv : absolute_value R S)

@[simp] protected theorem map_neg (a : R) : abv (-a) = abv a :=
begin
  by_cases ha : a = 0, { simp [ha] },
  refine (mul_self_eq_mul_self_iff.mp
    (by rw [← abv.map_mul, neg_mul_neg, abv.map_mul])).resolve_right _,
  exact ((neg_lt_zero.mpr (abv.pos ha)).trans (abv.pos (neg_ne_zero.mpr ha))).ne'
end

protected theorem map_sub (a b : R) : abv (a - b) = abv (b - a) :=
by rw [← neg_sub, abv.map_neg]

lemma abs_abv_sub_le_abv_sub (a b : R) :
  abs (abv a - abv b) ≤ abv (a - b) :=
abs_sub_le_iff.2 ⟨abv.le_sub _ _, by rw abv.map_sub; apply abv.le_sub⟩

end ring

end linear_ordered_comm_ring

section linear_ordered_field

section field

variables {R S : Type*} [division_ring R] [linear_ordered_field S] (abv : absolute_value R S)

@[simp] protected theorem map_inv (a : R) : abv a⁻¹ = (abv a)⁻¹ :=
abv.to_monoid_with_zero_hom.map_inv a

@[simp] protected theorem map_div (a b : R) : abv (a / b) = abv a / abv b :=
abv.to_monoid_with_zero_hom.map_div a b

end field

end linear_ordered_field

end absolute_value

section is_absolute_value

/-- A function `f` is an absolute value if it is nonnegative, zero only at 0, additive, and
multiplicative.

See also the type `absolute_value` which represents a bundled version of absolute values.
-/
class is_absolute_value {S} [ordered_semiring S]
  {R} [semiring R] (f : R → S) : Prop :=
(abv_nonneg [] : ∀ x, 0 ≤ f x)
(abv_eq_zero [] : ∀ {x}, f x = 0 ↔ x = 0)
(abv_add [] : ∀ x y, f (x + y) ≤ f x + f y)
(abv_mul [] : ∀ x y, f (x * y) = f x * f y)

namespace is_absolute_value

section ordered_semiring

variables {S : Type*} [ordered_semiring S]
variables {R : Type*} [semiring R] (abv : R → S) [is_absolute_value abv]

/-- A bundled absolute value is an absolute value. -/
instance absolute_value.is_absolute_value
  (abv : absolute_value R S) : is_absolute_value abv :=
{ abv_nonneg := abv.nonneg,
  abv_eq_zero := λ _, abv.eq_zero,
  abv_add := abv.add_le,
  abv_mul := abv.map_mul }

/-- Convert an unbundled `is_absolute_value` to a bundled `absolute_value`. -/
@[simps]
def to_absolute_value : absolute_value R S :=
{ to_fun := abv,
  add_le' := abv_add abv,
  eq_zero' := λ _, abv_eq_zero abv,
  nonneg' := abv_nonneg abv,
  map_mul' := abv_mul abv }

theorem abv_zero : abv 0 = 0 := (abv_eq_zero abv).2 rfl

theorem abv_pos {a : R} : 0 < abv a ↔ a ≠ 0 :=
by rw [lt_iff_le_and_ne, ne, eq_comm]; simp [abv_eq_zero abv, abv_nonneg abv]

end ordered_semiring

section linear_ordered_ring

variables {S : Type*} [linear_ordered_ring S]
variables {R : Type*} [semiring R] (abv : R → S) [is_absolute_value abv]

instance abs_is_absolute_value {S} [linear_ordered_ring S] :
  is_absolute_value (abs : S → S) :=
{ abv_nonneg  := abs_nonneg,
  abv_eq_zero := λ _, abs_eq_zero,
  abv_add     := abs_add,
  abv_mul     := abs_mul }

end linear_ordered_ring

section linear_ordered_comm_ring

variables {S : Type*} [linear_ordered_comm_ring S]

section semiring
variables {R : Type*} [semiring R] (abv : R → S) [is_absolute_value abv]

theorem abv_one [nontrivial R] : abv 1 = 1 :=
(mul_right_inj' $ mt (abv_eq_zero abv).1 one_ne_zero).1 $
by rw [← abv_mul abv, mul_one, mul_one]

/-- `abv` as a `monoid_with_zero_hom`. -/
def abv_hom [nontrivial R] : R →*₀ S := ⟨abv, abv_zero abv, abv_one abv, abv_mul abv⟩

lemma abv_pow [nontrivial R] (abv : R → S) [is_absolute_value abv]
  (a : R) (n : ℕ) : abv (a ^ n) = abv a ^ n :=
(abv_hom abv).to_monoid_hom.map_pow a n

end semiring

end linear_ordered_comm_ring

section linear_ordered_field
variables {S : Type*} [linear_ordered_field S]

section ring
variables {R : Type*} [ring R] (abv : R → S) [is_absolute_value abv]

theorem abv_neg (a : R) : abv (-a) = abv a :=
by rw [← mul_self_inj_of_nonneg (abv_nonneg abv _) (abv_nonneg abv _),
← abv_mul abv, ← abv_mul abv]; simp

lemma abv_sub_le (a b c : R) : abv (a - c) ≤ abv (a - b) + abv (b - c) :=
by simpa [sub_eq_add_neg, add_assoc] using abv_add abv (a - b) (b - c)

lemma sub_abv_le_abv_sub (a b : R) : abv a - abv b ≤ abv (a - b) :=
sub_le_iff_le_add.2 $ by simpa using abv_add abv (a - b) b

lemma abs_abv_sub_le_abv_sub (a b : R) :
  abs (abv a - abv b) ≤ abv (a - b) :=
abs_sub_le_iff.2 ⟨sub_abv_le_abv_sub abv _ _,
  by rw abv_sub abv; apply sub_abv_le_abv_sub abv⟩
end ring

section field
variables {R : Type*} [division_ring R] (abv : R → S) [is_absolute_value abv]

theorem abv_inv (a : R) : abv a⁻¹ = (abv a)⁻¹ :=
(abv_hom abv).map_inv a

theorem abv_div (a b : R) : abv (a / b) = abv a / abv b :=
(abv_hom abv).map_div a b

end field

end linear_ordered_field

end is_absolute_value

end is_absolute_value


import mynat.add -- + on mynat
import mynat.mul -- * on mynat

/- Tactic : intro

## Summary:

`intro p` will turn a goal `⊢ P → Q` into a hypothesis `p : P`
and goal `⊢ Q`. If `P` and `Q` are sets `intro p` means "let $p$ be an arbitrary element of $P$".
If `P` and `Q` are propositions then `intro p` says "assume $P$ is true".

## Details

If your goal is a function or an implication `⊢ P → Q` then `intro`
will always make progress. `intro p` turns

`⊢ P → Q`

into

```
p : P
⊢ Q
```

The opposite tactic to intro is `revert`; given the situation
just above, `revert p` turns the goal back into `⊢ P → Q`.

There are two points of view with `intro` -- the
function point of view (Function World) and the proposition
point of view (Proposition World).

## Example (functions)

What does it mean to define
a function? Given an arbitrary term of type `P` (or an element
of the set `P` if you think set-theoretically) you need
to come up with a term of type `Q`, so your first step is
to choose `p`, an arbitary element of `P`.

`intro p,` is Lean's way of saying "let $p\in P$ be arbitrary".
The tactic `intro p` changes

```
⊢ P → Q
```

into

```
p : P
⊢ Q
```

So `p` is an arbitrary element of `P` about which nothing is known,
and our task is to come up with an element of `Q` (which can of
course depend on `p`).

## Example (propositions)

If your goal is an implication $P\implies Q$ then Lean writes
this as `⊢ P → Q`, and `intro p,` can be thought of as meaning
"let $p$ be a proof of $P$", or more informally "let's assume that
$P$ is true". The goal changes to `⊢ Q` and the hypothesis `p : P`
appears in the local context.
-/

/-
# Function world.

## Level 2: the `intro` tactic.

Let's make a function. Let's define the function on the natural
numbers which sends a natural number $n$ to $3n+2$. If you delete the
`sorry` you will see that our goal is `mynat → mynat`. A mathematician
might denote this set with some exotic name such as
$\operatorname{Hom}(\mathbb{N},\mathbb{N})$,
but computer scientists use notation `X → Y` to denote the set of
functions from `X` to `Y` and this name definitely has its merits.
In type theory, `X → Y` is a type (the type of all functions from $X$ to $Y$),
and `f : X → Y` means that `f` is a term
of this type, i.e., $f$ is a function from $X$ to $Y$.

To define a function $X\to Y$ we need to choose an arbitrary
element $x\in X$ and then, perhaps using $x$, make an element of $Y$.
The Lean tactic for "let $x\in X$ be arbitrary" is `intro x`.

## Rule of thumb:

If your goal is `P → Q` then `intro p` will make progress.

To solve the goal below, you have to come up with a function from `mynat`
to `mynat`. Start with

`intro n,`

(i.e. "let $n\in\mathbb{N}$ be arbitrary") and note that our
local context now looks like this:

```
n : mynat
⊢ mynat
```

Our job now is to construct a natural number, which is
allowed to depend on $n$. We can do this using `exact` and
writing a formula for the function we want to define. For example
we imported addition and multiplication at the top of this file,
so

`exact 3*n+2,`

will close the goal, ultimately defining the function $f(n)=3n+2$.

-/

/- Definition
We define a function from mynat to mynat.
-/
example : mynat → mynat :=
begin [nat_num_game]
  intro n,
  exact 3*n+2,

end


/-
Copyright (c) 2021 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser, Kevin Buzzard, Jujian Zhang
-/

import algebra.algebra.operations
import algebra.algebra.subalgebra
import algebra.direct_sum.algebra

/-!
# Internally graded rings and algebras

This module provides `gsemiring` and `gcomm_semiring` instances for a collection of subobjects `A`
when a `set_like.graded_monoid` instance is available:

* on `add_submonoid R`s: `add_submonoid.gsemiring`, `add_submonoid.gcomm_semiring`.
* on `add_subgroup R`s: `add_subgroup.gsemiring`, `add_subgroup.gcomm_semiring`.
* on `submodule S R`s: `submodule.gsemiring`, `submodule.gcomm_semiring`.

With these instances in place, it provides the bundled canonical maps out of a direct sum of
subobjects into their carrier type:

* `direct_sum.add_submonoid_coe_ring_hom` (a `ring_hom` version of `direct_sum.add_submonoid_coe`)
* `direct_sum.add_subgroup_coe_ring_hom` (a `ring_hom` version of `direct_sum.add_subgroup_coe`)
* `direct_sum.submodule_coe_alg_hom` (an `alg_hom` version of `direct_sum.submodule_coe`)

Strictly the definitions in this file are not sufficient to fully define an "internal" direct sum;
to represent this case, `(h : direct_sum.submodule_is_internal A) [set_like.graded_monoid A]` is
needed. In the future there will likely be a data-carrying, constructive, typeclass version of
`direct_sum.submodule_is_internal` for providing an explicit decomposition function.

When `complete_lattice.independent (set.range A)` (a weaker condition than
`direct_sum.submodule_is_internal A`), these provide a grading of `⨆ i, A i`, and the
mapping `⨁ i, A i →+ ⨆ i, A i` can be obtained as
`direct_sum.to_monoid (λ i, add_submonoid.inclusion $ le_supr A i)`.

## tags

internally graded ring
-/

open_locale direct_sum big_operators

variables {ι : Type*} {S R : Type*}

section direct_sum
variables [decidable_eq ι]

/-! #### From `add_submonoid`s -/

namespace add_submonoid

/-- Build a `gsemiring` instance for a collection of `add_submonoid`s. -/
instance gsemiring [add_monoid ι] [semiring R]
  (A : ι → add_submonoid R) [set_like.graded_monoid A] :
  direct_sum.gsemiring (λ i, A i) :=
{ mul_zero := λ i j _, subtype.ext (mul_zero _),
  zero_mul := λ i j _, subtype.ext (zero_mul _),
  mul_add := λ i j _ _ _, subtype.ext (mul_add _ _ _),
  add_mul := λ i j _ _ _, subtype.ext (add_mul _ _ _),
  ..set_like.gmonoid A }

/-- Build a `gcomm_semiring` instance for a collection of `add_submonoid`s. -/
instance gcomm_semiring [add_comm_monoid ι] [comm_semiring R]
  (A : ι → add_submonoid R) [set_like.graded_monoid A] :
  direct_sum.gcomm_semiring (λ i, A i) :=
{ ..set_like.gcomm_monoid A,
  ..add_submonoid.gsemiring A, }

end add_submonoid

/-- The canonical ring isomorphism between `⨁ i, A i` and `R`-/
def direct_sum.submonoid_coe_ring_hom [add_monoid ι] [semiring R]
  (A : ι → add_submonoid R) [h : set_like.graded_monoid A] : (⨁ i, A i) →+* R :=
direct_sum.to_semiring (λ i, (A i).subtype) rfl (λ _ _ _ _, rfl)

/-- The canonical ring isomorphism between `⨁ i, A i` and `R`-/
@[simp] lemma direct_sum.submonoid_coe_ring_hom_of [add_monoid ι] [semiring R]
  (A : ι → add_submonoid R) [h : set_like.graded_monoid A] (i : ι) (x : A i) :
  direct_sum.submonoid_coe_ring_hom A (direct_sum.of (λ i, A i) i x) = x :=
direct_sum.to_semiring_of _ _ _ _ _

lemma direct_sum.coe_mul_apply_add_submonoid [add_monoid ι] [semiring R]
  (A : ι → add_submonoid R) [set_like.graded_monoid A]
  [Π (i : ι) (x : A i), decidable (x ≠ 0)] (r r' : ⨁ i, A i) (i : ι) :
  ((r * r') i : R) =
    ∑ ij in finset.filter (λ ij : ι × ι, ij.1 + ij.2 = i) (r.support.product r'.support),
      r ij.1 * r' ij.2 :=
begin
  rw [direct_sum.mul_eq_sum_support_ghas_mul, dfinsupp.finset_sum_apply,
    add_submonoid.coe_finset_sum],
  simp_rw [direct_sum.coe_of_add_submonoid_apply, ←finset.sum_filter, set_like.coe_ghas_mul],
end

/-! #### From `add_subgroup`s -/

namespace add_subgroup

/-- Build a `gsemiring` instance for a collection of `add_subgroup`s. -/
instance gsemiring [add_monoid ι] [ring R]
  (A : ι → add_subgroup R) [h : set_like.graded_monoid A] :
  direct_sum.gsemiring (λ i, A i) :=
have i' : set_like.graded_monoid (λ i, (A i).to_add_submonoid) := {..h},
by exactI add_submonoid.gsemiring (λ i, (A i).to_add_submonoid)

/-- Build a `gcomm_semiring` instance for a collection of `add_subgroup`s. -/
instance gcomm_semiring [add_comm_group ι] [comm_ring R]
  (A : ι → add_subgroup R) [h : set_like.graded_monoid A] :
  direct_sum.gsemiring (λ i, A i) :=
have i' : set_like.graded_monoid (λ i, (A i).to_add_submonoid) := {..h},
by exactI add_submonoid.gsemiring (λ i, (A i).to_add_submonoid)

end add_subgroup

/-- The canonical ring isomorphism between `⨁ i, A i` and `R`. -/
def direct_sum.subgroup_coe_ring_hom [add_monoid ι] [ring R]
  (A : ι → add_subgroup R) [set_like.graded_monoid A] : (⨁ i, A i) →+* R :=
direct_sum.to_semiring (λ i, (A i).subtype) rfl (λ _ _ _ _, rfl)

@[simp] lemma direct_sum.subgroup_coe_ring_hom_of [add_monoid ι] [ring R]
  (A : ι → add_subgroup R) [set_like.graded_monoid A] (i : ι) (x : A i) :
  direct_sum.subgroup_coe_ring_hom A (direct_sum.of (λ i, A i) i x) = x :=
direct_sum.to_semiring_of _ _ _ _ _

lemma direct_sum.coe_mul_apply_add_subgroup [add_monoid ι] [ring R]
  (A : ι → add_subgroup R) [set_like.graded_monoid A] [Π (i : ι) (x : A i), decidable (x ≠ 0)]
  (r r' : ⨁ i, A i) (i : ι) :
  ((r * r') i : R) =
    ∑ ij in finset.filter (λ ij : ι × ι, ij.1 + ij.2 = i) (r.support.product r'.support),
      r ij.1 * r' ij.2 :=
begin
  rw [direct_sum.mul_eq_sum_support_ghas_mul, dfinsupp.finset_sum_apply,
    add_subgroup.coe_finset_sum],
  simp_rw [direct_sum.coe_of_add_subgroup_apply, ←finset.sum_filter, set_like.coe_ghas_mul],
end

/-! #### From `submodules`s -/

namespace submodule

/-- Build a `gsemiring` instance for a collection of `submodule`s. -/
instance gsemiring [add_monoid ι]
  [comm_semiring S] [semiring R] [algebra S R]
  (A : ι → submodule S R) [h : set_like.graded_monoid A] :
  direct_sum.gsemiring (λ i, A i) :=
have i' : set_like.graded_monoid (λ i, (A i).to_add_submonoid) := {..h},
by exactI add_submonoid.gsemiring (λ i, (A i).to_add_submonoid)

/-- Build a `gsemiring` instance for a collection of `submodule`s. -/
instance gcomm_semiring [add_comm_monoid ι]
  [comm_semiring S] [comm_semiring R] [algebra S R]
  (A : ι → submodule S R) [h : set_like.graded_monoid A] :
  direct_sum.gcomm_semiring (λ i, A i) :=
have i' : set_like.graded_monoid (λ i, (A i).to_add_submonoid) := {..h},
by exactI add_submonoid.gcomm_semiring (λ i, (A i).to_add_submonoid)

/-- Build a `galgebra` instance for a collection of `submodule`s. -/
instance galgebra [add_monoid ι]
  [comm_semiring S] [semiring R] [algebra S R]
  (A : ι → submodule S R) [h : set_like.graded_monoid A] :
  direct_sum.galgebra S (λ i, A i) :=
{ to_fun := begin
    refine ((algebra.linear_map S R).cod_restrict (A 0) $ λ r, _).to_add_monoid_hom,
    exact submodule.one_le.mpr set_like.has_graded_one.one_mem (submodule.algebra_map_mem _),
  end,
  map_one := subtype.ext $ by exact (algebra_map S R).map_one,
  map_mul := λ x y, sigma.subtype_ext (add_zero 0).symm $ (algebra_map S R).map_mul _ _,
  commutes := λ r ⟨i, xi⟩,
    sigma.subtype_ext ((zero_add i).trans (add_zero i).symm) $ algebra.commutes _ _,
  smul_def := λ r ⟨i, xi⟩, sigma.subtype_ext (zero_add i).symm $ algebra.smul_def _ _ }

@[simp] lemma set_like.coe_galgebra_to_fun [add_monoid ι]
  [comm_semiring S] [semiring R] [algebra S R]
  (A : ι → submodule S R) [h : set_like.graded_monoid A] (s : S) :
    ↑(@direct_sum.galgebra.to_fun _ S (λ i, A i) _ _ _ _ _ _ _ s) = (algebra_map S R s : R) := rfl

/-- A direct sum of powers of a submodule of an algebra has a multiplicative structure. -/
instance nat_power_graded_monoid
  [comm_semiring S] [semiring R] [algebra S R] (p : submodule S R) :
  set_like.graded_monoid (λ i : ℕ, p ^ i) :=
{ one_mem := by { rw [←one_le, pow_zero], exact le_rfl },
  mul_mem := λ i j p q hp hq, by { rw pow_add, exact submodule.mul_mem_mul hp hq } }

end submodule

/-- The canonical algebra isomorphism between `⨁ i, A i` and `R`. -/
def direct_sum.submodule_coe_alg_hom [add_monoid ι]
  [comm_semiring S] [semiring R] [algebra S R]
  (A : ι → submodule S R) [h : set_like.graded_monoid A] : (⨁ i, A i) →ₐ[S] R :=
direct_sum.to_algebra S _ (λ i, (A i).subtype) rfl (λ _ _ _ _, rfl) (λ _, rfl)

/-- The supremum of submodules that form a graded monoid is a subalgebra, and equal to the range of
`direct_sum.submodule_coe_alg_hom`. -/
lemma submodule.supr_eq_to_submodule_range [add_monoid ι]
  [comm_semiring S] [semiring R] [algebra S R] (A : ι → submodule S R) [set_like.graded_monoid A] :
  (⨆ i, A i) = (direct_sum.submodule_coe_alg_hom A).range.to_submodule :=
(submodule.supr_eq_range_dfinsupp_lsum A).trans $ set_like.coe_injective rfl

@[simp] lemma direct_sum.submodule_coe_alg_hom_of [add_monoid ι]
  [comm_semiring S] [semiring R] [algebra S R]
  (A : ι → submodule S R) [h : set_like.graded_monoid A] (i : ι) (x : A i) :
  direct_sum.submodule_coe_alg_hom A (direct_sum.of (λ i, A i) i x) = x :=
direct_sum.to_semiring_of _ rfl (λ _ _ _ _, rfl) _ _

lemma direct_sum.coe_mul_apply_submodule [add_monoid ι]
  [comm_semiring S] [semiring R] [algebra S R]
  (A : ι → submodule S R) [Π (i : ι) (x : A i), decidable (x ≠ 0)]
  [set_like.graded_monoid A] (r r' : ⨁ i, A i) (i : ι) :
  ((r * r') i : R) =
    ∑ ij in finset.filter (λ ij : ι × ι, ij.1 + ij.2 = i) (r.support.product r'.support),
      r ij.1 * r' ij.2 :=
begin
  rw [direct_sum.mul_eq_sum_support_ghas_mul, dfinsupp.finset_sum_apply, submodule.coe_sum],
  simp_rw [direct_sum.coe_of_submodule_apply, ←finset.sum_filter, set_like.coe_ghas_mul],
end

end direct_sum

section homogeneous_element

lemma set_like.is_homogeneous_zero_submodule [has_zero ι]
  [semiring S] [add_comm_monoid R] [module S R]
  (A : ι → submodule S R) : set_like.is_homogeneous A (0 : R) :=
⟨0, submodule.zero_mem _⟩

lemma set_like.is_homogeneous.smul [comm_semiring S] [semiring R] [algebra S R]
  {A : ι → submodule S R} {s : S}
  {r : R} (hr : set_like.is_homogeneous A r) : set_like.is_homogeneous A (s • r) :=
let ⟨i, hi⟩ := hr in ⟨i, submodule.smul_mem _ _ hi⟩

end homogeneous_element