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

! This file was ported from Lean 3 source module data.nat.cast.basic
! leanprover-community/mathlib commit acebd8d49928f6ed8920e502a6c90674e75bd441
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.CharZero.Defs
import Mathbin.Algebra.GroupWithZero.Commute
import Mathbin.Algebra.Hom.Ring
import Mathbin.Algebra.Order.Group.Abs
import Mathbin.Algebra.Ring.Commute
import Mathbin.Data.Nat.Order.Basic
import Mathbin.Algebra.Group.Opposite

/-!
# Cast of natural numbers (additional theorems)

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

This file proves additional properties about the *canonical* homomorphism from
the natural numbers into an additive monoid with a one (`nat.cast`).

## Main declarations

* `cast_add_monoid_hom`: `cast` bundled as an `add_monoid_hom`.
* `cast_ring_hom`: `cast` bundled as a `ring_hom`.
-/

variable {α β : Type _}

namespace Nat

#print Nat.castAddMonoidHom /-
/-- `coe : ℕ → α` as an `add_monoid_hom`. -/
def castAddMonoidHom (α : Type _) [AddMonoidWithOne α] : ℕ →+ α
    where
  toFun := coe
  map_add' := cast_add
  map_zero' := cast_zero
#align nat.cast_add_monoid_hom Nat.castAddMonoidHom
-/

/- warning: nat.coe_cast_add_monoid_hom -> Nat.coe_castAddMonoidHom is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : AddMonoidWithOne.{u1} α], Eq.{succ u1} ((fun (_x : AddMonoidHom.{0, u1} Nat α (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1))) => Nat -> α) (Nat.castAddMonoidHom.{u1} α _inst_1)) (coeFn.{succ u1, succ u1} (AddMonoidHom.{0, u1} Nat α (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1))) (fun (_x : AddMonoidHom.{0, u1} Nat α (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1))) => Nat -> α) (AddMonoidHom.hasCoeToFun.{0, u1} Nat α (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1))) (Nat.castAddMonoidHom.{u1} α _inst_1)) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α _inst_1)))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : AddMonoidWithOne.{u1} α], Eq.{succ u1} (forall (a : Nat), (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Nat) => α) a) (FunLike.coe.{succ u1, 1, succ u1} (AddMonoidHom.{0, u1} Nat α (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1))) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Nat) => α) _x) (AddHomClass.toFunLike.{u1, 0, u1} (AddMonoidHom.{0, u1} Nat α (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1))) Nat α (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1))) (AddMonoidHomClass.toAddHomClass.{u1, 0, u1} (AddMonoidHom.{0, u1} Nat α (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1))) Nat α (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1)) (AddMonoidHom.addMonoidHomClass.{0, u1} Nat α (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} α (AddMonoidWithOne.toAddMonoid.{u1} α _inst_1))))) (Nat.castAddMonoidHom.{u1} α _inst_1)) (Nat.cast.{u1} α (AddMonoidWithOne.toNatCast.{u1} α _inst_1))
Case conversion may be inaccurate. Consider using '#align nat.coe_cast_add_monoid_hom Nat.coe_castAddMonoidHomₓ'. -/
@[simp]
theorem coe_castAddMonoidHom [AddMonoidWithOne α] : (castAddMonoidHom α : ℕ → α) = coe :=
  rfl
#align nat.coe_cast_add_monoid_hom Nat.coe_castAddMonoidHom

/- warning: nat.cast_mul -> Nat.cast_mul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α] (m : Nat) (n : Nat), Eq.{succ u1} α ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α _inst_1)))))) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) m n)) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1)))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α _inst_1)))))) m) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α _inst_1)))))) n))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α] (m : Nat) (n : Nat), Eq.{succ u1} α (Nat.cast.{u1} α (NonAssocSemiring.toNatCast.{u1} α _inst_1) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) m n)) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocSemiring.toMul.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1))) (Nat.cast.{u1} α (NonAssocSemiring.toNatCast.{u1} α _inst_1) m) (Nat.cast.{u1} α (NonAssocSemiring.toNatCast.{u1} α _inst_1) n))
Case conversion may be inaccurate. Consider using '#align nat.cast_mul Nat.cast_mulₓ'. -/
@[simp, norm_cast]
theorem cast_mul [NonAssocSemiring α] (m n : ℕ) : ((m * n : ℕ) : α) = m * n := by
  induction n <;> simp [mul_succ, mul_add, *]
#align nat.cast_mul Nat.cast_mul

#print Nat.castRingHom /-
/-- `coe : ℕ → α` as a `ring_hom` -/
def castRingHom (α : Type _) [NonAssocSemiring α] : ℕ →+* α :=
  { castAddMonoidHom α with
    toFun := coe
    map_one' := cast_one
    map_mul' := cast_mul }
#align nat.cast_ring_hom Nat.castRingHom
-/

/- warning: nat.coe_cast_ring_hom -> Nat.coe_castRingHom is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α], Eq.{succ u1} ((fun (_x : RingHom.{0, u1} Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1) => Nat -> α) (Nat.castRingHom.{u1} α _inst_1)) (coeFn.{succ u1, succ u1} (RingHom.{0, u1} Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1) (fun (_x : RingHom.{0, u1} Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1) => Nat -> α) (RingHom.hasCoeToFun.{0, u1} Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1) (Nat.castRingHom.{u1} α _inst_1)) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α _inst_1)))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α], Eq.{succ u1} (forall (a : Nat), (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Nat) => α) a) (FunLike.coe.{succ u1, 1, succ u1} (RingHom.{0, u1} Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Nat) => α) _x) (MulHomClass.toFunLike.{u1, 0, u1} (RingHom.{0, u1} Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1) Nat α (NonUnitalNonAssocSemiring.toMul.{0} Nat (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring))) (NonUnitalNonAssocSemiring.toMul.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1)) (NonUnitalRingHomClass.toMulHomClass.{u1, 0, u1} (RingHom.{0, u1} Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1) Nat α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1) (RingHomClass.toNonUnitalRingHomClass.{u1, 0, u1} (RingHom.{0, u1} Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1) Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1 (RingHom.instRingHomClassRingHom.{0, u1} Nat α (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring) _inst_1)))) (Nat.castRingHom.{u1} α _inst_1)) (Nat.cast.{u1} α (NonAssocSemiring.toNatCast.{u1} α _inst_1))
Case conversion may be inaccurate. Consider using '#align nat.coe_cast_ring_hom Nat.coe_castRingHomₓ'. -/
@[simp]
theorem coe_castRingHom [NonAssocSemiring α] : (castRingHom α : ℕ → α) = coe :=
  rfl
#align nat.coe_cast_ring_hom Nat.coe_castRingHom

/- warning: nat.cast_commute -> Nat.cast_commute is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α] (n : Nat) (x : α), Commute.{u1} α (Distrib.toHasMul.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α _inst_1)))))) n) x
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α] (n : Nat) (x : α), Commute.{u1} α (NonUnitalNonAssocSemiring.toMul.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1)) (Nat.cast.{u1} α (NonAssocSemiring.toNatCast.{u1} α _inst_1) n) x
Case conversion may be inaccurate. Consider using '#align nat.cast_commute Nat.cast_commuteₓ'. -/
theorem cast_commute [NonAssocSemiring α] (n : ℕ) (x : α) : Commute (↑n) x :=
  Nat.recOn n (by rw [cast_zero] <;> exact Commute.zero_left x) fun n ihn => by
    rw [cast_succ] <;> exact ihn.add_left (Commute.one_left x)
#align nat.cast_commute Nat.cast_commute

/- warning: nat.cast_comm -> Nat.cast_comm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α] (n : Nat) (x : α), Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1)))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α _inst_1)))))) n) x) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Distrib.toHasMul.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1)))) x ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α _inst_1)))))) n))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α] (n : Nat) (x : α), Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocSemiring.toMul.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1))) (Nat.cast.{u1} α (NonAssocSemiring.toNatCast.{u1} α _inst_1) n) x) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (NonUnitalNonAssocSemiring.toMul.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1))) x (Nat.cast.{u1} α (NonAssocSemiring.toNatCast.{u1} α _inst_1) n))
Case conversion may be inaccurate. Consider using '#align nat.cast_comm Nat.cast_commₓ'. -/
theorem cast_comm [NonAssocSemiring α] (n : ℕ) (x : α) : (n : α) * x = x * n :=
  (cast_commute n x).Eq
#align nat.cast_comm Nat.cast_comm

/- warning: nat.commute_cast -> Nat.commute_cast is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α] (x : α) (n : Nat), Commute.{u1} α (Distrib.toHasMul.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1))) x ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α _inst_1)))))) n)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NonAssocSemiring.{u1} α] (x : α) (n : Nat), Commute.{u1} α (NonUnitalNonAssocSemiring.toMul.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α _inst_1)) x (Nat.cast.{u1} α (NonAssocSemiring.toNatCast.{u1} α _inst_1) n)
Case conversion may be inaccurate. Consider using '#align nat.commute_cast Nat.commute_castₓ'. -/
theorem commute_cast [NonAssocSemiring α] (x : α) (n : ℕ) : Commute x n :=
  (n.cast_commute x).symm
#align nat.commute_cast Nat.commute_cast

section OrderedSemiring

variable [OrderedSemiring α]

#print Nat.mono_cast /-
@[mono]
theorem mono_cast : Monotone (coe : ℕ → α) :=
  monotone_nat_of_le_succ fun n => by
    rw [Nat.cast_succ] <;> exact le_add_of_nonneg_right zero_le_one
#align nat.mono_cast Nat.mono_cast
-/

/- warning: nat.cast_nonneg -> Nat.cast_nonneg is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] (n : Nat), LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))) n)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] (n : Nat), LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))) (Nat.cast.{u1} α (Semiring.toNatCast.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)) n)
Case conversion may be inaccurate. Consider using '#align nat.cast_nonneg Nat.cast_nonnegₓ'. -/
@[simp]
theorem cast_nonneg (n : ℕ) : 0 ≤ (n : α) :=
  @Nat.cast_zero α _ ▸ mono_cast (Nat.zero_le n)
#align nat.cast_nonneg Nat.cast_nonneg

section Nontrivial

variable [Nontrivial α]

/- warning: nat.cast_add_one_pos -> Nat.cast_add_one_pos is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] [_inst_2 : Nontrivial.{u1} α] (n : Nat), LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toHasAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))) n) (OfNat.ofNat.{u1} α 1 (OfNat.mk.{u1} α 1 (One.one.{u1} α (AddMonoidWithOne.toOne.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] [_inst_2 : Nontrivial.{u1} α] (n : Nat), LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))) (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))) (Nat.cast.{u1} α (Semiring.toNatCast.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)) n) (OfNat.ofNat.{u1} α 1 (One.toOfNat1.{u1} α (Semiring.toOne.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))
Case conversion may be inaccurate. Consider using '#align nat.cast_add_one_pos Nat.cast_add_one_posₓ'. -/
theorem cast_add_one_pos (n : ℕ) : 0 < (n : α) + 1 :=
  zero_lt_one.trans_le <| le_add_of_nonneg_left n.cast_nonneg
#align nat.cast_add_one_pos Nat.cast_add_one_pos

/- warning: nat.cast_pos -> Nat.cast_pos is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] [_inst_2 : Nontrivial.{u1} α] {n : Nat}, Iff (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α _inst_1)))) (OfNat.ofNat.{u1} α 0 (OfNat.mk.{u1} α 0 (Zero.zero.{u1} α (MulZeroClass.toHasZero.{u1} α (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)))))))) n)) (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : OrderedSemiring.{u1} α] [_inst_2 : Nontrivial.{u1} α] {n : Nat}, Iff (LT.lt.{u1} α (Preorder.toLT.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α _inst_1))) (OfNat.ofNat.{u1} α 0 (Zero.toOfNat0.{u1} α (MonoidWithZero.toZero.{u1} α (Semiring.toMonoidWithZero.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1))))) (Nat.cast.{u1} α (Semiring.toNatCast.{u1} α (OrderedSemiring.toSemiring.{u1} α _inst_1)) n)) (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n)
Case conversion may be inaccurate. Consider using '#align nat.cast_pos Nat.cast_posₓ'. -/
@[simp]
theorem cast_pos {n : ℕ} : (0 : α) < n ↔ 0 < n := by cases n <;> simp [cast_add_one_pos]
#align nat.cast_pos Nat.cast_pos

end Nontrivial

variable [CharZero α] {m n : ℕ}

#print Nat.strictMono_cast /-
theorem strictMono_cast : StrictMono (coe : ℕ → α) :=
  mono_cast.strictMono_of_injective cast_injective
#align nat.strict_mono_cast Nat.strictMono_cast
-/

#print Nat.castOrderEmbedding /-
/-- `coe : ℕ → α` as an `order_embedding` -/
@[simps (config := { fullyApplied := false })]
def castOrderEmbedding : ℕ ↪o α :=
  OrderEmbedding.ofStrictMono coe Nat.strictMono_cast
#align nat.cast_order_embedding Nat.castOrderEmbedding
-/

#print Nat.cast_le /-
@[simp, norm_cast]
theorem cast_le : (m : α) ≤ n ↔ m ≤ n :=
  strictMono_cast.le_iff_le
#align nat.cast_le Nat.cast_le
-/

#print Nat.cast_lt /-
@[simp, norm_cast, mono]
theorem cast_lt : (m : α) < n ↔ m < n :=
  strictMono_cast.lt_iff_lt
#align nat.cast_lt Nat.cast_lt
-/

#print Nat.one_lt_cast /-
@[simp, norm_cast]
theorem one_lt_cast : 1 < (n : α) ↔ 1 < n := by rw [← cast_one, cast_lt]
#align nat.one_lt_cast Nat.one_lt_cast
-/

#print Nat.one_le_cast /-
@[simp, norm_cast]
theorem one_le_cast : 1 ≤ (n : α) ↔ 1 ≤ n := by rw [← cast_one, cast_le]
#align nat.one_le_cast Nat.one_le_cast
-/

#print Nat.cast_lt_one /-
@[simp, norm_cast]
theorem cast_lt_one : (n : α) < 1 ↔ n = 0 := by
  rw [← cast_one, cast_lt, lt_succ_iff, ← bot_eq_zero, le_bot_iff]
#align nat.cast_lt_one Nat.cast_lt_one
-/

#print Nat.cast_le_one /-
@[simp, norm_cast]
theorem cast_le_one : (n : α) ≤ 1 ↔ n ≤ 1 := by rw [← cast_one, cast_le]
#align nat.cast_le_one Nat.cast_le_one
-/

end OrderedSemiring

/- warning: nat.cast_tsub -> Nat.cast_tsub is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CanonicallyOrderedCommSemiring.{u1} α] [_inst_2 : Sub.{u1} α] [_inst_3 : OrderedSub.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1)))))) (Distrib.toHasAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1))))))) _inst_2] [_inst_4 : ContravariantClass.{u1, u1} α α (HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toHasAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1))))))))) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommMonoid.toPartialOrder.{u1} α (OrderedSemiring.toOrderedAddCommMonoid.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1)))))))] (m : Nat) (n : Nat), Eq.{succ u1} α ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1)))))))))) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) m n)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α _inst_2) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1)))))))))) m) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1)))))))))) n))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CanonicallyOrderedCommSemiring.{u1} α] [_inst_2 : Sub.{u1} α] [_inst_3 : OrderedSub.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1))))) (Distrib.toAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1))))))) _inst_2] [_inst_4 : ContravariantClass.{u1, u1} α α (fun (x._@.Mathlib.Data.Nat.Cast.Basic._hyg.923 : α) (x._@.Mathlib.Data.Nat.Cast.Basic._hyg.925 : α) => HAdd.hAdd.{u1, u1, u1} α α α (instHAdd.{u1} α (Distrib.toAdd.{u1} α (NonUnitalNonAssocSemiring.toDistrib.{u1} α (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} α (Semiring.toNonAssocSemiring.{u1} α (OrderedSemiring.toSemiring.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1)))))))) x._@.Mathlib.Data.Nat.Cast.Basic._hyg.923 x._@.Mathlib.Data.Nat.Cast.Basic._hyg.925) (fun (x._@.Mathlib.Data.Nat.Cast.Basic._hyg.938 : α) (x._@.Mathlib.Data.Nat.Cast.Basic._hyg.940 : α) => LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedSemiring.toPartialOrder.{u1} α (OrderedCommSemiring.toOrderedSemiring.{u1} α (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{u1} α _inst_1))))) x._@.Mathlib.Data.Nat.Cast.Basic._hyg.938 x._@.Mathlib.Data.Nat.Cast.Basic._hyg.940)] (m : Nat) (n : Nat), Eq.{succ u1} α (Nat.cast.{u1} α (CanonicallyOrderedCommSemiring.toNatCast.{u1} α _inst_1) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) m n)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α _inst_2) (Nat.cast.{u1} α (CanonicallyOrderedCommSemiring.toNatCast.{u1} α _inst_1) m) (Nat.cast.{u1} α (CanonicallyOrderedCommSemiring.toNatCast.{u1} α _inst_1) n))
Case conversion may be inaccurate. Consider using '#align nat.cast_tsub Nat.cast_tsubₓ'. -/
/-- A version of `nat.cast_sub` that works for `ℝ≥0` and `ℚ≥0`. Note that this proof doesn't work
for `ℕ∞` and `ℝ≥0∞`, so we use type-specific lemmas for these types. -/
@[simp, norm_cast]
theorem cast_tsub [CanonicallyOrderedCommSemiring α] [Sub α] [OrderedSub α]
    [ContravariantClass α α (· + ·) (· ≤ ·)] (m n : ℕ) : ↑(m - n) = (m - n : α) :=
  by
  cases' le_total m n with h h
  · rw [tsub_eq_zero_of_le h, cast_zero, tsub_eq_zero_of_le]
    exact mono_cast h
  · rcases le_iff_exists_add'.mp h with ⟨m, rfl⟩
    rw [add_tsub_cancel_right, cast_add, add_tsub_cancel_right]
#align nat.cast_tsub Nat.cast_tsub

/- warning: nat.cast_min -> Nat.cast_min is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedSemiring.{u1} α] {a : Nat} {b : Nat}, Eq.{succ u1} α ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))))))))) (LinearOrder.min.{0} Nat Nat.linearOrder a b)) (LinearOrder.min.{u1} α (LinearOrderedAddCommMonoid.toLinearOrder.{u1} α (LinearOrderedSemiring.toLinearOrderedAddCommMonoid.{u1} α _inst_1)) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))))))))) a) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))))))))) b))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedSemiring.{u1} α] {a : Nat} {b : Nat}, Eq.{succ u1} α (Nat.cast.{u1} α (Semiring.toNatCast.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))) (Min.min.{0} Nat instMinNat a b)) (Min.min.{u1} α (LinearOrderedSemiring.toMin.{u1} α _inst_1) (Nat.cast.{u1} α (Semiring.toNatCast.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))) a) (Nat.cast.{u1} α (Semiring.toNatCast.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))) b))
Case conversion may be inaccurate. Consider using '#align nat.cast_min Nat.cast_minₓ'. -/
@[simp, norm_cast]
theorem cast_min [LinearOrderedSemiring α] {a b : ℕ} : (↑(min a b) : α) = min a b :=
  (@mono_cast α _).map_min
#align nat.cast_min Nat.cast_min

/- warning: nat.cast_max -> Nat.cast_max is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedSemiring.{u1} α] {a : Nat} {b : Nat}, Eq.{succ u1} α ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))))))))) (LinearOrder.max.{0} Nat Nat.linearOrder a b)) (LinearOrder.max.{u1} α (LinearOrderedAddCommMonoid.toLinearOrder.{u1} α (LinearOrderedSemiring.toLinearOrderedAddCommMonoid.{u1} α _inst_1)) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))))))))) a) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} α (NonAssocSemiring.toAddCommMonoidWithOne.{u1} α (Semiring.toNonAssocSemiring.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))))))))) b))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedSemiring.{u1} α] {a : Nat} {b : Nat}, Eq.{succ u1} α (Nat.cast.{u1} α (Semiring.toNatCast.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))) (Max.max.{0} Nat Nat.instMaxNat a b)) (Max.max.{u1} α (LinearOrderedSemiring.toMax.{u1} α _inst_1) (Nat.cast.{u1} α (Semiring.toNatCast.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))) a) (Nat.cast.{u1} α (Semiring.toNatCast.{u1} α (StrictOrderedSemiring.toSemiring.{u1} α (LinearOrderedSemiring.toStrictOrderedSemiring.{u1} α _inst_1))) b))
Case conversion may be inaccurate. Consider using '#align nat.cast_max Nat.cast_maxₓ'. -/
@[simp, norm_cast]
theorem cast_max [LinearOrderedSemiring α] {a b : ℕ} : (↑(max a b) : α) = max a b :=
  (@mono_cast α _).map_max
#align nat.cast_max Nat.cast_max

/- warning: nat.abs_cast -> Nat.abs_cast is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedRing.{u1} α] (a : Nat), Eq.{succ u1} α (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (SubNegMonoid.toHasNeg.{u1} α (AddGroup.toSubNegMonoid.{u1} α (AddGroupWithOne.toAddGroup.{u1} α (AddCommGroupWithOne.toAddGroupWithOne.{u1} α (Ring.toAddCommGroupWithOne.{u1} α (StrictOrderedRing.toRing.{u1} α (LinearOrderedRing.toStrictOrderedRing.{u1} α _inst_1))))))) (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (LinearOrder.toLattice.{u1} α (LinearOrderedRing.toLinearOrder.{u1} α _inst_1))))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddGroupWithOne.toAddMonoidWithOne.{u1} α (AddCommGroupWithOne.toAddGroupWithOne.{u1} α (Ring.toAddCommGroupWithOne.{u1} α (StrictOrderedRing.toRing.{u1} α (LinearOrderedRing.toStrictOrderedRing.{u1} α _inst_1))))))))) a)) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat α (HasLiftT.mk.{1, succ u1} Nat α (CoeTCₓ.coe.{1, succ u1} Nat α (Nat.castCoe.{u1} α (AddMonoidWithOne.toNatCast.{u1} α (AddGroupWithOne.toAddMonoidWithOne.{u1} α (AddCommGroupWithOne.toAddGroupWithOne.{u1} α (Ring.toAddCommGroupWithOne.{u1} α (StrictOrderedRing.toRing.{u1} α (LinearOrderedRing.toStrictOrderedRing.{u1} α _inst_1))))))))) a)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrderedRing.{u1} α] (a : Nat), Eq.{succ u1} α (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (Ring.toNeg.{u1} α (StrictOrderedRing.toRing.{u1} α (LinearOrderedRing.toStrictOrderedRing.{u1} α _inst_1))) (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α (LinearOrderedRing.toLinearOrder.{u1} α _inst_1)))))) (Nat.cast.{u1} α (NonAssocRing.toNatCast.{u1} α (Ring.toNonAssocRing.{u1} α (StrictOrderedRing.toRing.{u1} α (LinearOrderedRing.toStrictOrderedRing.{u1} α _inst_1)))) a)) (Nat.cast.{u1} α (NonAssocRing.toNatCast.{u1} α (Ring.toNonAssocRing.{u1} α (StrictOrderedRing.toRing.{u1} α (LinearOrderedRing.toStrictOrderedRing.{u1} α _inst_1)))) a)
Case conversion may be inaccurate. Consider using '#align nat.abs_cast Nat.abs_castₓ'. -/
@[simp, norm_cast]
theorem abs_cast [LinearOrderedRing α] (a : ℕ) : |(a : α)| = a :=
  abs_of_nonneg (cast_nonneg a)
#align nat.abs_cast Nat.abs_cast

#print Nat.coe_nat_dvd /-
theorem coe_nat_dvd [Semiring α] {m n : ℕ} (h : m ∣ n) : (m : α) ∣ (n : α) :=
  map_dvd (Nat.castRingHom α) h
#align nat.coe_nat_dvd Nat.coe_nat_dvd
-/

alias coe_nat_dvd ← _root_.has_dvd.dvd.nat_cast
#align has_dvd.dvd.nat_cast Dvd.Dvd.nat_cast

end Nat

section AddMonoidHomClass

variable {A B F : Type _} [AddMonoidWithOne B]

/- warning: ext_nat' -> ext_nat' is a dubious translation:
lean 3 declaration is
  forall {A : Type.{u1}} {F : Type.{u2}} [_inst_2 : AddMonoid.{u1} A] [_inst_3 : AddMonoidHomClass.{u2, 0, u1} F Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)] (f : F) (g : F), (Eq.{succ u1} A (coeFn.{succ u2, succ u1} F (fun (_x : F) => Nat -> A) (FunLike.hasCoeToFun.{succ u2, 1, succ u1} F Nat (fun (_x : Nat) => A) (AddHomClass.toFunLike.{u2, 0, u1} F Nat A (AddZeroClass.toHasAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddZeroClass.toHasAdd.{u1} A (AddMonoid.toAddZeroClass.{u1} A _inst_2)) (AddMonoidHomClass.toAddHomClass.{u2, 0, u1} F Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2) _inst_3))) f (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (coeFn.{succ u2, succ u1} F (fun (_x : F) => Nat -> A) (FunLike.hasCoeToFun.{succ u2, 1, succ u1} F Nat (fun (_x : Nat) => A) (AddHomClass.toFunLike.{u2, 0, u1} F Nat A (AddZeroClass.toHasAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddZeroClass.toHasAdd.{u1} A (AddMonoid.toAddZeroClass.{u1} A _inst_2)) (AddMonoidHomClass.toAddHomClass.{u2, 0, u1} F Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2) _inst_3))) g (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) -> (Eq.{succ u2} F f g)
but is expected to have type
  forall {A : Type.{u2}} {F : Type.{u1}} [_inst_2 : AddMonoid.{u2} A] [_inst_3 : AddMonoidHomClass.{u1, 0, u2} F Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u2} A _inst_2)] (f : F) (g : F), (Eq.{succ u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Nat) => A) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (FunLike.coe.{succ u1, 1, succ u2} F Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Nat) => A) _x) (AddHomClass.toFunLike.{u1, 0, u2} F Nat A (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddZeroClass.toAdd.{u2} A (AddMonoid.toAddZeroClass.{u2} A _inst_2)) (AddMonoidHomClass.toAddHomClass.{u1, 0, u2} F Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u2} A _inst_2) _inst_3)) f (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (FunLike.coe.{succ u1, 1, succ u2} F Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Nat) => A) _x) (AddHomClass.toFunLike.{u1, 0, u2} F Nat A (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddZeroClass.toAdd.{u2} A (AddMonoid.toAddZeroClass.{u2} A _inst_2)) (AddMonoidHomClass.toAddHomClass.{u1, 0, u2} F Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u2} A _inst_2) _inst_3)) g (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) -> (Eq.{succ u1} F f g)
Case conversion may be inaccurate. Consider using '#align ext_nat' ext_nat'ₓ'. -/
theorem ext_nat' [AddMonoid A] [AddMonoidHomClass F ℕ A] (f g : F) (h : f 1 = g 1) : f = g :=
  FunLike.ext f g <| by
    apply Nat.rec
    · simp only [Nat.zero_eq, map_zero]
    simp (config := { contextual := true }) [Nat.succ_eq_add_one, h]
#align ext_nat' ext_nat'

/- warning: add_monoid_hom.ext_nat -> AddMonoidHom.ext_nat is a dubious translation:
lean 3 declaration is
  forall {A : Type.{u1}} [_inst_2 : AddMonoid.{u1} A] {f : AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)} {g : AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)}, (Eq.{succ u1} A (coeFn.{succ u1, succ u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) (fun (_x : AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) => Nat -> A) (AddMonoidHom.hasCoeToFun.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) f (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (coeFn.{succ u1, succ u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) (fun (_x : AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) => Nat -> A) (AddMonoidHom.hasCoeToFun.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) g (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) -> (Eq.{succ u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) f g)
but is expected to have type
  forall {A : Type.{u1}} [_inst_2 : AddMonoid.{u1} A] {f : AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)} {g : AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)}, (Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Nat) => A) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (FunLike.coe.{succ u1, 1, succ u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Nat) => A) _x) (AddHomClass.toFunLike.{u1, 0, u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) Nat A (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddZeroClass.toAdd.{u1} A (AddMonoid.toAddZeroClass.{u1} A _inst_2)) (AddMonoidHomClass.toAddHomClass.{u1, 0, u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2) (AddMonoidHom.addMonoidHomClass.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)))) f (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (FunLike.coe.{succ u1, 1, succ u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Nat) => A) _x) (AddHomClass.toFunLike.{u1, 0, u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) Nat A (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddZeroClass.toAdd.{u1} A (AddMonoid.toAddZeroClass.{u1} A _inst_2)) (AddMonoidHomClass.toAddHomClass.{u1, 0, u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2) (AddMonoidHom.addMonoidHomClass.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)))) g (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) -> (Eq.{succ u1} (AddMonoidHom.{0, u1} Nat A (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) (AddMonoid.toAddZeroClass.{u1} A _inst_2)) f g)
Case conversion may be inaccurate. Consider using '#align add_monoid_hom.ext_nat AddMonoidHom.ext_natₓ'. -/
@[ext]
theorem AddMonoidHom.ext_nat [AddMonoid A] : ∀ {f g : ℕ →+ A}, ∀ h : f 1 = g 1, f = g :=
  ext_nat'
#align add_monoid_hom.ext_nat AddMonoidHom.ext_nat

variable [AddMonoidWithOne A]

#print eq_natCast' /-
-- these versions are primed so that the `ring_hom_class` versions aren't
theorem eq_natCast' [AddMonoidHomClass F ℕ A] (f : F) (h1 : f 1 = 1) : ∀ n : ℕ, f n = n
  | 0 => by simp
  | n + 1 => by rw [map_add, h1, eq_natCast' n, Nat.cast_add_one]
#align eq_nat_cast' eq_natCast'
-/

#print map_natCast' /-
theorem map_natCast' {A} [AddMonoidWithOne A] [AddMonoidHomClass F A B] (f : F) (h : f 1 = 1) :
    ∀ n : ℕ, f n = n
  | 0 => by simp
  | n + 1 => by
    rw [Nat.cast_add, map_add, Nat.cast_add, map_natCast', Nat.cast_one, h, Nat.cast_one]
#align map_nat_cast' map_natCast'
-/

end AddMonoidHomClass

section MonoidWithZeroHomClass

variable {A F : Type _} [MulZeroOneClass A]

#print ext_nat'' /-
/-- If two `monoid_with_zero_hom`s agree on the positive naturals they are equal. -/
theorem ext_nat'' [MonoidWithZeroHomClass F ℕ A] (f g : F) (h_pos : ∀ {n : ℕ}, 0 < n → f n = g n) :
    f = g := by
  apply FunLike.ext
  rintro (_ | n)
  · simp
  exact h_pos n.succ_pos
#align ext_nat'' ext_nat''
-/

/- warning: monoid_with_zero_hom.ext_nat -> MonoidWithZeroHom.ext_nat is a dubious translation:
lean 3 declaration is
  forall {A : Type.{u1}} [_inst_1 : MulZeroOneClass.{u1} A] {f : MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1} {g : MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1}, (forall {n : Nat}, (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n) -> (Eq.{succ u1} A (coeFn.{succ u1, succ u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) (fun (_x : MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) => Nat -> A) (MonoidWithZeroHom.hasCoeToFun.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) f n) (coeFn.{succ u1, succ u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) (fun (_x : MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) => Nat -> A) (MonoidWithZeroHom.hasCoeToFun.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) g n))) -> (Eq.{succ u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) f g)
but is expected to have type
  forall {A : Type.{u1}} [_inst_1 : MulZeroOneClass.{u1} A] {f : MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1} {g : MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1}, (forall {n : Nat}, (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n) -> (Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Nat) => A) n) (FunLike.coe.{succ u1, 1, succ u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Nat) => A) _x) (MulHomClass.toFunLike.{u1, 0, u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) Nat A (MulOneClass.toMul.{0} Nat (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) (MulOneClass.toMul.{u1} A (MulZeroOneClass.toMulOneClass.{u1} A _inst_1)) (MonoidHomClass.toMulHomClass.{u1, 0, u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) Nat A (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring))) (MulZeroOneClass.toMulOneClass.{u1} A _inst_1) (MonoidWithZeroHomClass.toMonoidHomClass.{u1, 0, u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1 (MonoidWithZeroHom.monoidWithZeroHomClass.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1)))) f n) (FunLike.coe.{succ u1, 1, succ u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Nat) => A) _x) (MulHomClass.toFunLike.{u1, 0, u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) Nat A (MulOneClass.toMul.{0} Nat (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)))) (MulOneClass.toMul.{u1} A (MulZeroOneClass.toMulOneClass.{u1} A _inst_1)) (MonoidHomClass.toMulHomClass.{u1, 0, u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) Nat A (MulZeroOneClass.toMulOneClass.{0} Nat (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring))) (MulZeroOneClass.toMulOneClass.{u1} A _inst_1) (MonoidWithZeroHomClass.toMonoidHomClass.{u1, 0, u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1 (MonoidWithZeroHom.monoidWithZeroHomClass.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1)))) g n))) -> (Eq.{succ u1} (MonoidWithZeroHom.{0, u1} Nat A (NonAssocSemiring.toMulZeroOneClass.{0} Nat (Semiring.toNonAssocSemiring.{0} Nat Nat.semiring)) _inst_1) f g)
Case conversion may be inaccurate. Consider using '#align monoid_with_zero_hom.ext_nat MonoidWithZeroHom.ext_natₓ'. -/
@[ext]
theorem MonoidWithZeroHom.ext_nat : ∀ {f g : ℕ →*₀ A}, (∀ {n : ℕ}, 0 < n → f n = g n) → f = g :=
  ext_nat''
#align monoid_with_zero_hom.ext_nat MonoidWithZeroHom.ext_nat

end MonoidWithZeroHomClass

section RingHomClass

variable {R S F : Type _} [NonAssocSemiring R] [NonAssocSemiring S]

#print eq_natCast /-
@[simp]
theorem eq_natCast [RingHomClass F ℕ R] (f : F) : ∀ n, f n = n :=
  eq_natCast' f <| map_one f
#align eq_nat_cast eq_natCast
-/

/- warning: map_nat_cast -> map_natCast is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} {F : Type.{u3}} [_inst_1 : NonAssocSemiring.{u1} R] [_inst_2 : NonAssocSemiring.{u2} S] [_inst_3 : RingHomClass.{u3, u1, u2} F R S _inst_1 _inst_2] (f : F) (n : Nat), Eq.{succ u2} S (coeFn.{succ u3, max (succ u1) (succ u2)} F (fun (_x : F) => R -> S) (FunLike.hasCoeToFun.{succ u3, succ u1, succ u2} F R (fun (_x : R) => S) (MulHomClass.toFunLike.{u3, u1, u2} F R S (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1))) (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S _inst_2))) (NonUnitalRingHomClass.toMulHomClass.{u3, u1, u2} F R S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S _inst_2) (RingHomClass.toNonUnitalRingHomClass.{u3, u1, u2} F R S _inst_1 _inst_2 _inst_3)))) f ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat R (HasLiftT.mk.{1, succ u1} Nat R (CoeTCₓ.coe.{1, succ u1} Nat R (Nat.castCoe.{u1} R (AddMonoidWithOne.toNatCast.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R _inst_1)))))) n)) ((fun (a : Type) (b : Type.{u2}) [self : HasLiftT.{1, succ u2} a b] => self.0) Nat S (HasLiftT.mk.{1, succ u2} Nat S (CoeTCₓ.coe.{1, succ u2} Nat S (Nat.castCoe.{u2} S (AddMonoidWithOne.toNatCast.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S _inst_2)))))) n)
but is expected to have type
  forall {R : Type.{u2}} {S : Type.{u1}} {F : Type.{u3}} [_inst_1 : NonAssocSemiring.{u2} R] [_inst_2 : NonAssocSemiring.{u1} S] [_inst_3 : RingHomClass.{u3, u2, u1} F R S _inst_1 _inst_2] (f : F) (n : Nat), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) (Nat.cast.{u2} R (NonAssocSemiring.toNatCast.{u2} R _inst_1) n)) (FunLike.coe.{succ u3, succ u2, succ u1} F R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) _x) (MulHomClass.toFunLike.{u3, u2, u1} F R S (NonUnitalNonAssocSemiring.toMul.{u2} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R _inst_1)) (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S _inst_2)) (NonUnitalRingHomClass.toMulHomClass.{u3, u2, u1} F R S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S _inst_2) (RingHomClass.toNonUnitalRingHomClass.{u3, u2, u1} F R S _inst_1 _inst_2 _inst_3))) f (Nat.cast.{u2} R (NonAssocSemiring.toNatCast.{u2} R _inst_1) n)) (Nat.cast.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) (Nat.cast.{u2} R (NonAssocSemiring.toNatCast.{u2} R _inst_1) n)) (NonAssocSemiring.toNatCast.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) (Nat.cast.{u2} R (NonAssocSemiring.toNatCast.{u2} R _inst_1) n)) _inst_2) n)
Case conversion may be inaccurate. Consider using '#align map_nat_cast map_natCastₓ'. -/
@[simp]
theorem map_natCast [RingHomClass F R S] (f : F) : ∀ n : ℕ, f (n : R) = n :=
  map_natCast' f <| map_one f
#align map_nat_cast map_natCast

#print ext_nat /-
theorem ext_nat [RingHomClass F ℕ R] (f g : F) : f = g :=
  ext_nat' f g <| by simp only [map_one]
#align ext_nat ext_nat
-/

/- warning: ne_zero.nat_of_injective -> NeZero.nat_of_injective is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} {F : Type.{u3}} [_inst_1 : NonAssocSemiring.{u1} R] [_inst_2 : NonAssocSemiring.{u2} S] {n : Nat} [h : NeZero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat R (HasLiftT.mk.{1, succ u1} Nat R (CoeTCₓ.coe.{1, succ u1} Nat R (Nat.castCoe.{u1} R (AddMonoidWithOne.toNatCast.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R _inst_1)))))) n)] [_inst_3 : RingHomClass.{u3, u1, u2} F R S _inst_1 _inst_2] {f : F}, (Function.Injective.{succ u1, succ u2} R S (coeFn.{succ u3, max (succ u1) (succ u2)} F (fun (_x : F) => R -> S) (FunLike.hasCoeToFun.{succ u3, succ u1, succ u2} F R (fun (_x : R) => S) (MulHomClass.toFunLike.{u3, u1, u2} F R S (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1))) (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S _inst_2))) (NonUnitalRingHomClass.toMulHomClass.{u3, u1, u2} F R S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S _inst_2) (RingHomClass.toNonUnitalRingHomClass.{u3, u1, u2} F R S _inst_1 _inst_2 _inst_3)))) f)) -> (NeZero.{u2} S (MulZeroClass.toHasZero.{u2} S (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S _inst_2))) ((fun (a : Type) (b : Type.{u2}) [self : HasLiftT.{1, succ u2} a b] => self.0) Nat S (HasLiftT.mk.{1, succ u2} Nat S (CoeTCₓ.coe.{1, succ u2} Nat S (Nat.castCoe.{u2} S (AddMonoidWithOne.toNatCast.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S _inst_2)))))) n))
but is expected to have type
  forall {R : Type.{u3}} {S : Type.{u1}} {F : Type.{u2}} [_inst_1 : NonAssocSemiring.{u3} R] [_inst_2 : NonAssocSemiring.{u1} S] {n : Nat} [h : NeZero.{u3} R (MulZeroOneClass.toZero.{u3} R (NonAssocSemiring.toMulZeroOneClass.{u3} R _inst_1)) (Nat.cast.{u3} R (NonAssocSemiring.toNatCast.{u3} R _inst_1) n)] [_inst_3 : RingHomClass.{u2, u3, u1} F R S _inst_1 _inst_2] {f : F}, (Function.Injective.{succ u3, succ u1} R S (FunLike.coe.{succ u2, succ u3, succ u1} F R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) _x) (MulHomClass.toFunLike.{u2, u3, u1} F R S (NonUnitalNonAssocSemiring.toMul.{u3} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} R _inst_1)) (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S _inst_2)) (NonUnitalRingHomClass.toMulHomClass.{u2, u3, u1} F R S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S _inst_2) (RingHomClass.toNonUnitalRingHomClass.{u2, u3, u1} F R S _inst_1 _inst_2 _inst_3))) f)) -> (NeZero.{u1} S (MulZeroOneClass.toZero.{u1} S (NonAssocSemiring.toMulZeroOneClass.{u1} S _inst_2)) (Nat.cast.{u1} S (NonAssocSemiring.toNatCast.{u1} S _inst_2) n))
Case conversion may be inaccurate. Consider using '#align ne_zero.nat_of_injective NeZero.nat_of_injectiveₓ'. -/
theorem NeZero.nat_of_injective {n : ℕ} [h : NeZero (n : R)] [RingHomClass F R S] {f : F}
    (hf : Function.Injective f) : NeZero (n : S) :=
  ⟨fun h => NeZero.natCast_ne n R <| hf <| by simpa only [map_natCast, map_zero] ⟩
#align ne_zero.nat_of_injective NeZero.nat_of_injective

/- warning: ne_zero.nat_of_ne_zero -> NeZero.nat_of_neZero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} [_inst_3 : Semiring.{u1} R] [_inst_4 : Semiring.{u2} S] {F : Type.{u3}} [_inst_5 : RingHomClass.{u3, u1, u2} F R S (Semiring.toNonAssocSemiring.{u1} R _inst_3) (Semiring.toNonAssocSemiring.{u2} S _inst_4)], F -> (forall {n : Nat} [hn : NeZero.{u2} S (MulZeroClass.toHasZero.{u2} S (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S _inst_4)))) ((fun (a : Type) (b : Type.{u2}) [self : HasLiftT.{1, succ u2} a b] => self.0) Nat S (HasLiftT.mk.{1, succ u2} Nat S (CoeTCₓ.coe.{1, succ u2} Nat S (Nat.castCoe.{u2} S (AddMonoidWithOne.toNatCast.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S (Semiring.toNonAssocSemiring.{u2} S _inst_4))))))) n)], NeZero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_3)))) ((fun (a : Type) (b : Type.{u1}) [self : HasLiftT.{1, succ u1} a b] => self.0) Nat R (HasLiftT.mk.{1, succ u1} Nat R (CoeTCₓ.coe.{1, succ u1} Nat R (Nat.castCoe.{u1} R (AddMonoidWithOne.toNatCast.{u1} R (AddCommMonoidWithOne.toAddMonoidWithOne.{u1} R (NonAssocSemiring.toAddCommMonoidWithOne.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_3))))))) n))
but is expected to have type
  forall {R : Type.{u3}} {S : Type.{u2}} [_inst_3 : Semiring.{u3} R] [_inst_4 : Semiring.{u2} S] {F : Type.{u1}} [_inst_5 : RingHomClass.{u1, u3, u2} F R S (Semiring.toNonAssocSemiring.{u3} R _inst_3) (Semiring.toNonAssocSemiring.{u2} S _inst_4)], F -> (forall {n : Nat} [hn : NeZero.{u2} S (MonoidWithZero.toZero.{u2} S (Semiring.toMonoidWithZero.{u2} S _inst_4)) (Nat.cast.{u2} S (Semiring.toNatCast.{u2} S _inst_4) n)], NeZero.{u3} R (MonoidWithZero.toZero.{u3} R (Semiring.toMonoidWithZero.{u3} R _inst_3)) (Nat.cast.{u3} R (Semiring.toNatCast.{u3} R _inst_3) n))
Case conversion may be inaccurate. Consider using '#align ne_zero.nat_of_ne_zero NeZero.nat_of_neZeroₓ'. -/
theorem NeZero.nat_of_neZero {R S} [Semiring R] [Semiring S] {F} [RingHomClass F R S] (f : F)
    {n : ℕ} [hn : NeZero (n : S)] : NeZero (n : R) :=
  by
  apply NeZero.of_map f
  simp only [map_natCast, hn]
#align ne_zero.nat_of_ne_zero NeZero.nat_of_neZero

end RingHomClass

namespace RingHom

#print RingHom.eq_natCast' /-
/-- This is primed to match `eq_int_cast'`. -/
theorem eq_natCast' {R} [NonAssocSemiring R] (f : ℕ →+* R) : f = Nat.castRingHom R :=
  RingHom.ext <| eq_natCast f
#align ring_hom.eq_nat_cast' RingHom.eq_natCast'
-/

end RingHom

#print Nat.cast_id /-
@[simp, norm_cast]
theorem Nat.cast_id (n : ℕ) : ↑n = n :=
  rfl
#align nat.cast_id Nat.cast_id
-/

#print Nat.castRingHom_nat /-
@[simp]
theorem Nat.castRingHom_nat : Nat.castRingHom ℕ = RingHom.id ℕ :=
  rfl
#align nat.cast_ring_hom_nat Nat.castRingHom_nat
-/

#print Nat.uniqueRingHom /-
-- I don't think `ring_hom_class` is good here, because of the `subsingleton` TC slowness
instance Nat.uniqueRingHom {R : Type _} [NonAssocSemiring R] : Unique (ℕ →+* R)
    where
  default := Nat.castRingHom R
  uniq := RingHom.eq_natCast'
#align nat.unique_ring_hom Nat.uniqueRingHom
-/

namespace Pi

variable {π : α → Type _} [∀ a, NatCast (π a)]

instance : NatCast (∀ a, π a) := by refine_struct { .. } <;> pi_instance_derive_field

#print Pi.nat_apply /-
theorem nat_apply (n : ℕ) (a : α) : (n : ∀ a, π a) a = n :=
  rfl
#align pi.nat_apply Pi.nat_apply
-/

/- warning: pi.coe_nat -> Pi.coe_nat is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {π : α -> Type.{u2}} [_inst_1 : forall (a : α), NatCast.{u2} (π a)] (n : Nat), Eq.{max (succ u1) (succ u2)} (forall (a : α), π a) ((fun (a : Type) (b : Sort.{max (succ u1) (succ u2)}) [self : HasLiftT.{1, max (succ u1) (succ u2)} a b] => self.0) Nat (forall (a : α), π a) (HasLiftT.mk.{1, max (succ u1) (succ u2)} Nat (forall (a : α), π a) (CoeTCₓ.coe.{1, max (succ u1) (succ u2)} Nat (forall (a : α), π a) (Nat.castCoe.{max u1 u2} (forall (a : α), π a) (Pi.hasNatCast.{u1, u2} α (fun (a : α) => π a) (fun (a : α) => _inst_1 a))))) n) (fun (_x : α) => (fun (a : Type) (b : Type.{u2}) [self : HasLiftT.{1, succ u2} a b] => self.0) Nat (π _x) (HasLiftT.mk.{1, succ u2} Nat (π _x) (CoeTCₓ.coe.{1, succ u2} Nat (π _x) (Nat.castCoe.{u2} (π _x) (_inst_1 _x)))) n)
but is expected to have type
  forall {α : Type.{u2}} {π : α -> Type.{u1}} [_inst_1 : forall (a : α), NatCast.{u1} (π a)] (n : Nat), Eq.{max (succ u2) (succ u1)} (forall (a : α), π a) (Nat.cast.{max u2 u1} (forall (a : α), π a) (Pi.natCast.{u2, u1} α (fun (a : α) => π a) (fun (a : α) => _inst_1 a)) n) (fun (_x : α) => Nat.cast.{u1} (π _x) (_inst_1 _x) n)
Case conversion may be inaccurate. Consider using '#align pi.coe_nat Pi.coe_natₓ'. -/
@[simp]
theorem coe_nat (n : ℕ) : (n : ∀ a, π a) = fun _ => n :=
  rfl
#align pi.coe_nat Pi.coe_nat

end Pi

/- warning: sum.elim_nat_cast_nat_cast -> Sum.elim_natCast_natCast is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : NatCast.{u3} γ] (n : Nat), Eq.{max (max (succ u1) (succ u2)) (succ u3)} ((Sum.{u1, u2} α β) -> γ) (Sum.elim.{u1, u2, succ u3} α β γ ((fun (a : Type) (b : Sort.{max (succ u1) (succ u3)}) [self : HasLiftT.{1, max (succ u1) (succ u3)} a b] => self.0) Nat (α -> γ) (HasLiftT.mk.{1, max (succ u1) (succ u3)} Nat (α -> γ) (CoeTCₓ.coe.{1, max (succ u1) (succ u3)} Nat (α -> γ) (Nat.castCoe.{max u1 u3} (α -> γ) (Pi.hasNatCast.{u1, u3} α (fun (ᾰ : α) => γ) (fun (a : α) => _inst_1))))) n) ((fun (a : Type) (b : Sort.{max (succ u2) (succ u3)}) [self : HasLiftT.{1, max (succ u2) (succ u3)} a b] => self.0) Nat (β -> γ) (HasLiftT.mk.{1, max (succ u2) (succ u3)} Nat (β -> γ) (CoeTCₓ.coe.{1, max (succ u2) (succ u3)} Nat (β -> γ) (Nat.castCoe.{max u2 u3} (β -> γ) (Pi.hasNatCast.{u2, u3} β (fun (ᾰ : β) => γ) (fun (a : β) => _inst_1))))) n)) ((fun (a : Type) (b : Sort.{max (max (succ u1) (succ u2)) (succ u3)}) [self : HasLiftT.{1, max (max (succ u1) (succ u2)) (succ u3)} a b] => self.0) Nat ((Sum.{u1, u2} α β) -> γ) (HasLiftT.mk.{1, max (max (succ u1) (succ u2)) (succ u3)} Nat ((Sum.{u1, u2} α β) -> γ) (CoeTCₓ.coe.{1, max (max (succ u1) (succ u2)) (succ u3)} Nat ((Sum.{u1, u2} α β) -> γ) (Nat.castCoe.{max (max u1 u2) u3} ((Sum.{u1, u2} α β) -> γ) (Pi.hasNatCast.{max u1 u2, u3} (Sum.{u1, u2} α β) (fun (ᾰ : Sum.{u1, u2} α β) => γ) (fun (a : Sum.{u1, u2} α β) => _inst_1))))) n)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} [_inst_1 : NatCast.{u1} γ] (n : Nat), Eq.{max (max (succ u3) (succ u2)) (succ u1)} ((Sum.{u3, u2} α β) -> γ) (Sum.elim.{u3, u2, succ u1} α β γ (Nat.cast.{max u3 u1} (α -> γ) (Pi.natCast.{u3, u1} α (fun (a._@.Mathlib.Data.Nat.Cast.Basic._hyg.2266 : α) => γ) (fun (a : α) => _inst_1)) n) (Nat.cast.{max u2 u1} (β -> γ) (Pi.natCast.{u2, u1} β (fun (a._@.Mathlib.Data.Nat.Cast.Basic._hyg.2273 : β) => γ) (fun (a : β) => _inst_1)) n)) (Nat.cast.{max (max u3 u2) u1} ((Sum.{u3, u2} α β) -> γ) (Pi.natCast.{max u3 u2, u1} (Sum.{u3, u2} α β) (fun (a._@.Mathlib.Data.Sum.Basic._hyg.1871 : Sum.{u3, u2} α β) => γ) (fun (a : Sum.{u3, u2} α β) => _inst_1)) n)
Case conversion may be inaccurate. Consider using '#align sum.elim_nat_cast_nat_cast Sum.elim_natCast_natCastₓ'. -/
theorem Sum.elim_natCast_natCast {α β γ : Type _} [NatCast γ] (n : ℕ) :
    Sum.elim (n : α → γ) (n : β → γ) = n :=
  @Sum.elim_lam_const_lam_const α β γ n
#align sum.elim_nat_cast_nat_cast Sum.elim_natCast_natCast

namespace Pi

variable {π : α → Type _} [∀ a, AddMonoidWithOne (π a)]

instance : AddMonoidWithOne (∀ a, π a) := by refine_struct { .. } <;> pi_instance_derive_field

end Pi

/-! ### Order dual -/

open OrderDual

instance [h : NatCast α] : NatCast αᵒᵈ :=
  h

instance [h : AddMonoidWithOne α] : AddMonoidWithOne αᵒᵈ :=
  h

instance [h : AddCommMonoidWithOne α] : AddCommMonoidWithOne αᵒᵈ :=
  h

#print toDual_natCast /-
@[simp]
theorem toDual_natCast [NatCast α] (n : ℕ) : toDual (n : α) = n :=
  rfl
#align to_dual_nat_cast toDual_natCast
-/

#print ofDual_natCast /-
@[simp]
theorem ofDual_natCast [NatCast α] (n : ℕ) : (ofDual n : α) = n :=
  rfl
#align of_dual_nat_cast ofDual_natCast
-/

/-! ### Lexicographic order -/

instance [h : NatCast α] : NatCast (Lex α) :=
  h

instance [h : AddMonoidWithOne α] : AddMonoidWithOne (Lex α) :=
  h

instance [h : AddCommMonoidWithOne α] : AddCommMonoidWithOne (Lex α) :=
  h

#print toLex_natCast /-
@[simp]
theorem toLex_natCast [NatCast α] (n : ℕ) : toLex (n : α) = n :=
  rfl
#align to_lex_nat_cast toLex_natCast
-/

#print ofLex_natCast /-
@[simp]
theorem ofLex_natCast [NatCast α] (n : ℕ) : (ofLex n : α) = n :=
  rfl
#align of_lex_nat_cast ofLex_natCast
-/


/-
Copyright (c) 2022 Joël Riou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joël Riou
-/

import category_theory.lifting_properties.basic
import category_theory.adjunction.basic

/-!

# Lifting properties and adjunction

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

In this file, we obtain `adjunction.has_lifting_property_iff`, which states
that when we have an adjunction `adj : G ⊣ F` between two functors `G : C ⥤ D`
and `F : D ⥤ C`, then a morphism of the form `G.map i` has the left lifting
property in `D` with respect to a morphism `p` if and only the morphism `i`
has the left lifting property in `C` with respect to `F.map p`.

-/

namespace category_theory

open category

variables {C D : Type*} [category C] [category D] {G : C ⥤ D} {F : D ⥤ C}

namespace comm_sq

section
variables {A B : C} {X Y : D} {i : A ⟶ B} {p : X ⟶ Y} {u : G.obj A ⟶ X} {v : G.obj B ⟶ Y}
  (sq : comm_sq u (G.map i) p v) (adj : G ⊣ F)

include sq

/-- When we have an adjunction `G ⊣ F`, any commutative square where the left
map is of the form `G.map i` and the right map is `p` has an "adjoint" commutative
square whose left map is `i` and whose right map is `F.map p`. -/
lemma right_adjoint :
  comm_sq (adj.hom_equiv _ _ u) i (F.map p) (adj.hom_equiv _ _ v) :=
⟨begin
  simp only [adjunction.hom_equiv_unit, assoc, ← F.map_comp, sq.w],
  rw [F.map_comp, adjunction.unit_naturality_assoc],
end⟩

/-- The liftings of a commutative are in bijection with the liftings of its (right)
adjoint square. -/
def right_adjoint_lift_struct_equiv :
  sq.lift_struct ≃ (sq.right_adjoint adj).lift_struct :=
{ to_fun := λ l,
  { l := adj.hom_equiv _ _ l.l,
    fac_left' := by rw [← adj.hom_equiv_naturality_left, l.fac_left],
    fac_right' := by rw [← adjunction.hom_equiv_naturality_right, l.fac_right], },
  inv_fun := λ l,
  { l := (adj.hom_equiv _ _).symm l.l,
    fac_left' := begin
      rw [← adjunction.hom_equiv_naturality_left_symm, l.fac_left],
      apply (adj.hom_equiv _ _).left_inv,
    end,
    fac_right' := begin
      rw [← adjunction.hom_equiv_naturality_right_symm, l.fac_right],
      apply (adj.hom_equiv _ _).left_inv,
    end, },
  left_inv := by tidy,
  right_inv := by tidy, }

/-- A square has a lifting if and only if its (right) adjoint square has a lifting. -/
lemma right_adjoint_has_lift_iff :
  has_lift (sq.right_adjoint adj) ↔ has_lift sq :=
begin
  simp only [has_lift.iff],
  exact equiv.nonempty_congr (sq.right_adjoint_lift_struct_equiv adj).symm,
end

instance [has_lift sq] : has_lift (sq.right_adjoint adj) :=
by { rw right_adjoint_has_lift_iff, apply_instance, }

end

section
variables {A B : C} {X Y : D} {i : A ⟶ B} {p : X ⟶ Y} {u : A ⟶ F.obj X} {v : B ⟶ F.obj Y}
  (sq : comm_sq u i (F.map p) v) (adj : G ⊣ F)

include sq

/-- When we have an adjunction `G ⊣ F`, any commutative square where the left
map is of the form `i` and the right map is `F.map p` has an "adjoint" commutative
square whose left map is `G.map i` and whose right map is `p`. -/
lemma left_adjoint :
  comm_sq ((adj.hom_equiv _ _).symm u) (G.map i) p
    ((adj.hom_equiv _ _).symm v) :=
⟨begin
  simp only [adjunction.hom_equiv_counit, assoc,
    ← G.map_comp_assoc, ← sq.w],
  rw [G.map_comp, assoc, adjunction.counit_naturality],
end⟩

/-- The liftings of a commutative are in bijection with the liftings of its (left)
adjoint square. -/
def left_adjoint_lift_struct_equiv :
  sq.lift_struct ≃ (sq.left_adjoint adj).lift_struct :=
{ to_fun := λ l,
  { l := (adj.hom_equiv _ _).symm l.l,
    fac_left' := by rw [← adj.hom_equiv_naturality_left_symm, l.fac_left],
    fac_right' := by rw [← adj.hom_equiv_naturality_right_symm, l.fac_right], },
  inv_fun := λ l,
  { l := (adj.hom_equiv _ _) l.l,
    fac_left' := begin
      rw [← adj.hom_equiv_naturality_left, l.fac_left],
      apply (adj.hom_equiv _ _).right_inv,
    end,
    fac_right' := begin
      rw [← adj.hom_equiv_naturality_right, l.fac_right],
      apply (adj.hom_equiv _ _).right_inv,
    end, },
  left_inv := by tidy,
  right_inv := by tidy, }

/-- A (left) adjoint square has a lifting if and only if the original square has a lifting. -/
lemma left_adjoint_has_lift_iff :
  has_lift (sq.left_adjoint adj) ↔ has_lift sq :=
begin
  simp only [has_lift.iff],
  exact equiv.nonempty_congr (sq.left_adjoint_lift_struct_equiv adj).symm,
end

instance [has_lift sq] : has_lift (sq.left_adjoint adj) :=
by { rw left_adjoint_has_lift_iff, apply_instance, }

end

end comm_sq

namespace adjunction

lemma has_lifting_property_iff (adj : G ⊣ F) {A B : C} {X Y : D} (i : A ⟶ B) (p : X ⟶ Y) :
  has_lifting_property (G.map i) p ↔ has_lifting_property i (F.map p) :=
begin
  split; introI; constructor; intros f g sq,
  { rw ← sq.left_adjoint_has_lift_iff adj,
    apply_instance, },
  { rw ← sq.right_adjoint_has_lift_iff adj,
    apply_instance, },
end

end adjunction

end category_theory


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

import category_theory.preadditive.default

/-!
# Preadditive structure on functor categories

If `C` and `D` are categories and `D` is preadditive,
then `C ⥤ D` is also preadditive.

-/

open_locale big_operators

namespace category_theory
open category_theory.limits preadditive

variables {C D : Type*} [category C] [category D] [preadditive D]

instance functor_category_preadditive : preadditive (C ⥤ D) :=
{ hom_group := λ F G,
  { add := λ α β,
    { app := λ X, α.app X + β.app X,
      naturality' := by { intros, rw [comp_add, add_comp, α.naturality, β.naturality] } },
    zero := { app := λ X, 0, naturality' := by { intros, rw [zero_comp, comp_zero] } },
    neg := λ α,
    { app := λ X, -α.app X,
      naturality' := by { intros, rw [comp_neg, neg_comp, α.naturality] } },
    sub := λ α β,
    { app := λ X, α.app X - β.app X,
      naturality' := by { intros, rw [comp_sub, sub_comp, α.naturality, β.naturality] } },
    add_assoc := by { intros, ext, apply add_assoc },
    zero_add := by { intros, ext, apply zero_add },
    add_zero := by { intros, ext, apply add_zero },
    sub_eq_add_neg := by { intros, ext, apply sub_eq_add_neg },
    add_left_neg := by { intros, ext, apply add_left_neg },
    add_comm := by { intros, ext, apply add_comm } },
  add_comp' := by { intros, ext, apply add_comp },
  comp_add' := by { intros, ext, apply comp_add } }

namespace nat_trans

variables {F G : C ⥤ D}

/-- Application of a natural transformation at a fixed object,
as group homomorphism -/
@[simps] def app_hom (X : C) : (F ⟶ G) →+ (F.obj X ⟶ G.obj X) :=
{ to_fun := λ α, α.app X,
  map_zero' := rfl,
  map_add' := λ _ _, rfl }

@[simp] lemma app_zero (X : C) : (0 : F ⟶ G).app X = 0 := rfl

@[simp]

@[simp] lemma app_sub (X : C) (α β : F ⟶ G) : (α - β).app X = α.app X - β.app X := rfl

@[simp] lemma app_neg (X : C) (α : F ⟶ G) : (-α).app X = -α.app X := rfl

@[simp] lemma app_nsmul (X : C) (α : F ⟶ G) (n : ℕ) : (n • α).app X = n • α.app X :=
(app_hom X).map_nsmul α n

@[simp] lemma app_zsmul (X : C) (α : F ⟶ G) (n : ℤ) : (n • α).app X = n • α.app X :=
(app_hom X : (F ⟶ G) →+ (F.obj X ⟶ G.obj X)).map_zsmul α n

@[simp] lemma app_sum {ι : Type*} (s : finset ι) (X : C) (α : ι → (F ⟶ G)) :
  (∑ i in s, α i).app X = ∑ i in s, ((α i).app X) :=
by { rw [← app_hom_apply, add_monoid_hom.map_sum], refl }

end nat_trans

end category_theory


/-
Copyright (c) 2018 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Jeremy Avigad, Mario Carneiro, Simon Hudon
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.fin2
import Mathlib.logic.function.basic
import Mathlib.tactic.basic
import Mathlib.PostPort

universes u_1 u u_2 u_3 u_4

namespace Mathlib

/-!

# Tuples of types, and their categorical structure.

## Features

* `typevec n` - n-tuples of types
* `α ⟹ β`    - n-tuples of maps
* `f ⊚ g`     - composition

Also, support functions for operating with n-tuples of types, such as:

* `append1 α β`    - append type `β` to n-tuple `α` to obtain an (n+1)-tuple
* `drop α`         - drops the last element of an (n+1)-tuple
* `last α`         - returns the last element of an (n+1)-tuple
* `append_fun f g` - appends a function g to an n-tuple of functions
* `drop_fun f`     - drops the last function from an n+1-tuple
* `last_fun f`     - returns the last function of a tuple.

Since e.g. `append1 α.drop α.last` is propositionally equal to `α` but not definitionally equal
to it, we need support functions and lemmas to mediate between constructions.
-/

/--
n-tuples of types, as a category
-/
def typevec (n : ℕ) := fin2 n → Type u_1

protected instance typevec.inhabited {n : ℕ} : Inhabited (typevec n) :=
  { default := fun (_x : fin2 n) => PUnit }

namespace typevec

/-- arrow in the category of `typevec` -/
def arrow {n : ℕ} (α : typevec n) (β : typevec n) := (i : fin2 n) → α i → β i

protected instance arrow.inhabited {n : ℕ} (α : typevec n) (β : typevec n)
    [(i : fin2 n) → Inhabited (β i)] : Inhabited (arrow α β) :=
  { default := fun (_x : fin2 n) (_x_1 : α _x) => Inhabited.default }

/-- identity of arrow composition -/
def id {n : ℕ} {α : typevec n} : arrow α α := fun (i : fin2 n) (x : α i) => x

/-- arrow composition in the category of `typevec` -/
def comp {n : ℕ} {α : typevec n} {β : typevec n} {γ : typevec n} (g : arrow β γ) (f : arrow α β) :
    arrow α γ :=
  fun (i : fin2 n) (x : α i) => g i (f i x)

@[simp] theorem id_comp {n : ℕ} {α : typevec n} {β : typevec n} (f : arrow α β) : comp id f = f :=
  rfl

@[simp] theorem comp_id {n : ℕ} {α : typevec n} {β : typevec n} (f : arrow α β) : comp f id = f :=
  rfl

theorem comp_assoc {n : ℕ} {α : typevec n} {β : typevec n} {γ : typevec n} {δ : typevec n}
    (h : arrow γ δ) (g : arrow β γ) (f : arrow α β) : comp (comp h g) f = comp h (comp g f) :=
  rfl

/--
Support for extending a typevec by one element.
-/
def append1 {n : ℕ} (α : typevec n) (β : Type u_1) : typevec (n + 1) := sorry

infixl:67 " ::: " => Mathlib.typevec.append1

/-- retain only a `n-length` prefix of the argument -/
def drop {n : ℕ} (α : typevec (n + 1)) : typevec n := fun (i : fin2 n) => α (fin2.fs i)

/-- take the last value of a `(n+1)-length` vector -/
def last {n : ℕ} (α : typevec (n + 1)) := α fin2.fz

protected instance last.inhabited {n : ℕ} (α : typevec (n + 1)) [Inhabited (α fin2.fz)] :
    Inhabited (last α) :=
  { default := Inhabited.default }

theorem drop_append1 {n : ℕ} {α : typevec n} {β : Type u_1} {i : fin2 n} : drop (α ::: β) i = α i :=
  rfl

@[simp] theorem drop_append1' {n : ℕ} {α : typevec n} {β : Type u_1} : drop (α ::: β) = α :=
  funext fun (x : fin2 n) => drop_append1

theorem last_append1 {n : ℕ} {α : typevec n} {β : Type u_1} : last (α ::: β) = β := rfl

@[simp] theorem append1_drop_last {n : ℕ} (α : typevec (n + 1)) : drop α ::: last α = α := sorry

/-- cases on `(n+1)-length` vectors -/
def append1_cases {n : ℕ} {C : typevec (n + 1) → Sort u}
    (H : (α : typevec n) → (β : Type u_1) → C (α ::: β)) (γ : typevec (n + 1)) : C γ :=
  eq.mpr sorry (H (drop γ) (last γ))

@[simp] theorem append1_cases_append1 {n : ℕ} {C : typevec (n + 1) → Sort u}
    (H : (α : typevec n) → (β : Type u_1) → C (α ::: β)) (α : typevec n) (β : Type u_1) :
    append1_cases H (α ::: β) = H α β :=
  rfl

/-- append an arrow and a function for arbitrary source and target
type vectors -/
def split_fun {n : ℕ} {α : typevec (n + 1)} {α' : typevec (n + 1)} (f : arrow (drop α) (drop α'))
    (g : last α → last α') : arrow α α' :=
  sorry

/-- append an arrow and a function as well as their respective source
and target types / typevecs -/
def append_fun {n : ℕ} {α : typevec n} {α' : typevec n} {β : Type u_1} {β' : Type u_2}
    (f : arrow α α') (g : β → β') : arrow (α ::: β) (α' ::: β') :=
  split_fun f g

infixl:67 " ::: " => Mathlib.typevec.append_fun

/-- split off the prefix of an arrow -/
def drop_fun {n : ℕ} {α : typevec (n + 1)} {β : typevec (n + 1)} (f : arrow α β) :
    arrow (drop α) (drop β) :=
  fun (i : fin2 n) => f (fin2.fs i)

/-- split off the last function of an arrow -/
def last_fun {n : ℕ} {α : typevec (n + 1)} {β : typevec (n + 1)} (f : arrow α β) :
    last α → last β :=
  f fin2.fz

/-- arrow in the category of `0-length` vectors -/
def nil_fun {α : typevec 0} {β : typevec 0} : arrow α β := fun (i : fin2 0) => fin2.elim0 i

theorem eq_of_drop_last_eq {n : ℕ} {α : typevec (n + 1)} {β : typevec (n + 1)} {f : arrow α β}
    {g : arrow α β} (h₀ : drop_fun f = drop_fun g) (h₁ : last_fun f = last_fun g) : f = g :=
  sorry

@[simp] theorem drop_fun_split_fun {n : ℕ} {α : typevec (n + 1)} {α' : typevec (n + 1)}
    (f : arrow (drop α) (drop α')) (g : last α → last α') : drop_fun (split_fun f g) = f :=
  rfl

/-- turn an equality into an arrow -/
def arrow.mp {n : ℕ} {α : typevec n} {β : typevec n} (h : α = β) : arrow α β := sorry

/-- turn an equality into an arrow, with reverse direction -/
def arrow.mpr {n : ℕ} {α : typevec n} {β : typevec n} (h : α = β) : arrow β α := sorry

/-- decompose a vector into its prefix appended with its last element -/
def to_append1_drop_last {n : ℕ} {α : typevec (n + 1)} : arrow α (drop α ::: last α) :=
  arrow.mpr (append1_drop_last α)

/-- stitch two bits of a vector back together -/
def from_append1_drop_last {n : ℕ} {α : typevec (n + 1)} : arrow (drop α ::: last α) α :=
  arrow.mp (append1_drop_last α)

@[simp] theorem last_fun_split_fun {n : ℕ} {α : typevec (n + 1)} {α' : typevec (n + 1)}
    (f : arrow (drop α) (drop α')) (g : last α → last α') : last_fun (split_fun f g) = g :=
  rfl

@[simp] theorem drop_fun_append_fun {n : ℕ} {α : typevec n} {α' : typevec n} {β : Type u_1}
    {β' : Type u_2} (f : arrow α α') (g : β → β') : drop_fun (f ::: g) = f :=
  rfl

@[simp] theorem last_fun_append_fun {n : ℕ} {α : typevec n} {α' : typevec n} {β : Type u_1}
    {β' : Type u_2} (f : arrow α α') (g : β → β') : last_fun (f ::: g) = g :=
  rfl

theorem split_drop_fun_last_fun {n : ℕ} {α : typevec (n + 1)} {α' : typevec (n + 1)}
    (f : arrow α α') : split_fun (drop_fun f) (last_fun f) = f :=
  eq_of_drop_last_eq rfl rfl

theorem split_fun_inj {n : ℕ} {α : typevec (n + 1)} {α' : typevec (n + 1)}
    {f : arrow (drop α) (drop α')} {f' : arrow (drop α) (drop α')} {g : last α → last α'}
    {g' : last α → last α'} (H : split_fun f g = split_fun f' g') : f = f' ∧ g = g' :=
  sorry

theorem append_fun_inj {n : ℕ} {α : typevec n} {α' : typevec n} {β : Type u_1} {β' : Type u_2}
    {f : arrow α α'} {f' : arrow α α'} {g : β → β'} {g' : β → β'} :
    f ::: g = f' ::: g' → f = f' ∧ g = g' :=
  split_fun_inj

theorem split_fun_comp {n : ℕ} {α₀ : typevec (n + 1)} {α₁ : typevec (n + 1)} {α₂ : typevec (n + 1)}
    (f₀ : arrow (drop α₀) (drop α₁)) (f₁ : arrow (drop α₁) (drop α₂)) (g₀ : last α₀ → last α₁)
    (g₁ : last α₁ → last α₂) :
    split_fun (comp f₁ f₀) (g₁ ∘ g₀) = comp (split_fun f₁ g₁) (split_fun f₀ g₀) :=
  eq_of_drop_last_eq rfl rfl

theorem append_fun_comp_split_fun {n : ℕ} {α : typevec n} {γ : typevec n} {β : Type u_1}
    {δ : Type u_2} {ε : typevec (n + 1)} (f₀ : arrow (drop ε) α) (f₁ : arrow α γ) (g₀ : last ε → β)
    (g₁ : β → δ) : comp (f₁ ::: g₁) (split_fun f₀ g₀) = split_fun (comp f₁ f₀) (g₁ ∘ g₀) :=
  Eq.symm (split_fun_comp f₀ f₁ g₀ g₁)

theorem append_fun_comp {n : ℕ} {α₀ : typevec n} {α₁ : typevec n} {α₂ : typevec n} {β₀ : Type u_1}
    {β₁ : Type u_2} {β₂ : Type u_3} (f₀ : arrow α₀ α₁) (f₁ : arrow α₁ α₂) (g₀ : β₀ → β₁)
    (g₁ : β₁ → β₂) : comp f₁ f₀ ::: g₁ ∘ g₀ = comp (f₁ ::: g₁) (f₀ ::: g₀) :=
  eq_of_drop_last_eq rfl rfl

theorem append_fun_comp' {n : ℕ} {α₀ : typevec n} {α₁ : typevec n} {α₂ : typevec n} {β₀ : Type u_1}
    {β₁ : Type u_2} {β₂ : Type u_3} (f₀ : arrow α₀ α₁) (f₁ : arrow α₁ α₂) (g₀ : β₀ → β₁)
    (g₁ : β₁ → β₂) : comp (f₁ ::: g₁) (f₀ ::: g₀) = comp f₁ f₀ ::: g₁ ∘ g₀ :=
  eq_of_drop_last_eq rfl rfl

theorem nil_fun_comp {α₀ : typevec 0} (f₀ : arrow α₀ fin2.elim0) : comp nil_fun f₀ = f₀ :=
  funext fun (x : fin2 0) => fin2.elim0 x

theorem append_fun_comp_id {n : ℕ} {α : typevec n} {β₀ : Type u_1} {β₁ : Type u_1} {β₂ : Type u_1}
    (g₀ : β₀ → β₁) (g₁ : β₁ → β₂) : id ::: g₁ ∘ g₀ = comp (id ::: g₁) (id ::: g₀) :=
  eq_of_drop_last_eq rfl rfl

@[simp] theorem drop_fun_comp {n : ℕ} {α₀ : typevec (n + 1)} {α₁ : typevec (n + 1)}
    {α₂ : typevec (n + 1)} (f₀ : arrow α₀ α₁) (f₁ : arrow α₁ α₂) :
    drop_fun (comp f₁ f₀) = comp (drop_fun f₁) (drop_fun f₀) :=
  rfl

@[simp] theorem last_fun_comp {n : ℕ} {α₀ : typevec (n + 1)} {α₁ : typevec (n + 1)}
    {α₂ : typevec (n + 1)} (f₀ : arrow α₀ α₁) (f₁ : arrow α₁ α₂) :
    last_fun (comp f₁ f₀) = last_fun f₁ ∘ last_fun f₀ :=
  rfl

theorem append_fun_aux {n : ℕ} {α : typevec n} {α' : typevec n} {β : Type u_1} {β' : Type u_2}
    (f : arrow (α ::: β) (α' ::: β')) : drop_fun f ::: last_fun f = f :=
  eq_of_drop_last_eq rfl rfl

theorem append_fun_id_id {n : ℕ} {α : typevec n} {β : Type u_1} : id ::: id = id :=
  eq_of_drop_last_eq rfl rfl

protected instance subsingleton0 : subsingleton (typevec 0) :=
  subsingleton.intro fun (a b : typevec 0) => funext fun (a_1 : fin2 0) => fin2.elim0 a_1

/-- cases distinction for 0-length type vector -/
protected def cases_nil {β : typevec 0 → Sort u_2} (f : β fin2.elim0) (v : typevec 0) : β v :=
  cast sorry f

/-- cases distinction for (n+1)-length type vector -/
protected def cases_cons (n : ℕ) {β : typevec (n + 1) → Sort u_2}
    (f : (t : Type u_1) → (v : typevec n) → β (v ::: t)) (v : typevec (n + 1)) : β v :=
  cast sorry (f (last v) (drop v))

protected theorem cases_nil_append1 {β : typevec 0 → Sort u_2} (f : β fin2.elim0) :
    typevec.cases_nil f fin2.elim0 = f :=
  rfl

protected theorem cases_cons_append1 (n : ℕ) {β : typevec (n + 1) → Sort u_2}
    (f : (t : Type u_1) → (v : typevec n) → β (v ::: t)) (v : typevec n) (α : Type u_1) :
    typevec.cases_cons n f (v ::: α) = f α v :=
  rfl

/-- cases distinction for an arrow in the category of 0-length type vectors -/
def typevec_cases_nil₃ {β : (v : typevec 0) → (v' : typevec 0) → arrow v v' → Sort u_3}
    (f : β fin2.elim0 fin2.elim0 nil_fun) (v : typevec 0) (v' : typevec 0) (fs : arrow v v') :
    β v v' fs :=
  cast sorry f

/-- cases distinction for an arrow in the category of (n+1)-length type vectors -/
def typevec_cases_cons₃ (n : ℕ)
    {β : (v : typevec (n + 1)) → (v' : typevec (n + 1)) → arrow v v' → Sort u_3}
    (F :
      (t : Type u_1) →
        (t' : Type u_2) →
          (f : t → t') →
            (v : typevec n) →
              (v' : typevec n) → (fs : arrow v v') → β (v ::: t) (v' ::: t') (fs ::: f))
    (v : typevec (n + 1)) (v' : typevec (n + 1)) (fs : arrow v v') : β v v' fs :=
  eq.mpr sorry
    (eq.mpr sorry
      fun (fs : arrow (drop v ::: last v) (drop v' ::: last v')) =>
        eq.mpr sorry (F (last v) (last v') (last_fun fs) (drop v) (drop v') (drop_fun fs)))

/-- specialized cases distinction for an arrow in the category of 0-length type vectors -/
def typevec_cases_nil₂ {β : arrow fin2.elim0 fin2.elim0 → Sort u_3} (f : β nil_fun) :
    (f : arrow fin2.elim0 fin2.elim0) → β f :=
  fun (g : arrow fin2.elim0 fin2.elim0) => eq.mpr sorry f

/-- specialized cases distinction for an arrow in the category of (n+1)-length type vectors -/
def typevec_cases_cons₂ (n : ℕ) (t : Type u_1) (t' : Type u_2) (v : typevec n) (v' : typevec n)
    {β : arrow (v ::: t) (v' ::: t') → Sort u_3}
    (F : (f : t → t') → (fs : arrow v v') → β (fs ::: f)) (fs : arrow (v ::: t) (v' ::: t')) :
    β fs :=
  eq.mpr sorry (F (last_fun fs) (drop_fun fs))

theorem typevec_cases_nil₂_append_fun {β : arrow fin2.elim0 fin2.elim0 → Sort u_3} (f : β nil_fun) :
    typevec_cases_nil₂ f nil_fun = f :=
  rfl

theorem typevec_cases_cons₂_append_fun (n : ℕ) (t : Type u_1) (t' : Type u_2) (v : typevec n)
    (v' : typevec n) {β : arrow (v ::: t) (v' ::: t') → Sort u_3}
    (F : (f : t → t') → (fs : arrow v v') → β (fs ::: f)) (f : t → t') (fs : arrow v v') :
    typevec_cases_cons₂ n t t' v v' F (fs ::: f) = F f fs :=
  rfl

/- for lifting predicates and relations -/

/-- `pred_last α p x` predicates `p` of the last element of `x : α.append1 β`. -/
def pred_last {n : ℕ} (α : typevec n) {β : Type u_1} (p : β → Prop) {i : fin2 (n + 1)} :
    append1 α β i → Prop :=
  sorry

/-- `rel_last α r x y` says that `p` the last elements of `x y : α.append1 β` are related by `r` and
all the other elements are equal. -/
def rel_last {n : ℕ} (α : typevec n) {β : Type u_1} {γ : Type u_1} (r : β → γ → Prop)
    {i : fin2 (n + 1)} : append1 α β i → append1 α γ i → Prop :=
  sorry

/-- `repeat n t` is a `n-length` type vector that contains `n` occurences of `t` -/
def repeat (n : ℕ) (t : Type u_1) : typevec n := sorry

/-- `prod α β` is the pointwise product of the components of `α` and `β` -/
def prod {n : ℕ} (α : typevec n) (β : typevec n) : typevec n := sorry

/-- `const x α` is an arrow that ignores its source and constructs a `typevec` that
contains nothing but `x` -/
protected def const {β : Type u_1} (x : β) {n : ℕ} (α : typevec n) : arrow α (repeat n β) := sorry

/-- vector of equality on a product of vectors -/
def repeat_eq {n : ℕ} (α : typevec n) : arrow (prod α α) (repeat n Prop) := sorry

theorem const_append1 {β : Type u_1} {γ : Type u_2} (x : γ) {n : ℕ} (α : typevec n) :
    typevec.const x (α ::: β) = typevec.const x α ::: fun (_x : β) => x :=
  sorry

theorem eq_nil_fun {α : typevec 0} {β : typevec 0} (f : arrow α β) : f = nil_fun := sorry

theorem id_eq_nil_fun {α : typevec 0} : id = nil_fun := sorry

theorem const_nil {β : Type u_1} (x : β) (α : typevec 0) : typevec.const x α = nil_fun := sorry

theorem repeat_eq_append1 {β : Type u_1} {n : ℕ} (α : typevec n) :
    repeat_eq (α ::: β) = split_fun (repeat_eq α) (function.uncurry Eq) :=
  sorry

theorem repeat_eq_nil (α : typevec 0) : repeat_eq α = nil_fun := sorry

/-- predicate on a type vector to constrain only the last object -/
def pred_last' {n : ℕ} (α : typevec n) {β : Type u_1} (p : β → Prop) :
    arrow (α ::: β) (repeat (n + 1) Prop) :=
  split_fun (typevec.const True α) p

/-- predicate on the product of two type vectors to constrain only their last object -/
def rel_last' {n : ℕ} (α : typevec n) {β : Type u_1} (p : β → β → Prop) :
    arrow (prod (α ::: β) (α ::: β)) (repeat (n + 1) Prop) :=
  split_fun (repeat_eq α) (function.uncurry p)

/-- given `F : typevec.{u} (n+1) → Type u`, `curry F : Type u → typevec.{u} → Type u`,
i.e. its first argument can be fed in separately from the rest of the vector of arguments -/
def curry {n : ℕ} (F : typevec (n + 1) → Type u_1) (α : Type u) (β : typevec n) := F (β ::: α)

protected instance curry.inhabited {n : ℕ} (F : typevec (n + 1) → Type u_1) (α : Type u)
    (β : typevec n) [I : Inhabited (F (β ::: α))] : Inhabited (curry F α β) :=
  I

/-- arrow to remove one element of a `repeat` vector -/
def drop_repeat (α : Type u_1) {n : ℕ} : arrow (drop (repeat (Nat.succ n) α)) (repeat n α) := sorry

/-- projection for a repeat vector -/
def of_repeat {α : Type u_1} {n : ℕ} {i : fin2 n} : repeat n α i → α := sorry

theorem const_iff_true {n : ℕ} {α : typevec n} {i : fin2 n} {x : α i} {p : Prop} :
    of_repeat (typevec.const p α i x) ↔ p :=
  sorry

-- variables  {F : typevec.{u} n → Type*} [mvfunctor F]

/-- left projection of a `prod` vector -/
def prod.fst {n : ℕ} {α : typevec n} {β : typevec n} : arrow (prod α β) α := sorry

/-- right projection of a `prod` vector -/
def prod.snd {n : ℕ} {α : typevec n} {β : typevec n} : arrow (prod α β) β := sorry

/-- introduce a product where both components are the same -/
def prod.diag {n : ℕ} {α : typevec n} : arrow α (prod α α) := sorry

/-- constructor for `prod` -/
def prod.mk {n : ℕ} {α : typevec n} {β : typevec n} (i : fin2 n) : α i → β i → prod α β i := sorry

@[simp] theorem prod_fst_mk {n : ℕ} {α : typevec n} {β : typevec n} (i : fin2 n) (a : α i)
    (b : β i) : prod.fst i (prod.mk i a b) = a :=
  sorry

@[simp] theorem prod_snd_mk {n : ℕ} {α : typevec n} {β : typevec n} (i : fin2 n) (a : α i)
    (b : β i) : prod.snd i (prod.mk i a b) = b :=
  sorry

/-- `prod` is functorial -/
protected def prod.map {n : ℕ} {α : typevec n} {α' : typevec n} {β : typevec n} {β' : typevec n} :
    arrow α β → arrow α' β' → arrow (prod α α') (prod β β') :=
  sorry

theorem fst_prod_mk {n : ℕ} {α : typevec n} {α' : typevec n} {β : typevec n} {β' : typevec n}
    (f : arrow α β) (g : arrow α' β') : comp prod.fst (prod.map f g) = comp f prod.fst :=
  sorry

theorem snd_prod_mk {n : ℕ} {α : typevec n} {α' : typevec n} {β : typevec n} {β' : typevec n}
    (f : arrow α β) (g : arrow α' β') : comp prod.snd (prod.map f g) = comp g prod.snd :=
  sorry

theorem fst_diag {n : ℕ} {α : typevec n} : comp prod.fst prod.diag = id := sorry

theorem snd_diag {n : ℕ} {α : typevec n} : comp prod.snd prod.diag = id := sorry

theorem repeat_eq_iff_eq {n : ℕ} {α : typevec n} {i : fin2 n} {x : α i} {y : α i} :
    of_repeat (repeat_eq α i (prod.mk i x y)) ↔ x = y :=
  sorry

/-- given a predicate vector `p` over vector `α`, `subtype_ p` is the type of vectors
that contain an `α` that satisfies `p` -/
def subtype_ {n : ℕ} {α : typevec n} (p : arrow α (repeat n Prop)) : typevec n := sorry

/-- projection on `subtype_` -/
def subtype_val {n : ℕ} {α : typevec n} (p : arrow α (repeat n Prop)) : arrow (subtype_ p) α :=
  sorry

/-- arrow that rearranges the type of `subtype_` to turn a subtype of vector into
a vector of subtypes -/
def to_subtype {n : ℕ} {α : typevec n} (p : arrow α (repeat n Prop)) :
    arrow (fun (i : fin2 n) => Subtype fun (x : α i) => of_repeat (p i x)) (subtype_ p) :=
  sorry

/-- arrow that rearranges the type of `subtype_` to turn a vector of subtypes
into a subtype of vector -/
def of_subtype {n : ℕ} {α : typevec n} (p : arrow α (repeat n Prop)) :
    arrow (subtype_ p) fun (i : fin2 n) => Subtype fun (x : α i) => of_repeat (p i x) :=
  sorry

/-- similar to `to_subtype` adapted to relations (i.e. predicate on product) -/
def to_subtype' {n : ℕ} {α : typevec n} (p : arrow (prod α α) (repeat n Prop)) :
    arrow
        (fun (i : fin2 n) =>
          Subtype fun (x : α i × α i) => of_repeat (p i (prod.mk i (prod.fst x) (prod.snd x))))
        (subtype_ p) :=
  sorry

/-- similar to `of_subtype` adapted to relations (i.e. predicate on product) -/
def of_subtype' {n : ℕ} {α : typevec n} (p : arrow (prod α α) (repeat n Prop)) :
    arrow (subtype_ p)
        fun (i : fin2 n) =>
          Subtype fun (x : α i × α i) => of_repeat (p i (prod.mk i (prod.fst x) (prod.snd x))) :=
  sorry

/-- similar to `diag` but the target vector is a `subtype_`
guaranteeing the equality of the components -/
def diag_sub {n : ℕ} {α : typevec n} : arrow α (subtype_ (repeat_eq α)) := sorry

theorem subtype_val_nil {α : typevec 0} (ps : arrow α (repeat 0 Prop)) : subtype_val ps = nil_fun :=
  sorry

theorem diag_sub_val {n : ℕ} {α : typevec n} :
    comp (subtype_val (repeat_eq α)) diag_sub = prod.diag :=
  sorry

theorem prod_id {n : ℕ} {α : typevec n} {β : typevec n} : prod.map id id = id := sorry

theorem append_prod_append_fun {n : ℕ} {α : typevec n} {α' : typevec n} {β : typevec n}
    {β' : typevec n} {φ : Type u} {φ' : Type u} {ψ : Type u} {ψ' : Type u} {f₀ : arrow α α'}
    {g₀ : arrow β β'} {f₁ : φ → φ'} {g₁ : ψ → ψ'} :
    prod.map f₀ g₀ ::: prod.map f₁ g₁ = prod.map (f₀ ::: f₁) (g₀ ::: g₁) :=
  sorry

@[simp] theorem drop_fun_diag {n : ℕ} {α : typevec (n + 1)} : drop_fun prod.diag = prod.diag :=
  sorry

@[simp] theorem drop_fun_subtype_val {n : ℕ} {α : typevec (n + 1)}
    (p : arrow α (repeat (n + 1) Prop)) : drop_fun (subtype_val p) = subtype_val (drop_fun p) :=
  rfl

@[simp] theorem last_fun_subtype_val {n : ℕ} {α : typevec (n + 1)}
    (p : arrow α (repeat (n + 1) Prop)) : last_fun (subtype_val p) = subtype.val :=
  rfl

@[simp] theorem drop_fun_to_subtype {n : ℕ} {α : typevec (n + 1)}
    (p : arrow α (repeat (n + 1) Prop)) :
    drop_fun (to_subtype p) = to_subtype fun (i : fin2 n) (x : α (fin2.fs i)) => p (fin2.fs i) x :=
  sorry

@[simp] theorem last_fun_to_subtype {n : ℕ} {α : typevec (n + 1)}
    (p : arrow α (repeat (n + 1) Prop)) : last_fun (to_subtype p) = id :=
  sorry

@[simp] theorem drop_fun_of_subtype {n : ℕ} {α : typevec (n + 1)}
    (p : arrow α (repeat (n + 1) Prop)) : drop_fun (of_subtype p) = of_subtype (drop_fun p) :=
  sorry

@[simp] theorem last_fun_of_subtype {n : ℕ} {α : typevec (n + 1)}
    (p : arrow α (repeat (n + 1) Prop)) : last_fun (of_subtype p) = id :=
  sorry

@[simp] theorem drop_fun_rel_last {n : ℕ} {α : typevec n} {β : Type u_1} (R : β → β → Prop) :
    drop_fun (rel_last' α R) = repeat_eq α :=
  rfl

@[simp] theorem drop_fun_prod {n : ℕ} {α : typevec (n + 1)} {α' : typevec (n + 1)}
    {β : typevec (n + 1)} {β' : typevec (n + 1)} (f : arrow α β) (f' : arrow α' β') :
    drop_fun (prod.map f f') = prod.map (drop_fun f) (drop_fun f') :=
  sorry

@[simp] theorem last_fun_prod {n : ℕ} {α : typevec (n + 1)} {α' : typevec (n + 1)}
    {β : typevec (n + 1)} {β' : typevec (n + 1)} (f : arrow α β) (f' : arrow α' β') :
    last_fun (prod.map f f') = prod.map (last_fun f) (last_fun f') :=
  sorry

@[simp] theorem drop_fun_from_append1_drop_last {n : ℕ} {α : typevec (n + 1)} :
    drop_fun from_append1_drop_last = id :=
  rfl

@[simp] theorem last_fun_from_append1_drop_last {n : ℕ} {α : typevec (n + 1)} :
    last_fun from_append1_drop_last = id :=
  rfl

@[simp] theorem drop_fun_id {n : ℕ} {α : typevec (n + 1)} : drop_fun id = id := rfl

@[simp] theorem prod_map_id {n : ℕ} {α : typevec n} {β : typevec n} : prod.map id id = id := sorry

@[simp] theorem subtype_val_diag_sub {n : ℕ} {α : typevec n} :
    comp (subtype_val (repeat_eq α)) diag_sub = prod.diag :=
  sorry

@[simp] theorem to_subtype_of_subtype {n : ℕ} {α : typevec n} (p : arrow α (repeat n Prop)) :
    comp (to_subtype p) (of_subtype p) = id :=
  sorry

@[simp] theorem subtype_val_to_subtype {n : ℕ} {α : typevec n} (p : arrow α (repeat n Prop)) :
    comp (subtype_val p) (to_subtype p) = fun (_x : fin2 n) => subtype.val :=
  sorry

@[simp] theorem to_subtype_of_subtype_assoc {n : ℕ} {α : typevec n} {β : typevec n}
    (p : arrow α (repeat n Prop)) (f : arrow β (subtype_ p)) :
    comp (to_subtype p) (comp (of_subtype fun (i : fin2 n) (x : α i) => p i x) f) = f :=
  sorry

@[simp] theorem to_subtype'_of_subtype' {n : ℕ} {α : typevec n}
    (r : arrow (prod α α) (repeat n Prop)) : comp (to_subtype' r) (of_subtype' r) = id :=
  sorry

theorem subtype_val_to_subtype' {n : ℕ} {α : typevec n} (r : arrow (prod α α) (repeat n Prop)) :
    comp (subtype_val r) (to_subtype' r) =
        fun (i : fin2 n)
          (x :
          Subtype fun (x : α i × α i) => of_repeat (r i (prod.mk i (prod.fst x) (prod.snd x)))) =>
          prod.mk i (prod.fst (subtype.val x)) (prod.snd (subtype.val x)) :=
  sorry

end Mathlib