/-
Copyright (c) 2020 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Myers, Yury Kudryashov

! This file was ported from Lean 3 source module algebra.add_torsor
! leanprover-community/mathlib commit 3e32bc908f617039c74c06ea9a897e30c30803c2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Pointwise.Smul

/-!
# Torsors of additive group actions

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

This file defines torsors of additive group actions.

## Notations

The group elements are referred to as acting on points.  This file
defines the notation `+ᵥ` for adding a group element to a point and
`-ᵥ` for subtracting two points to produce a group element.

## Implementation notes

Affine spaces are the motivating example of torsors of additive group actions. It may be appropriate
to refactor in terms of the general definition of group actions, via `to_additive`, when there is a
use for multiplicative torsors (currently mathlib only develops the theory of group actions for
multiplicative group actions).

## Notations

* `v +ᵥ p` is a notation for `has_vadd.vadd`, the left action of an additive monoid;

* `p₁ -ᵥ p₂` is a notation for `has_vsub.vsub`, difference between two points in an additive torsor
  as an element of the corresponding additive group;

## References

* https://en.wikipedia.org/wiki/Principal_homogeneous_space
* https://en.wikipedia.org/wiki/Affine_space

-/

#print AddTorsor /-
/-- An `add_torsor G P` gives a structure to the nonempty type `P`,
acted on by an `add_group G` with a transitive and free action given
by the `+ᵥ` operation and a corresponding subtraction given by the
`-ᵥ` operation. In the case of a vector space, it is an affine
space. -/
class AddTorsor (G : outParam (Type _)) (P : Type _) [outParam <| AddGroup G] extends AddAction G P,
  VSub G P where
  [Nonempty : Nonempty P]
  vsub_vadd' : ∀ p1 p2 : P, (p1 -ᵥ p2 : G) +ᵥ p2 = p1
  vadd_vsub' : ∀ (g : G) (p : P), g +ᵥ p -ᵥ p = g
#align add_torsor AddTorsor
-/

attribute [instance, nolint dangerous_instance] AddTorsor.nonempty

attribute [nolint dangerous_instance] AddTorsor.toHasVsub

#print addGroupIsAddTorsor /-
/-- An `add_group G` is a torsor for itself. -/
@[nolint instance_priority]
instance addGroupIsAddTorsor (G : Type _) [AddGroup G] : AddTorsor G G
    where
  vsub := Sub.sub
  vsub_vadd' := sub_add_cancel
  vadd_vsub' := add_sub_cancel
#align add_group_is_add_torsor addGroupIsAddTorsor
-/

/- warning: vsub_eq_sub -> vsub_eq_sub is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : AddGroup.{u1} G] (g1 : G) (g2 : G), Eq.{succ u1} G (VSub.vsub.{u1, u1} G G (AddTorsor.toHasVsub.{u1, u1} G G _inst_1 (addGroupIsAddTorsor.{u1} G _inst_1)) g1 g2) (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))) g1 g2)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : AddGroup.{u1} G] (g1 : G) (g2 : G), Eq.{succ u1} G (VSub.vsub.{u1, u1} G G (AddTorsor.toVSub.{u1, u1} G G _inst_1 (addGroupIsAddTorsor.{u1} G _inst_1)) g1 g2) (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))) g1 g2)
Case conversion may be inaccurate. Consider using '#align vsub_eq_sub vsub_eq_subₓ'. -/
/-- Simplify subtraction for a torsor for an `add_group G` over
itself. -/
@[simp]
theorem vsub_eq_sub {G : Type _} [AddGroup G] (g1 g2 : G) : g1 -ᵥ g2 = g1 - g2 :=
  rfl
#align vsub_eq_sub vsub_eq_sub

section General

variable {G : Type _} {P : Type _} [AddGroup G] [T : AddTorsor G P]

include T

#print vsub_vadd /-
/-- Adding the result of subtracting from another point produces that
point. -/
@[simp]
theorem vsub_vadd (p1 p2 : P) : p1 -ᵥ p2 +ᵥ p2 = p1 :=
  AddTorsor.vsub_vadd' p1 p2
#align vsub_vadd vsub_vadd
-/

/- warning: vadd_vsub -> vadd_vsub is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (g : G) (p : P), Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T)) g p) p) g
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (g : G) (p : P), Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) (HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)) (AddTorsor.toAddAction.{u2, u1} G P _inst_1 T))) g p) p) g
Case conversion may be inaccurate. Consider using '#align vadd_vsub vadd_vsubₓ'. -/
/-- Adding a group element then subtracting the original point
produces that group element. -/
@[simp]
theorem vadd_vsub (g : G) (p : P) : g +ᵥ p -ᵥ p = g :=
  AddTorsor.vadd_vsub' g p
#align vadd_vsub vadd_vsub

#print vadd_right_cancel /-
/-- If the same point added to two group elements produces equal
results, those group elements are equal. -/
theorem vadd_right_cancel {g1 g2 : G} (p : P) (h : g1 +ᵥ p = g2 +ᵥ p) : g1 = g2 := by
  rw [← vadd_vsub g1, h, vadd_vsub]
#align vadd_right_cancel vadd_right_cancel
-/

#print vadd_right_cancel_iff /-
@[simp]
theorem vadd_right_cancel_iff {g1 g2 : G} (p : P) : g1 +ᵥ p = g2 +ᵥ p ↔ g1 = g2 :=
  ⟨vadd_right_cancel p, fun h => h ▸ rfl⟩
#align vadd_right_cancel_iff vadd_right_cancel_iff
-/

/- warning: vadd_right_injective -> vadd_right_injective is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (p : P), Function.Injective.{succ u1, succ u2} G P (fun (_x : G) => VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T)) _x p)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (p : P), Function.Injective.{succ u2, succ u1} G P (fun (_x : G) => HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)) (AddTorsor.toAddAction.{u2, u1} G P _inst_1 T))) _x p)
Case conversion may be inaccurate. Consider using '#align vadd_right_injective vadd_right_injectiveₓ'. -/
/-- Adding a group element to the point `p` is an injective
function. -/
theorem vadd_right_injective (p : P) : Function.Injective ((· +ᵥ p) : G → P) := fun g1 g2 =>
  vadd_right_cancel p
#align vadd_right_injective vadd_right_injective

/- warning: vadd_vsub_assoc -> vadd_vsub_assoc is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (g : G) (p1 : P) (p2 : P), Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T)) g p1) p2) (HAdd.hAdd.{u1, u1, u1} G G G (instHAdd.{u1} G (AddZeroClass.toHasAdd.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))))) g (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p2))
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (g : G) (p1 : P) (p2 : P), Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) (HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)) (AddTorsor.toAddAction.{u2, u1} G P _inst_1 T))) g p1) p2) (HAdd.hAdd.{u2, u2, u2} G G G (instHAdd.{u2} G (AddZeroClass.toAdd.{u2} G (AddMonoid.toAddZeroClass.{u2} G (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1))))) g (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p2))
Case conversion may be inaccurate. Consider using '#align vadd_vsub_assoc vadd_vsub_assocₓ'. -/
/-- Adding a group element to a point, then subtracting another point,
produces the same result as subtracting the points then adding the
group element. -/
theorem vadd_vsub_assoc (g : G) (p1 p2 : P) : g +ᵥ p1 -ᵥ p2 = g + (p1 -ᵥ p2) :=
  by
  apply vadd_right_cancel p2
  rw [vsub_vadd, add_vadd, vsub_vadd]
#align vadd_vsub_assoc vadd_vsub_assoc

/- warning: vsub_self -> vsub_self is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (p : P), Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p p) (OfNat.ofNat.{u1} G 0 (OfNat.mk.{u1} G 0 (Zero.zero.{u1} G (AddZeroClass.toHasZero.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)))))))
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (p : P), Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p p) (OfNat.ofNat.{u2} G 0 (Zero.toOfNat0.{u2} G (NegZeroClass.toZero.{u2} G (SubNegZeroMonoid.toNegZeroClass.{u2} G (SubtractionMonoid.toSubNegZeroMonoid.{u2} G (AddGroup.toSubtractionMonoid.{u2} G _inst_1))))))
Case conversion may be inaccurate. Consider using '#align vsub_self vsub_selfₓ'. -/
/-- Subtracting a point from itself produces 0. -/
@[simp]
theorem vsub_self (p : P) : p -ᵥ p = (0 : G) := by
  rw [← zero_add (p -ᵥ p), ← vadd_vsub_assoc, vadd_vsub]
#align vsub_self vsub_self

/- warning: eq_of_vsub_eq_zero -> eq_of_vsub_eq_zero is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] {p1 : P} {p2 : P}, (Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p2) (OfNat.ofNat.{u1} G 0 (OfNat.mk.{u1} G 0 (Zero.zero.{u1} G (AddZeroClass.toHasZero.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)))))))) -> (Eq.{succ u2} P p1 p2)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] {p1 : P} {p2 : P}, (Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p2) (OfNat.ofNat.{u2} G 0 (Zero.toOfNat0.{u2} G (NegZeroClass.toZero.{u2} G (SubNegZeroMonoid.toNegZeroClass.{u2} G (SubtractionMonoid.toSubNegZeroMonoid.{u2} G (AddGroup.toSubtractionMonoid.{u2} G _inst_1))))))) -> (Eq.{succ u1} P p1 p2)
Case conversion may be inaccurate. Consider using '#align eq_of_vsub_eq_zero eq_of_vsub_eq_zeroₓ'. -/
/-- If subtracting two points produces 0, they are equal. -/
theorem eq_of_vsub_eq_zero {p1 p2 : P} (h : p1 -ᵥ p2 = (0 : G)) : p1 = p2 := by
  rw [← vsub_vadd p1 p2, h, zero_vadd]
#align eq_of_vsub_eq_zero eq_of_vsub_eq_zero

/- warning: vsub_eq_zero_iff_eq -> vsub_eq_zero_iff_eq is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] {p1 : P} {p2 : P}, Iff (Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p2) (OfNat.ofNat.{u1} G 0 (OfNat.mk.{u1} G 0 (Zero.zero.{u1} G (AddZeroClass.toHasZero.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)))))))) (Eq.{succ u2} P p1 p2)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] {p1 : P} {p2 : P}, Iff (Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p2) (OfNat.ofNat.{u2} G 0 (Zero.toOfNat0.{u2} G (NegZeroClass.toZero.{u2} G (SubNegZeroMonoid.toNegZeroClass.{u2} G (SubtractionMonoid.toSubNegZeroMonoid.{u2} G (AddGroup.toSubtractionMonoid.{u2} G _inst_1))))))) (Eq.{succ u1} P p1 p2)
Case conversion may be inaccurate. Consider using '#align vsub_eq_zero_iff_eq vsub_eq_zero_iff_eqₓ'. -/
/-- Subtracting two points produces 0 if and only if they are
equal. -/
@[simp]
theorem vsub_eq_zero_iff_eq {p1 p2 : P} : p1 -ᵥ p2 = (0 : G) ↔ p1 = p2 :=
  Iff.intro eq_of_vsub_eq_zero fun h => h ▸ vsub_self _
#align vsub_eq_zero_iff_eq vsub_eq_zero_iff_eq

/- warning: vsub_ne_zero -> vsub_ne_zero is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] {p : P} {q : P}, Iff (Ne.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p q) (OfNat.ofNat.{u1} G 0 (OfNat.mk.{u1} G 0 (Zero.zero.{u1} G (AddZeroClass.toHasZero.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)))))))) (Ne.{succ u2} P p q)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] {p : P} {q : P}, Iff (Ne.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p q) (OfNat.ofNat.{u2} G 0 (Zero.toOfNat0.{u2} G (NegZeroClass.toZero.{u2} G (SubNegZeroMonoid.toNegZeroClass.{u2} G (SubtractionMonoid.toSubNegZeroMonoid.{u2} G (AddGroup.toSubtractionMonoid.{u2} G _inst_1))))))) (Ne.{succ u1} P p q)
Case conversion may be inaccurate. Consider using '#align vsub_ne_zero vsub_ne_zeroₓ'. -/
theorem vsub_ne_zero {p q : P} : p -ᵥ q ≠ (0 : G) ↔ p ≠ q :=
  not_congr vsub_eq_zero_iff_eq
#align vsub_ne_zero vsub_ne_zero

/- warning: vsub_add_vsub_cancel -> vsub_add_vsub_cancel is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (p1 : P) (p2 : P) (p3 : P), Eq.{succ u1} G (HAdd.hAdd.{u1, u1, u1} G G G (instHAdd.{u1} G (AddZeroClass.toHasAdd.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))))) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p2) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p2 p3)) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p3)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (p1 : P) (p2 : P) (p3 : P), Eq.{succ u2} G (HAdd.hAdd.{u2, u2, u2} G G G (instHAdd.{u2} G (AddZeroClass.toAdd.{u2} G (AddMonoid.toAddZeroClass.{u2} G (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1))))) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p2) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p2 p3)) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p3)
Case conversion may be inaccurate. Consider using '#align vsub_add_vsub_cancel vsub_add_vsub_cancelₓ'. -/
/-- Cancellation adding the results of two subtractions. -/
@[simp]
theorem vsub_add_vsub_cancel (p1 p2 p3 : P) : p1 -ᵥ p2 + (p2 -ᵥ p3) = p1 -ᵥ p3 :=
  by
  apply vadd_right_cancel p3
  rw [add_vadd, vsub_vadd, vsub_vadd, vsub_vadd]
#align vsub_add_vsub_cancel vsub_add_vsub_cancel

/- warning: neg_vsub_eq_vsub_rev -> neg_vsub_eq_vsub_rev is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (p1 : P) (p2 : P), Eq.{succ u1} G (Neg.neg.{u1} G (SubNegMonoid.toHasNeg.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p2)) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p2 p1)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (p1 : P) (p2 : P), Eq.{succ u2} G (Neg.neg.{u2} G (NegZeroClass.toNeg.{u2} G (SubNegZeroMonoid.toNegZeroClass.{u2} G (SubtractionMonoid.toSubNegZeroMonoid.{u2} G (AddGroup.toSubtractionMonoid.{u2} G _inst_1)))) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p2)) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p2 p1)
Case conversion may be inaccurate. Consider using '#align neg_vsub_eq_vsub_rev neg_vsub_eq_vsub_revₓ'. -/
/-- Subtracting two points in the reverse order produces the negation
of subtracting them. -/
@[simp]
theorem neg_vsub_eq_vsub_rev (p1 p2 : P) : -(p1 -ᵥ p2) = p2 -ᵥ p1 :=
  by
  refine' neg_eq_of_add_eq_zero_right (vadd_right_cancel p1 _)
  rw [vsub_add_vsub_cancel, vsub_self]
#align neg_vsub_eq_vsub_rev neg_vsub_eq_vsub_rev

/- warning: vadd_vsub_eq_sub_vsub -> vadd_vsub_eq_sub_vsub is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (g : G) (p : P) (q : P), Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T)) g p) q) (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))) g (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) q p))
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (g : G) (p : P) (q : P), Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) (HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)) (AddTorsor.toAddAction.{u2, u1} G P _inst_1 T))) g p) q) (HSub.hSub.{u2, u2, u2} G G G (instHSub.{u2} G (SubNegMonoid.toSub.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1))) g (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) q p))
Case conversion may be inaccurate. Consider using '#align vadd_vsub_eq_sub_vsub vadd_vsub_eq_sub_vsubₓ'. -/
theorem vadd_vsub_eq_sub_vsub (g : G) (p q : P) : g +ᵥ p -ᵥ q = g - (q -ᵥ p) := by
  rw [vadd_vsub_assoc, sub_eq_add_neg, neg_vsub_eq_vsub_rev]
#align vadd_vsub_eq_sub_vsub vadd_vsub_eq_sub_vsub

/- warning: vsub_vadd_eq_vsub_sub -> vsub_vadd_eq_vsub_sub is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (p1 : P) (p2 : P) (g : G), Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T)) g p2)) (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p2) g)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (p1 : P) (p2 : P) (g : G), Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 (HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)) (AddTorsor.toAddAction.{u2, u1} G P _inst_1 T))) g p2)) (HSub.hSub.{u2, u2, u2} G G G (instHSub.{u2} G (SubNegMonoid.toSub.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1))) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p2) g)
Case conversion may be inaccurate. Consider using '#align vsub_vadd_eq_vsub_sub vsub_vadd_eq_vsub_subₓ'. -/
/-- Subtracting the result of adding a group element produces the same result
as subtracting the points and subtracting that group element. -/
theorem vsub_vadd_eq_vsub_sub (p1 p2 : P) (g : G) : p1 -ᵥ (g +ᵥ p2) = p1 -ᵥ p2 - g := by
  rw [← add_right_inj (p2 -ᵥ p1 : G), vsub_add_vsub_cancel, ← neg_vsub_eq_vsub_rev, vadd_vsub, ←
    add_sub_assoc, ← neg_vsub_eq_vsub_rev, neg_add_self, zero_sub]
#align vsub_vadd_eq_vsub_sub vsub_vadd_eq_vsub_sub

/- warning: vsub_sub_vsub_cancel_right -> vsub_sub_vsub_cancel_right is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (p1 : P) (p2 : P) (p3 : P), Eq.{succ u1} G (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p3) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p2 p3)) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p2)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (p1 : P) (p2 : P) (p3 : P), Eq.{succ u2} G (HSub.hSub.{u2, u2, u2} G G G (instHSub.{u2} G (SubNegMonoid.toSub.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1))) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p3) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p2 p3)) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p2)
Case conversion may be inaccurate. Consider using '#align vsub_sub_vsub_cancel_right vsub_sub_vsub_cancel_rightₓ'. -/
/-- Cancellation subtracting the results of two subtractions. -/
@[simp]
theorem vsub_sub_vsub_cancel_right (p1 p2 p3 : P) : p1 -ᵥ p3 - (p2 -ᵥ p3) = p1 -ᵥ p2 := by
  rw [← vsub_vadd_eq_vsub_sub, vsub_vadd]
#align vsub_sub_vsub_cancel_right vsub_sub_vsub_cancel_right

#print eq_vadd_iff_vsub_eq /-
/-- Convert between an equality with adding a group element to a point
and an equality of a subtraction of two points with a group
element. -/
theorem eq_vadd_iff_vsub_eq (p1 : P) (g : G) (p2 : P) : p1 = g +ᵥ p2 ↔ p1 -ᵥ p2 = g :=
  ⟨fun h => h.symm ▸ vadd_vsub _ _, fun h => h ▸ (vsub_vadd _ _).symm⟩
#align eq_vadd_iff_vsub_eq eq_vadd_iff_vsub_eq
-/

/- warning: vadd_eq_vadd_iff_neg_add_eq_vsub -> vadd_eq_vadd_iff_neg_add_eq_vsub is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] {v₁ : G} {v₂ : G} {p₁ : P} {p₂ : P}, Iff (Eq.{succ u2} P (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T)) v₁ p₁) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T)) v₂ p₂)) (Eq.{succ u1} G (HAdd.hAdd.{u1, u1, u1} G G G (instHAdd.{u1} G (AddZeroClass.toHasAdd.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))))) (Neg.neg.{u1} G (SubNegMonoid.toHasNeg.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) v₁) v₂) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p₁ p₂))
but is expected to have type
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] {v₁ : G} {v₂ : G} {p₁ : P} {p₂ : P}, Iff (Eq.{succ u2} P (HVAdd.hVAdd.{u1, u2, u2} G P P (instHVAdd.{u1, u2} G P (AddAction.toVAdd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T))) v₁ p₁) (HVAdd.hVAdd.{u1, u2, u2} G P P (instHVAdd.{u1, u2} G P (AddAction.toVAdd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T))) v₂ p₂)) (Eq.{succ u1} G (HAdd.hAdd.{u1, u1, u1} G G G (instHAdd.{u1} G (AddZeroClass.toAdd.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))))) (Neg.neg.{u1} G (NegZeroClass.toNeg.{u1} G (SubNegZeroMonoid.toNegZeroClass.{u1} G (SubtractionMonoid.toSubNegZeroMonoid.{u1} G (AddGroup.toSubtractionMonoid.{u1} G _inst_1)))) v₁) v₂) (VSub.vsub.{u1, u2} G P (AddTorsor.toVSub.{u1, u2} G P _inst_1 T) p₁ p₂))
Case conversion may be inaccurate. Consider using '#align vadd_eq_vadd_iff_neg_add_eq_vsub vadd_eq_vadd_iff_neg_add_eq_vsubₓ'. -/
theorem vadd_eq_vadd_iff_neg_add_eq_vsub {v₁ v₂ : G} {p₁ p₂ : P} :
    v₁ +ᵥ p₁ = v₂ +ᵥ p₂ ↔ -v₁ + v₂ = p₁ -ᵥ p₂ := by
  rw [eq_vadd_iff_vsub_eq, vadd_vsub_assoc, ← add_right_inj (-v₁), neg_add_cancel_left, eq_comm]
#align vadd_eq_vadd_iff_neg_add_eq_vsub vadd_eq_vadd_iff_neg_add_eq_vsub

namespace Set

open Pointwise

/- warning: set.singleton_vsub_self -> Set.singleton_vsub_self is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (p : P), Eq.{succ u1} (Set.{u1} G) (VSub.vsub.{u1, u2} (Set.{u1} G) (Set.{u2} P) (Set.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T)) (Singleton.singleton.{u2, u2} P (Set.{u2} P) (Set.hasSingleton.{u2} P) p) (Singleton.singleton.{u2, u2} P (Set.{u2} P) (Set.hasSingleton.{u2} P) p)) (Singleton.singleton.{u1, u1} G (Set.{u1} G) (Set.hasSingleton.{u1} G) (OfNat.ofNat.{u1} G 0 (OfNat.mk.{u1} G 0 (Zero.zero.{u1} G (AddZeroClass.toHasZero.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))))))))
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (p : P), Eq.{succ u2} (Set.{u2} G) (VSub.vsub.{u2, u1} (Set.{u2} G) (Set.{u1} P) (Set.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T)) (Singleton.singleton.{u1, u1} P (Set.{u1} P) (Set.instSingletonSet.{u1} P) p) (Singleton.singleton.{u1, u1} P (Set.{u1} P) (Set.instSingletonSet.{u1} P) p)) (Singleton.singleton.{u2, u2} G (Set.{u2} G) (Set.instSingletonSet.{u2} G) (OfNat.ofNat.{u2} G 0 (Zero.toOfNat0.{u2} G (NegZeroClass.toZero.{u2} G (SubNegZeroMonoid.toNegZeroClass.{u2} G (SubtractionMonoid.toSubNegZeroMonoid.{u2} G (AddGroup.toSubtractionMonoid.{u2} G _inst_1)))))))
Case conversion may be inaccurate. Consider using '#align set.singleton_vsub_self Set.singleton_vsub_selfₓ'. -/
@[simp]
theorem singleton_vsub_self (p : P) : ({p} : Set P) -ᵥ {p} = {(0 : G)} := by
  rw [Set.singleton_vsub_singleton, vsub_self]
#align set.singleton_vsub_self Set.singleton_vsub_self

end Set

/- warning: vadd_vsub_vadd_cancel_right -> vadd_vsub_vadd_cancel_right is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] (v₁ : G) (v₂ : G) (p : P), Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T)) v₁ p) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 T)) v₂ p)) (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))) v₁ v₂)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] (v₁ : G) (v₂ : G) (p : P), Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) (HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)) (AddTorsor.toAddAction.{u2, u1} G P _inst_1 T))) v₁ p) (HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)) (AddTorsor.toAddAction.{u2, u1} G P _inst_1 T))) v₂ p)) (HSub.hSub.{u2, u2, u2} G G G (instHSub.{u2} G (SubNegMonoid.toSub.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1))) v₁ v₂)
Case conversion may be inaccurate. Consider using '#align vadd_vsub_vadd_cancel_right vadd_vsub_vadd_cancel_rightₓ'. -/
@[simp]
theorem vadd_vsub_vadd_cancel_right (v₁ v₂ : G) (p : P) : v₁ +ᵥ p -ᵥ (v₂ +ᵥ p) = v₁ - v₂ := by
  rw [vsub_vadd_eq_vsub_sub, vadd_vsub_assoc, vsub_self, add_zero]
#align vadd_vsub_vadd_cancel_right vadd_vsub_vadd_cancel_right

/- warning: vsub_left_cancel -> vsub_left_cancel is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] {p1 : P} {p2 : P} {p : P}, (Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p2 p)) -> (Eq.{succ u2} P p1 p2)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] {p1 : P} {p2 : P} {p : P}, (Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p2 p)) -> (Eq.{succ u1} P p1 p2)
Case conversion may be inaccurate. Consider using '#align vsub_left_cancel vsub_left_cancelₓ'. -/
/-- If the same point subtracted from two points produces equal
results, those points are equal. -/
theorem vsub_left_cancel {p1 p2 p : P} (h : p1 -ᵥ p = p2 -ᵥ p) : p1 = p2 := by
  rwa [← sub_eq_zero, vsub_sub_vsub_cancel_right, vsub_eq_zero_iff_eq] at h
#align vsub_left_cancel vsub_left_cancel

/- warning: vsub_left_cancel_iff -> vsub_left_cancel_iff is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] {p1 : P} {p2 : P} {p : P}, Iff (Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p1 p) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p2 p)) (Eq.{succ u2} P p1 p2)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] {p1 : P} {p2 : P} {p : P}, Iff (Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p1 p) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p2 p)) (Eq.{succ u1} P p1 p2)
Case conversion may be inaccurate. Consider using '#align vsub_left_cancel_iff vsub_left_cancel_iffₓ'. -/
/-- The same point subtracted from two points produces equal results
if and only if those points are equal. -/
@[simp]
theorem vsub_left_cancel_iff {p1 p2 p : P} : p1 -ᵥ p = p2 -ᵥ p ↔ p1 = p2 :=
  ⟨vsub_left_cancel, fun h => h ▸ rfl⟩
#align vsub_left_cancel_iff vsub_left_cancel_iff

#print vsub_left_injective /-
/-- Subtracting the point `p` is an injective function. -/
theorem vsub_left_injective (p : P) : Function.Injective ((· -ᵥ p) : P → G) := fun p2 p3 =>
  vsub_left_cancel
#align vsub_left_injective vsub_left_injective
-/

/- warning: vsub_right_cancel -> vsub_right_cancel is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] {p1 : P} {p2 : P} {p : P}, (Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p p1) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p p2)) -> (Eq.{succ u2} P p1 p2)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] {p1 : P} {p2 : P} {p : P}, (Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p p1) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p p2)) -> (Eq.{succ u1} P p1 p2)
Case conversion may be inaccurate. Consider using '#align vsub_right_cancel vsub_right_cancelₓ'. -/
/-- If subtracting two points from the same point produces equal
results, those points are equal. -/
theorem vsub_right_cancel {p1 p2 p : P} (h : p -ᵥ p1 = p -ᵥ p2) : p1 = p2 :=
  by
  refine' vadd_left_cancel (p -ᵥ p2) _
  rw [vsub_vadd, ← h, vsub_vadd]
#align vsub_right_cancel vsub_right_cancel

/- warning: vsub_right_cancel_iff -> vsub_right_cancel_iff is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [T : AddTorsor.{u1, u2} G P _inst_1] {p1 : P} {p2 : P} {p : P}, Iff (Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p p1) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 T) p p2)) (Eq.{succ u2} P p1 p2)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [T : AddTorsor.{u2, u1} G P _inst_1] {p1 : P} {p2 : P} {p : P}, Iff (Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p p1) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 T) p p2)) (Eq.{succ u1} P p1 p2)
Case conversion may be inaccurate. Consider using '#align vsub_right_cancel_iff vsub_right_cancel_iffₓ'. -/
/-- Subtracting two points from the same point produces equal results
if and only if those points are equal. -/
@[simp]
theorem vsub_right_cancel_iff {p1 p2 p : P} : p -ᵥ p1 = p -ᵥ p2 ↔ p1 = p2 :=
  ⟨vsub_right_cancel, fun h => h ▸ rfl⟩
#align vsub_right_cancel_iff vsub_right_cancel_iff

#print vsub_right_injective /-
/-- Subtracting a point from the point `p` is an injective
function. -/
theorem vsub_right_injective (p : P) : Function.Injective ((· -ᵥ ·) p : P → G) := fun p2 p3 =>
  vsub_right_cancel
#align vsub_right_injective vsub_right_injective
-/

end General

section comm

variable {G : Type _} {P : Type _} [AddCommGroup G] [AddTorsor G P]

include G

/- warning: vsub_sub_vsub_cancel_left -> vsub_sub_vsub_cancel_left is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddCommGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1)] (p1 : P) (p2 : P) (p3 : P), Eq.{succ u1} G (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1)))) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p3 p2) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p3 p1)) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p1 p2)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddCommGroup.{u2} G] [_inst_2 : AddTorsor.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1)] (p1 : P) (p2 : P) (p3 : P), Eq.{succ u2} G (HSub.hSub.{u2, u2, u2} G G G (instHSub.{u2} G (SubNegMonoid.toSub.{u2} G (AddGroup.toSubNegMonoid.{u2} G (AddCommGroup.toAddGroup.{u2} G _inst_1)))) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2) p3 p2) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2) p3 p1)) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2) p1 p2)
Case conversion may be inaccurate. Consider using '#align vsub_sub_vsub_cancel_left vsub_sub_vsub_cancel_leftₓ'. -/
/-- Cancellation subtracting the results of two subtractions. -/
@[simp]
theorem vsub_sub_vsub_cancel_left (p1 p2 p3 : P) : p3 -ᵥ p2 - (p3 -ᵥ p1) = p1 -ᵥ p2 := by
  rw [sub_eq_add_neg, neg_vsub_eq_vsub_rev, add_comm, vsub_add_vsub_cancel]
#align vsub_sub_vsub_cancel_left vsub_sub_vsub_cancel_left

/- warning: vadd_vsub_vadd_cancel_left -> vadd_vsub_vadd_cancel_left is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddCommGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1)] (v : G) (p1 : P) (p2 : P), Eq.{succ u1} G (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1))) (AddTorsor.toAddAction.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2)) v p1) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1))) (AddTorsor.toAddAction.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2)) v p2)) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p1 p2)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddCommGroup.{u2} G] [_inst_2 : AddTorsor.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1)] (v : G) (p1 : P) (p2 : P), Eq.{succ u2} G (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2) (HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G (AddCommGroup.toAddGroup.{u2} G _inst_1))) (AddTorsor.toAddAction.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2))) v p1) (HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G (AddCommGroup.toAddGroup.{u2} G _inst_1))) (AddTorsor.toAddAction.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2))) v p2)) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2) p1 p2)
Case conversion may be inaccurate. Consider using '#align vadd_vsub_vadd_cancel_left vadd_vsub_vadd_cancel_leftₓ'. -/
@[simp]
theorem vadd_vsub_vadd_cancel_left (v : G) (p1 p2 : P) : v +ᵥ p1 -ᵥ (v +ᵥ p2) = p1 -ᵥ p2 := by
  rw [vsub_vadd_eq_vsub_sub, vadd_vsub_assoc, add_sub_cancel']
#align vadd_vsub_vadd_cancel_left vadd_vsub_vadd_cancel_left

#print vsub_vadd_comm /-
theorem vsub_vadd_comm (p1 p2 p3 : P) : (p1 -ᵥ p2 : G) +ᵥ p3 = p3 -ᵥ p2 +ᵥ p1 :=
  by
  rw [← @vsub_eq_zero_iff_eq G, vadd_vsub_assoc, vsub_vadd_eq_vsub_sub]
  simp
#align vsub_vadd_comm vsub_vadd_comm
-/

/- warning: vadd_eq_vadd_iff_sub_eq_vsub -> vadd_eq_vadd_iff_sub_eq_vsub is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddCommGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1)] {v₁ : G} {v₂ : G} {p₁ : P} {p₂ : P}, Iff (Eq.{succ u2} P (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1))) (AddTorsor.toAddAction.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2)) v₁ p₁) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1))) (AddTorsor.toAddAction.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2)) v₂ p₂)) (Eq.{succ u1} G (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1)))) v₂ v₁) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p₁ p₂))
but is expected to have type
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddCommGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1)] {v₁ : G} {v₂ : G} {p₁ : P} {p₂ : P}, Iff (Eq.{succ u2} P (HVAdd.hVAdd.{u1, u2, u2} G P P (instHVAdd.{u1, u2} G P (AddAction.toVAdd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1))) (AddTorsor.toAddAction.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2))) v₁ p₁) (HVAdd.hVAdd.{u1, u2, u2} G P P (instHVAdd.{u1, u2} G P (AddAction.toVAdd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1))) (AddTorsor.toAddAction.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2))) v₂ p₂)) (Eq.{succ u1} G (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1)))) v₂ v₁) (VSub.vsub.{u1, u2} G P (AddTorsor.toVSub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p₁ p₂))
Case conversion may be inaccurate. Consider using '#align vadd_eq_vadd_iff_sub_eq_vsub vadd_eq_vadd_iff_sub_eq_vsubₓ'. -/
theorem vadd_eq_vadd_iff_sub_eq_vsub {v₁ v₂ : G} {p₁ p₂ : P} :
    v₁ +ᵥ p₁ = v₂ +ᵥ p₂ ↔ v₂ - v₁ = p₁ -ᵥ p₂ := by
  rw [vadd_eq_vadd_iff_neg_add_eq_vsub, neg_add_eq_sub]
#align vadd_eq_vadd_iff_sub_eq_vsub vadd_eq_vadd_iff_sub_eq_vsub

/- warning: vsub_sub_vsub_comm -> vsub_sub_vsub_comm is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddCommGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1)] (p₁ : P) (p₂ : P) (p₃ : P) (p₄ : P), Eq.{succ u1} G (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1)))) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p₁ p₂) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p₃ p₄)) (HSub.hSub.{u1, u1, u1} G G G (instHSub.{u1} G (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_1)))) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p₁ p₃) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_1) _inst_2) p₂ p₄))
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddCommGroup.{u2} G] [_inst_2 : AddTorsor.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1)] (p₁ : P) (p₂ : P) (p₃ : P) (p₄ : P), Eq.{succ u2} G (HSub.hSub.{u2, u2, u2} G G G (instHSub.{u2} G (SubNegMonoid.toSub.{u2} G (AddGroup.toSubNegMonoid.{u2} G (AddCommGroup.toAddGroup.{u2} G _inst_1)))) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2) p₁ p₂) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2) p₃ p₄)) (HSub.hSub.{u2, u2, u2} G G G (instHSub.{u2} G (SubNegMonoid.toSub.{u2} G (AddGroup.toSubNegMonoid.{u2} G (AddCommGroup.toAddGroup.{u2} G _inst_1)))) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2) p₁ p₃) (VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_1) _inst_2) p₂ p₄))
Case conversion may be inaccurate. Consider using '#align vsub_sub_vsub_comm vsub_sub_vsub_commₓ'. -/
theorem vsub_sub_vsub_comm (p₁ p₂ p₃ p₄ : P) : p₁ -ᵥ p₂ - (p₃ -ᵥ p₄) = p₁ -ᵥ p₃ - (p₂ -ᵥ p₄) := by
  rw [← vsub_vadd_eq_vsub_sub, vsub_vadd_comm, vsub_vadd_eq_vsub_sub]
#align vsub_sub_vsub_comm vsub_sub_vsub_comm

end comm

namespace Prod

variable {G : Type _} {P : Type _} {G' : Type _} {P' : Type _} [AddGroup G] [AddGroup G']
  [AddTorsor G P] [AddTorsor G' P']

instance : AddTorsor (G × G') (P × P')
    where
  vadd v p := (v.1 +ᵥ p.1, v.2 +ᵥ p.2)
  zero_vadd p := by simp
  add_vadd := by simp [add_vadd]
  vsub p₁ p₂ := (p₁.1 -ᵥ p₂.1, p₁.2 -ᵥ p₂.2)
  Nonempty := Prod.nonempty
  vsub_vadd' p₁ p₂ := show (p₁.1 -ᵥ p₂.1 +ᵥ p₂.1, _) = p₁ by simp
  vadd_vsub' v p := show (v.1 +ᵥ p.1 -ᵥ p.1, v.2 +ᵥ p.2 -ᵥ p.2) = v by simp

/- warning: prod.fst_vadd -> Prod.fst_vadd is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} {G' : Type.{u3}} {P' : Type.{u4}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddGroup.{u3} G'] [_inst_3 : AddTorsor.{u1, u2} G P _inst_1] [_inst_4 : AddTorsor.{u3, u4} G' P' _inst_2] (v : Prod.{u1, u3} G G') (p : Prod.{u2, u4} P P'), Eq.{succ u2} P (Prod.fst.{u2, u4} P P' (VAdd.vadd.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (AddAction.toHasVadd.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (SubNegMonoid.toAddMonoid.{max u1 u3} (Prod.{u1, u3} G G') (AddGroup.toSubNegMonoid.{max u1 u3} (Prod.{u1, u3} G G') (Prod.addGroup.{u1, u3} G G' _inst_1 _inst_2))) (AddTorsor.toAddAction.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (Prod.addGroup.{u1, u3} G G' _inst_1 _inst_2) (Prod.addTorsor.{u1, u2, u3, u4} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4))) v p)) (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 _inst_3)) (Prod.fst.{u1, u3} G G' v) (Prod.fst.{u2, u4} P P' p))
but is expected to have type
  forall {G : Type.{u4}} {P : Type.{u2}} {G' : Type.{u3}} {P' : Type.{u1}} [_inst_1 : AddGroup.{u4} G] [_inst_2 : AddGroup.{u3} G'] [_inst_3 : AddTorsor.{u4, u2} G P _inst_1] [_inst_4 : AddTorsor.{u3, u1} G' P' _inst_2] (v : Prod.{u4, u3} G G') (p : Prod.{u2, u1} P P'), Eq.{succ u2} P (Prod.fst.{u2, u1} P P' (HVAdd.hVAdd.{max u4 u3, max u2 u1, max u2 u1} (Prod.{u4, u3} G G') (Prod.{u2, u1} P P') (Prod.{u2, u1} P P') (instHVAdd.{max u4 u3, max u2 u1} (Prod.{u4, u3} G G') (Prod.{u2, u1} P P') (AddAction.toVAdd.{max u4 u3, max u2 u1} (Prod.{u4, u3} G G') (Prod.{u2, u1} P P') (Prod.instAddMonoidSum.{u4, u3} G G' (SubNegMonoid.toAddMonoid.{u4} G (AddGroup.toSubNegMonoid.{u4} G _inst_1)) (SubNegMonoid.toAddMonoid.{u3} G' (AddGroup.toSubNegMonoid.{u3} G' _inst_2))) (AddTorsor.toAddAction.{max u4 u3, max u2 u1} (Prod.{u4, u3} G G') (Prod.{u2, u1} P P') (Prod.instAddGroupSum.{u4, u3} G G' _inst_1 _inst_2) (Prod.instAddTorsorProdProdInstAddGroupSum.{u4, u2, u3, u1} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4)))) v p)) (HVAdd.hVAdd.{u4, u2, u2} G P P (instHVAdd.{u4, u2} G P (AddAction.toVAdd.{u4, u2} G P (SubNegMonoid.toAddMonoid.{u4} G (AddGroup.toSubNegMonoid.{u4} G _inst_1)) (AddTorsor.toAddAction.{u4, u2} G P _inst_1 _inst_3))) (Prod.fst.{u4, u3} G G' v) (Prod.fst.{u2, u1} P P' p))
Case conversion may be inaccurate. Consider using '#align prod.fst_vadd Prod.fst_vaddₓ'. -/
@[simp]
theorem fst_vadd (v : G × G') (p : P × P') : (v +ᵥ p).1 = v.1 +ᵥ p.1 :=
  rfl
#align prod.fst_vadd Prod.fst_vadd

/- warning: prod.snd_vadd -> Prod.snd_vadd is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} {G' : Type.{u3}} {P' : Type.{u4}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddGroup.{u3} G'] [_inst_3 : AddTorsor.{u1, u2} G P _inst_1] [_inst_4 : AddTorsor.{u3, u4} G' P' _inst_2] (v : Prod.{u1, u3} G G') (p : Prod.{u2, u4} P P'), Eq.{succ u4} P' (Prod.snd.{u2, u4} P P' (VAdd.vadd.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (AddAction.toHasVadd.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (SubNegMonoid.toAddMonoid.{max u1 u3} (Prod.{u1, u3} G G') (AddGroup.toSubNegMonoid.{max u1 u3} (Prod.{u1, u3} G G') (Prod.addGroup.{u1, u3} G G' _inst_1 _inst_2))) (AddTorsor.toAddAction.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (Prod.addGroup.{u1, u3} G G' _inst_1 _inst_2) (Prod.addTorsor.{u1, u2, u3, u4} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4))) v p)) (VAdd.vadd.{u3, u4} G' P' (AddAction.toHasVadd.{u3, u4} G' P' (SubNegMonoid.toAddMonoid.{u3} G' (AddGroup.toSubNegMonoid.{u3} G' _inst_2)) (AddTorsor.toAddAction.{u3, u4} G' P' _inst_2 _inst_4)) (Prod.snd.{u1, u3} G G' v) (Prod.snd.{u2, u4} P P' p))
but is expected to have type
  forall {G : Type.{u4}} {P : Type.{u2}} {G' : Type.{u3}} {P' : Type.{u1}} [_inst_1 : AddGroup.{u4} G] [_inst_2 : AddGroup.{u3} G'] [_inst_3 : AddTorsor.{u4, u2} G P _inst_1] [_inst_4 : AddTorsor.{u3, u1} G' P' _inst_2] (v : Prod.{u4, u3} G G') (p : Prod.{u2, u1} P P'), Eq.{succ u1} P' (Prod.snd.{u2, u1} P P' (HVAdd.hVAdd.{max u4 u3, max u2 u1, max u2 u1} (Prod.{u4, u3} G G') (Prod.{u2, u1} P P') (Prod.{u2, u1} P P') (instHVAdd.{max u4 u3, max u2 u1} (Prod.{u4, u3} G G') (Prod.{u2, u1} P P') (AddAction.toVAdd.{max u4 u3, max u2 u1} (Prod.{u4, u3} G G') (Prod.{u2, u1} P P') (Prod.instAddMonoidSum.{u4, u3} G G' (SubNegMonoid.toAddMonoid.{u4} G (AddGroup.toSubNegMonoid.{u4} G _inst_1)) (SubNegMonoid.toAddMonoid.{u3} G' (AddGroup.toSubNegMonoid.{u3} G' _inst_2))) (AddTorsor.toAddAction.{max u4 u3, max u2 u1} (Prod.{u4, u3} G G') (Prod.{u2, u1} P P') (Prod.instAddGroupSum.{u4, u3} G G' _inst_1 _inst_2) (Prod.instAddTorsorProdProdInstAddGroupSum.{u4, u2, u3, u1} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4)))) v p)) (HVAdd.hVAdd.{u3, u1, u1} G' P' P' (instHVAdd.{u3, u1} G' P' (AddAction.toVAdd.{u3, u1} G' P' (SubNegMonoid.toAddMonoid.{u3} G' (AddGroup.toSubNegMonoid.{u3} G' _inst_2)) (AddTorsor.toAddAction.{u3, u1} G' P' _inst_2 _inst_4))) (Prod.snd.{u4, u3} G G' v) (Prod.snd.{u2, u1} P P' p))
Case conversion may be inaccurate. Consider using '#align prod.snd_vadd Prod.snd_vaddₓ'. -/
@[simp]
theorem snd_vadd (v : G × G') (p : P × P') : (v +ᵥ p).2 = v.2 +ᵥ p.2 :=
  rfl
#align prod.snd_vadd Prod.snd_vadd

/- warning: prod.mk_vadd_mk -> Prod.mk_vadd_mk is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} {G' : Type.{u3}} {P' : Type.{u4}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddGroup.{u3} G'] [_inst_3 : AddTorsor.{u1, u2} G P _inst_1] [_inst_4 : AddTorsor.{u3, u4} G' P' _inst_2] (v : G) (v' : G') (p : P) (p' : P'), Eq.{succ (max u2 u4)} (Prod.{u2, u4} P P') (VAdd.vadd.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (AddAction.toHasVadd.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (SubNegMonoid.toAddMonoid.{max u1 u3} (Prod.{u1, u3} G G') (AddGroup.toSubNegMonoid.{max u1 u3} (Prod.{u1, u3} G G') (Prod.addGroup.{u1, u3} G G' _inst_1 _inst_2))) (AddTorsor.toAddAction.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (Prod.addGroup.{u1, u3} G G' _inst_1 _inst_2) (Prod.addTorsor.{u1, u2, u3, u4} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4))) (Prod.mk.{u1, u3} G G' v v') (Prod.mk.{u2, u4} P P' p p')) (Prod.mk.{u2, u4} P P' (VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 _inst_3)) v p) (VAdd.vadd.{u3, u4} G' P' (AddAction.toHasVadd.{u3, u4} G' P' (SubNegMonoid.toAddMonoid.{u3} G' (AddGroup.toSubNegMonoid.{u3} G' _inst_2)) (AddTorsor.toAddAction.{u3, u4} G' P' _inst_2 _inst_4)) v' p'))
but is expected to have type
  forall {G : Type.{u1}} {P : Type.{u4}} {G' : Type.{u2}} {P' : Type.{u3}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddGroup.{u2} G'] [_inst_3 : AddTorsor.{u1, u4} G P _inst_1] [_inst_4 : AddTorsor.{u2, u3} G' P' _inst_2] (v : G) (v' : G') (p : P) (p' : P'), Eq.{max (succ u4) (succ u3)} (Prod.{u4, u3} P P') (HVAdd.hVAdd.{max u2 u1, max u3 u4, max u4 u3} (Prod.{u1, u2} G G') (Prod.{u4, u3} P P') (Prod.{u4, u3} P P') (instHVAdd.{max u1 u2, max u4 u3} (Prod.{u1, u2} G G') (Prod.{u4, u3} P P') (AddAction.toVAdd.{max u1 u2, max u4 u3} (Prod.{u1, u2} G G') (Prod.{u4, u3} P P') (Prod.instAddMonoidSum.{u1, u2} G G' (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (SubNegMonoid.toAddMonoid.{u2} G' (AddGroup.toSubNegMonoid.{u2} G' _inst_2))) (AddTorsor.toAddAction.{max u1 u2, max u4 u3} (Prod.{u1, u2} G G') (Prod.{u4, u3} P P') (Prod.instAddGroupSum.{u1, u2} G G' _inst_1 _inst_2) (Prod.instAddTorsorProdProdInstAddGroupSum.{u1, u4, u2, u3} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4)))) (Prod.mk.{u1, u2} G G' v v') (Prod.mk.{u4, u3} P P' p p')) (Prod.mk.{u4, u3} P P' (HVAdd.hVAdd.{u1, u4, u4} G P P (instHVAdd.{u1, u4} G P (AddAction.toVAdd.{u1, u4} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u4} G P _inst_1 _inst_3))) v p) (HVAdd.hVAdd.{u2, u3, u3} G' P' P' (instHVAdd.{u2, u3} G' P' (AddAction.toVAdd.{u2, u3} G' P' (SubNegMonoid.toAddMonoid.{u2} G' (AddGroup.toSubNegMonoid.{u2} G' _inst_2)) (AddTorsor.toAddAction.{u2, u3} G' P' _inst_2 _inst_4))) v' p'))
Case conversion may be inaccurate. Consider using '#align prod.mk_vadd_mk Prod.mk_vadd_mkₓ'. -/
@[simp]
theorem mk_vadd_mk (v : G) (v' : G') (p : P) (p' : P') : (v, v') +ᵥ (p, p') = (v +ᵥ p, v' +ᵥ p') :=
  rfl
#align prod.mk_vadd_mk Prod.mk_vadd_mk

/- warning: prod.fst_vsub -> Prod.fst_vsub is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} {G' : Type.{u3}} {P' : Type.{u4}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddGroup.{u3} G'] [_inst_3 : AddTorsor.{u1, u2} G P _inst_1] [_inst_4 : AddTorsor.{u3, u4} G' P' _inst_2] (p₁ : Prod.{u2, u4} P P') (p₂ : Prod.{u2, u4} P P'), Eq.{succ u1} G (Prod.fst.{u1, u3} G G' (VSub.vsub.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (AddTorsor.toHasVsub.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (Prod.addGroup.{u1, u3} G G' _inst_1 _inst_2) (Prod.addTorsor.{u1, u2, u3, u4} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4)) p₁ p₂)) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 _inst_3) (Prod.fst.{u2, u4} P P' p₁) (Prod.fst.{u2, u4} P P' p₂))
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u4}} {G' : Type.{u1}} {P' : Type.{u3}} [_inst_1 : AddGroup.{u2} G] [_inst_2 : AddGroup.{u1} G'] [_inst_3 : AddTorsor.{u2, u4} G P _inst_1] [_inst_4 : AddTorsor.{u1, u3} G' P' _inst_2] (p₁ : Prod.{u4, u3} P P') (p₂ : Prod.{u4, u3} P P'), Eq.{succ u2} G (Prod.fst.{u2, u1} G G' (VSub.vsub.{max u2 u1, max u4 u3} (Prod.{u2, u1} G G') (Prod.{u4, u3} P P') (AddTorsor.toVSub.{max u2 u1, max u4 u3} (Prod.{u2, u1} G G') (Prod.{u4, u3} P P') (Prod.instAddGroupSum.{u2, u1} G G' _inst_1 _inst_2) (Prod.instAddTorsorProdProdInstAddGroupSum.{u2, u4, u1, u3} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4)) p₁ p₂)) (VSub.vsub.{u2, u4} G P (AddTorsor.toVSub.{u2, u4} G P _inst_1 _inst_3) (Prod.fst.{u4, u3} P P' p₁) (Prod.fst.{u4, u3} P P' p₂))
Case conversion may be inaccurate. Consider using '#align prod.fst_vsub Prod.fst_vsubₓ'. -/
@[simp]
theorem fst_vsub (p₁ p₂ : P × P') : (p₁ -ᵥ p₂ : G × G').1 = p₁.1 -ᵥ p₂.1 :=
  rfl
#align prod.fst_vsub Prod.fst_vsub

/- warning: prod.snd_vsub -> Prod.snd_vsub is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} {G' : Type.{u3}} {P' : Type.{u4}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddGroup.{u3} G'] [_inst_3 : AddTorsor.{u1, u2} G P _inst_1] [_inst_4 : AddTorsor.{u3, u4} G' P' _inst_2] (p₁ : Prod.{u2, u4} P P') (p₂ : Prod.{u2, u4} P P'), Eq.{succ u3} G' (Prod.snd.{u1, u3} G G' (VSub.vsub.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (AddTorsor.toHasVsub.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (Prod.addGroup.{u1, u3} G G' _inst_1 _inst_2) (Prod.addTorsor.{u1, u2, u3, u4} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4)) p₁ p₂)) (VSub.vsub.{u3, u4} G' P' (AddTorsor.toHasVsub.{u3, u4} G' P' _inst_2 _inst_4) (Prod.snd.{u2, u4} P P' p₁) (Prod.snd.{u2, u4} P P' p₂))
but is expected to have type
  forall {G : Type.{u1}} {P : Type.{u4}} {G' : Type.{u2}} {P' : Type.{u3}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddGroup.{u2} G'] [_inst_3 : AddTorsor.{u1, u4} G P _inst_1] [_inst_4 : AddTorsor.{u2, u3} G' P' _inst_2] (p₁ : Prod.{u4, u3} P P') (p₂ : Prod.{u4, u3} P P'), Eq.{succ u2} G' (Prod.snd.{u1, u2} G G' (VSub.vsub.{max u1 u2, max u4 u3} (Prod.{u1, u2} G G') (Prod.{u4, u3} P P') (AddTorsor.toVSub.{max u1 u2, max u4 u3} (Prod.{u1, u2} G G') (Prod.{u4, u3} P P') (Prod.instAddGroupSum.{u1, u2} G G' _inst_1 _inst_2) (Prod.instAddTorsorProdProdInstAddGroupSum.{u1, u4, u2, u3} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4)) p₁ p₂)) (VSub.vsub.{u2, u3} G' P' (AddTorsor.toVSub.{u2, u3} G' P' _inst_2 _inst_4) (Prod.snd.{u4, u3} P P' p₁) (Prod.snd.{u4, u3} P P' p₂))
Case conversion may be inaccurate. Consider using '#align prod.snd_vsub Prod.snd_vsubₓ'. -/
@[simp]
theorem snd_vsub (p₁ p₂ : P × P') : (p₁ -ᵥ p₂ : G × G').2 = p₁.2 -ᵥ p₂.2 :=
  rfl
#align prod.snd_vsub Prod.snd_vsub

/- warning: prod.mk_vsub_mk -> Prod.mk_vsub_mk is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} {G' : Type.{u3}} {P' : Type.{u4}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddGroup.{u3} G'] [_inst_3 : AddTorsor.{u1, u2} G P _inst_1] [_inst_4 : AddTorsor.{u3, u4} G' P' _inst_2] (p₁ : P) (p₂ : P) (p₁' : P') (p₂' : P'), Eq.{max (succ u1) (succ u3)} (Prod.{u1, u3} G G') (VSub.vsub.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (AddTorsor.toHasVsub.{max u1 u3, max u2 u4} (Prod.{u1, u3} G G') (Prod.{u2, u4} P P') (Prod.addGroup.{u1, u3} G G' _inst_1 _inst_2) (Prod.addTorsor.{u1, u2, u3, u4} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4)) (Prod.mk.{u2, u4} P P' p₁ p₁') (Prod.mk.{u2, u4} P P' p₂ p₂')) (Prod.mk.{u1, u3} G G' (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 _inst_3) p₁ p₂) (VSub.vsub.{u3, u4} G' P' (AddTorsor.toHasVsub.{u3, u4} G' P' _inst_2 _inst_4) p₁' p₂'))
but is expected to have type
  forall {G : Type.{u4}} {P : Type.{u1}} {G' : Type.{u3}} {P' : Type.{u2}} [_inst_1 : AddGroup.{u4} G] [_inst_2 : AddGroup.{u3} G'] [_inst_3 : AddTorsor.{u4, u1} G P _inst_1] [_inst_4 : AddTorsor.{u3, u2} G' P' _inst_2] (p₁ : P) (p₂ : P) (p₁' : P') (p₂' : P'), Eq.{max (succ u4) (succ u3)} (Prod.{u4, u3} G G') (VSub.vsub.{max u4 u3, max u2 u1} (Prod.{u4, u3} G G') (Prod.{u1, u2} P P') (AddTorsor.toVSub.{max u4 u3, max u1 u2} (Prod.{u4, u3} G G') (Prod.{u1, u2} P P') (Prod.instAddGroupSum.{u4, u3} G G' _inst_1 _inst_2) (Prod.instAddTorsorProdProdInstAddGroupSum.{u4, u1, u3, u2} G P G' P' _inst_1 _inst_2 _inst_3 _inst_4)) (Prod.mk.{u1, u2} P P' p₁ p₁') (Prod.mk.{u1, u2} P P' p₂ p₂')) (Prod.mk.{u4, u3} G G' (VSub.vsub.{u4, u1} G P (AddTorsor.toVSub.{u4, u1} G P _inst_1 _inst_3) p₁ p₂) (VSub.vsub.{u3, u2} G' P' (AddTorsor.toVSub.{u3, u2} G' P' _inst_2 _inst_4) p₁' p₂'))
Case conversion may be inaccurate. Consider using '#align prod.mk_vsub_mk Prod.mk_vsub_mkₓ'. -/
@[simp]
theorem mk_vsub_mk (p₁ p₂ : P) (p₁' p₂' : P') :
    ((p₁, p₁') -ᵥ (p₂, p₂') : G × G') = (p₁ -ᵥ p₂, p₁' -ᵥ p₂') :=
  rfl
#align prod.mk_vsub_mk Prod.mk_vsub_mk

end Prod

namespace Pi

universe u v w

variable {I : Type u} {fg : I → Type v} [∀ i, AddGroup (fg i)] {fp : I → Type w}

open AddAction AddTorsor

/-- A product of `add_torsor`s is an `add_torsor`. -/
instance [T : ∀ i, AddTorsor (fg i) (fp i)] : AddTorsor (∀ i, fg i) (∀ i, fp i)
    where
  vadd g p i := g i +ᵥ p i
  zero_vadd p := funext fun i => zero_vadd (fg i) (p i)
  add_vadd g₁ g₂ p := funext fun i => add_vadd (g₁ i) (g₂ i) (p i)
  vsub p₁ p₂ i := p₁ i -ᵥ p₂ i
  Nonempty := ⟨fun i => Classical.choice (T i).Nonempty⟩
  vsub_vadd' p₁ p₂ := funext fun i => vsub_vadd (p₁ i) (p₂ i)
  vadd_vsub' g p := funext fun i => vadd_vsub (g i) (p i)

end Pi

namespace Equiv

variable {G : Type _} {P : Type _} [AddGroup G] [AddTorsor G P]

include G

#print Equiv.vaddConst /-
/-- `v ↦ v +ᵥ p` as an equivalence. -/
def vaddConst (p : P) : G ≃ P where
  toFun v := v +ᵥ p
  invFun p' := p' -ᵥ p
  left_inv v := vadd_vsub _ _
  right_inv p' := vsub_vadd _ _
#align equiv.vadd_const Equiv.vaddConst
-/

/- warning: equiv.coe_vadd_const -> Equiv.coe_vaddConst is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1] (p : P), Eq.{max (succ u1) (succ u2)} (G -> P) (coeFn.{max 1 (max (succ u1) (succ u2)) (succ u2) (succ u1), max (succ u1) (succ u2)} (Equiv.{succ u1, succ u2} G P) (fun (_x : Equiv.{succ u1, succ u2} G P) => G -> P) (Equiv.hasCoeToFun.{succ u1, succ u2} G P) (Equiv.vaddConst.{u1, u2} G P _inst_1 _inst_2 p)) (fun (v : G) => VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 _inst_2)) v p)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [_inst_2 : AddTorsor.{u2, u1} G P _inst_1] (p : P), Eq.{max (succ u2) (succ u1)} (forall (ᾰ : G), (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : G) => P) ᾰ) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Equiv.{succ u2, succ u1} G P) G (fun (_x : G) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : G) => P) _x) (Equiv.instFunLikeEquiv.{succ u2, succ u1} G P) (Equiv.vaddConst.{u2, u1} G P _inst_1 _inst_2 p)) (fun (v : G) => HVAdd.hVAdd.{u2, u1, u1} G P ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : G) => P) v) (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)) (AddTorsor.toAddAction.{u2, u1} G P _inst_1 _inst_2))) v p)
Case conversion may be inaccurate. Consider using '#align equiv.coe_vadd_const Equiv.coe_vaddConstₓ'. -/
@[simp]
theorem coe_vaddConst (p : P) : ⇑(vaddConst p) = fun v => v +ᵥ p :=
  rfl
#align equiv.coe_vadd_const Equiv.coe_vaddConst

/- warning: equiv.coe_vadd_const_symm -> Equiv.coe_vaddConst_symm is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1] (p : P), Eq.{max (succ u2) (succ u1)} (P -> G) (coeFn.{max 1 (max (succ u2) (succ u1)) (succ u1) (succ u2), max (succ u2) (succ u1)} (Equiv.{succ u2, succ u1} P G) (fun (_x : Equiv.{succ u2, succ u1} P G) => P -> G) (Equiv.hasCoeToFun.{succ u2, succ u1} P G) (Equiv.symm.{succ u1, succ u2} G P (Equiv.vaddConst.{u1, u2} G P _inst_1 _inst_2 p))) (fun (p' : P) => VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 _inst_2) p' p)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [_inst_2 : AddTorsor.{u2, u1} G P _inst_1] (p : P), Eq.{max (succ u2) (succ u1)} (forall (ᾰ : P), (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : P) => G) ᾰ) (FunLike.coe.{max (succ u2) (succ u1), succ u1, succ u2} (Equiv.{succ u1, succ u2} P G) P (fun (_x : P) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : P) => G) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u2} P G) (Equiv.symm.{succ u2, succ u1} G P (Equiv.vaddConst.{u2, u1} G P _inst_1 _inst_2 p))) (fun (p' : P) => VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 _inst_2) p' p)
Case conversion may be inaccurate. Consider using '#align equiv.coe_vadd_const_symm Equiv.coe_vaddConst_symmₓ'. -/
@[simp]
theorem coe_vaddConst_symm (p : P) : ⇑(vaddConst p).symm = fun p' => p' -ᵥ p :=
  rfl
#align equiv.coe_vadd_const_symm Equiv.coe_vaddConst_symm

#print Equiv.constVSub /-
/-- `p' ↦ p -ᵥ p'` as an equivalence. -/
def constVSub (p : P) : P ≃ G where
  toFun := (· -ᵥ ·) p
  invFun v := -v +ᵥ p
  left_inv p' := by simp
  right_inv v := by simp [vsub_vadd_eq_vsub_sub]
#align equiv.const_vsub Equiv.constVSub
-/

/- warning: equiv.coe_const_vsub -> Equiv.coe_constVSub is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1] (p : P), Eq.{max (succ u2) (succ u1)} (P -> G) (coeFn.{max 1 (max (succ u2) (succ u1)) (succ u1) (succ u2), max (succ u2) (succ u1)} (Equiv.{succ u2, succ u1} P G) (fun (_x : Equiv.{succ u2, succ u1} P G) => P -> G) (Equiv.hasCoeToFun.{succ u2, succ u1} P G) (Equiv.constVSub.{u1, u2} G P _inst_1 _inst_2 p)) (VSub.vsub.{u1, u2} G P (AddTorsor.toHasVsub.{u1, u2} G P _inst_1 _inst_2) p)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [_inst_2 : AddTorsor.{u2, u1} G P _inst_1] (p : P), Eq.{max (succ u2) (succ u1)} (forall (ᾰ : P), (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : P) => G) ᾰ) (FunLike.coe.{max (succ u2) (succ u1), succ u1, succ u2} (Equiv.{succ u1, succ u2} P G) P (fun (_x : P) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : P) => G) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u2} P G) (Equiv.constVSub.{u2, u1} G P _inst_1 _inst_2 p)) ((fun (x._@.Mathlib.Algebra.AddTorsor._hyg.2809 : P) (x._@.Mathlib.Algebra.AddTorsor._hyg.2811 : P) => VSub.vsub.{u2, u1} G P (AddTorsor.toVSub.{u2, u1} G P _inst_1 _inst_2) x._@.Mathlib.Algebra.AddTorsor._hyg.2809 x._@.Mathlib.Algebra.AddTorsor._hyg.2811) p)
Case conversion may be inaccurate. Consider using '#align equiv.coe_const_vsub Equiv.coe_constVSubₓ'. -/
@[simp]
theorem coe_constVSub (p : P) : ⇑(constVSub p) = (· -ᵥ ·) p :=
  rfl
#align equiv.coe_const_vsub Equiv.coe_constVSub

/- warning: equiv.coe_const_vsub_symm -> Equiv.coe_constVSub_symm is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1] (p : P), Eq.{max (succ u1) (succ u2)} (G -> P) (coeFn.{max 1 (max (succ u1) (succ u2)) (succ u2) (succ u1), max (succ u1) (succ u2)} (Equiv.{succ u1, succ u2} G P) (fun (_x : Equiv.{succ u1, succ u2} G P) => G -> P) (Equiv.hasCoeToFun.{succ u1, succ u2} G P) (Equiv.symm.{succ u2, succ u1} P G (Equiv.constVSub.{u1, u2} G P _inst_1 _inst_2 p))) (fun (v : G) => VAdd.vadd.{u1, u2} G P (AddAction.toHasVadd.{u1, u2} G P (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) (AddTorsor.toAddAction.{u1, u2} G P _inst_1 _inst_2)) (Neg.neg.{u1} G (SubNegMonoid.toHasNeg.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)) v) p)
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [_inst_2 : AddTorsor.{u2, u1} G P _inst_1] (p : P), Eq.{max (succ u2) (succ u1)} (forall (ᾰ : G), (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : G) => P) ᾰ) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Equiv.{succ u2, succ u1} G P) G (fun (_x : G) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : G) => P) _x) (Equiv.instFunLikeEquiv.{succ u2, succ u1} G P) (Equiv.symm.{succ u1, succ u2} P G (Equiv.constVSub.{u2, u1} G P _inst_1 _inst_2 p))) (fun (v : G) => HVAdd.hVAdd.{u2, u1, u1} G P P (instHVAdd.{u2, u1} G P (AddAction.toVAdd.{u2, u1} G P (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)) (AddTorsor.toAddAction.{u2, u1} G P _inst_1 _inst_2))) (Neg.neg.{u2} G (NegZeroClass.toNeg.{u2} G (SubNegZeroMonoid.toNegZeroClass.{u2} G (SubtractionMonoid.toSubNegZeroMonoid.{u2} G (AddGroup.toSubtractionMonoid.{u2} G _inst_1)))) v) p)
Case conversion may be inaccurate. Consider using '#align equiv.coe_const_vsub_symm Equiv.coe_constVSub_symmₓ'. -/
@[simp]
theorem coe_constVSub_symm (p : P) : ⇑(constVSub p).symm = fun v => -v +ᵥ p :=
  rfl
#align equiv.coe_const_vsub_symm Equiv.coe_constVSub_symm

variable (P)

#print Equiv.constVAdd /-
/-- The permutation given by `p ↦ v +ᵥ p`. -/
def constVAdd (v : G) : Equiv.Perm P where
  toFun := (· +ᵥ ·) v
  invFun := (· +ᵥ ·) (-v)
  left_inv p := by simp [vadd_vadd]
  right_inv p := by simp [vadd_vadd]
#align equiv.const_vadd Equiv.constVAdd
-/

#print Equiv.coe_constVAdd /-
@[simp]
theorem coe_constVAdd (v : G) : ⇑(constVAdd P v) = (· +ᵥ ·) v :=
  rfl
#align equiv.coe_const_vadd Equiv.coe_constVAdd
-/

variable (G)

/- warning: equiv.const_vadd_zero -> Equiv.constVAdd_zero is a dubious translation:
lean 3 declaration is
  forall (G : Type.{u1}) (P : Type.{u2}) [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1], Eq.{succ u2} (Equiv.Perm.{succ u2} P) (Equiv.constVAdd.{u1, u2} G P _inst_1 _inst_2 (OfNat.ofNat.{u1} G 0 (OfNat.mk.{u1} G 0 (Zero.zero.{u1} G (AddZeroClass.toHasZero.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)))))))) (OfNat.ofNat.{u2} (Equiv.Perm.{succ u2} P) 1 (OfNat.mk.{u2} (Equiv.Perm.{succ u2} P) 1 (One.one.{u2} (Equiv.Perm.{succ u2} P) (MulOneClass.toHasOne.{u2} (Equiv.Perm.{succ u2} P) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} P) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} P) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} P) (Equiv.Perm.permGroup.{u2} P))))))))
but is expected to have type
  forall (G : Type.{u1}) (P : Type.{u2}) [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1], Eq.{succ u2} (Equiv.Perm.{succ u2} P) (Equiv.constVAdd.{u1, u2} G P _inst_1 _inst_2 (OfNat.ofNat.{u1} G 0 (Zero.toOfNat0.{u1} G (NegZeroClass.toZero.{u1} G (SubNegZeroMonoid.toNegZeroClass.{u1} G (SubtractionMonoid.toSubNegZeroMonoid.{u1} G (AddGroup.toSubtractionMonoid.{u1} G _inst_1))))))) (OfNat.ofNat.{u2} (Equiv.Perm.{succ u2} P) 1 (One.toOfNat1.{u2} (Equiv.Perm.{succ u2} P) (InvOneClass.toOne.{u2} (Equiv.Perm.{succ u2} P) (DivInvOneMonoid.toInvOneClass.{u2} (Equiv.Perm.{succ u2} P) (DivisionMonoid.toDivInvOneMonoid.{u2} (Equiv.Perm.{succ u2} P) (Group.toDivisionMonoid.{u2} (Equiv.Perm.{succ u2} P) (Equiv.Perm.permGroup.{u2} P)))))))
Case conversion may be inaccurate. Consider using '#align equiv.const_vadd_zero Equiv.constVAdd_zeroₓ'. -/
@[simp]
theorem constVAdd_zero : constVAdd P (0 : G) = 1 :=
  ext <| zero_vadd G
#align equiv.const_vadd_zero Equiv.constVAdd_zero

variable {G}

/- warning: equiv.const_vadd_add -> Equiv.constVAdd_add is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} (P : Type.{u2}) [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1] (v₁ : G) (v₂ : G), Eq.{succ u2} (Equiv.Perm.{succ u2} P) (Equiv.constVAdd.{u1, u2} G P _inst_1 _inst_2 (HAdd.hAdd.{u1, u1, u1} G G G (instHAdd.{u1} G (AddZeroClass.toHasAdd.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))))) v₁ v₂)) (HMul.hMul.{u2, u2, u2} (Equiv.Perm.{succ u2} P) (Equiv.Perm.{succ u2} P) (Equiv.Perm.{succ u2} P) (instHMul.{u2} (Equiv.Perm.{succ u2} P) (MulOneClass.toHasMul.{u2} (Equiv.Perm.{succ u2} P) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} P) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} P) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} P) (Equiv.Perm.permGroup.{u2} P)))))) (Equiv.constVAdd.{u1, u2} G P _inst_1 _inst_2 v₁) (Equiv.constVAdd.{u1, u2} G P _inst_1 _inst_2 v₂))
but is expected to have type
  forall {G : Type.{u1}} (P : Type.{u2}) [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1] (v₁ : G) (v₂ : G), Eq.{succ u2} (Equiv.Perm.{succ u2} P) (Equiv.constVAdd.{u1, u2} G P _inst_1 _inst_2 (HAdd.hAdd.{u1, u1, u1} G G G (instHAdd.{u1} G (AddZeroClass.toAdd.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1))))) v₁ v₂)) (HMul.hMul.{u2, u2, u2} (Equiv.Perm.{succ u2} P) (Equiv.Perm.{succ u2} P) (Equiv.Perm.{succ u2} P) (instHMul.{u2} (Equiv.Perm.{succ u2} P) (MulOneClass.toMul.{u2} (Equiv.Perm.{succ u2} P) (Monoid.toMulOneClass.{u2} (Equiv.Perm.{succ u2} P) (DivInvMonoid.toMonoid.{u2} (Equiv.Perm.{succ u2} P) (Group.toDivInvMonoid.{u2} (Equiv.Perm.{succ u2} P) (Equiv.Perm.permGroup.{u2} P)))))) (Equiv.constVAdd.{u1, u2} G P _inst_1 _inst_2 v₁) (Equiv.constVAdd.{u1, u2} G P _inst_1 _inst_2 v₂))
Case conversion may be inaccurate. Consider using '#align equiv.const_vadd_add Equiv.constVAdd_addₓ'. -/
@[simp]
theorem constVAdd_add (v₁ v₂ : G) : constVAdd P (v₁ + v₂) = constVAdd P v₁ * constVAdd P v₂ :=
  ext <| add_vadd v₁ v₂
#align equiv.const_vadd_add Equiv.constVAdd_add

#print Equiv.constVAddHom /-
/-- `equiv.const_vadd` as a homomorphism from `multiplicative G` to `equiv.perm P` -/
def constVAddHom : Multiplicative G →* Equiv.Perm P
    where
  toFun v := constVAdd P v.toAdd
  map_one' := constVAdd_zero G P
  map_mul' := constVAdd_add P
#align equiv.const_vadd_hom Equiv.constVAddHom
-/

variable {P}

open _Root_.Function

#print Equiv.pointReflection /-
/-- Point reflection in `x` as a permutation. -/
def pointReflection (x : P) : Perm P :=
  (constVSub x).trans (vaddConst x)
#align equiv.point_reflection Equiv.pointReflection
-/

#print Equiv.pointReflection_apply /-
theorem pointReflection_apply (x y : P) : pointReflection x y = x -ᵥ y +ᵥ x :=
  rfl
#align equiv.point_reflection_apply Equiv.pointReflection_apply
-/

#print Equiv.pointReflection_symm /-
@[simp]
theorem pointReflection_symm (x : P) : (pointReflection x).symm = pointReflection x :=
  ext <| by simp [point_reflection]
#align equiv.point_reflection_symm Equiv.pointReflection_symm
-/

#print Equiv.pointReflection_self /-
@[simp]
theorem pointReflection_self (x : P) : pointReflection x x = x :=
  vsub_vadd _ _
#align equiv.point_reflection_self Equiv.pointReflection_self
-/

#print Equiv.pointReflection_involutive /-
theorem pointReflection_involutive (x : P) : Involutive (pointReflection x : P → P) := fun y =>
  (Equiv.apply_eq_iff_eq_symm_apply _).2 <| by rw [point_reflection_symm]
#align equiv.point_reflection_involutive Equiv.pointReflection_involutive
-/

/- warning: equiv.point_reflection_fixed_iff_of_injective_bit0 -> Equiv.pointReflection_fixed_iff_of_injective_bit0 is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1] {x : P} {y : P}, (Function.Injective.{succ u1, succ u1} G G (bit0.{u1} G (AddZeroClass.toHasAdd.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G _inst_1)))))) -> (Iff (Eq.{succ u2} P (coeFn.{succ u2, succ u2} (Equiv.Perm.{succ u2} P) (fun (_x : Equiv.{succ u2, succ u2} P P) => P -> P) (Equiv.hasCoeToFun.{succ u2, succ u2} P P) (Equiv.pointReflection.{u1, u2} G P _inst_1 _inst_2 x) y) y) (Eq.{succ u2} P y x))
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_1 : AddGroup.{u2} G] [_inst_2 : AddTorsor.{u2, u1} G P _inst_1] {x : P} {y : P}, (Function.Injective.{succ u2, succ u2} G G (bit0.{u2} G (AddZeroClass.toAdd.{u2} G (AddMonoid.toAddZeroClass.{u2} G (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G _inst_1)))))) -> (Iff (Eq.{succ u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : P) => P) y) (FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.Perm.{succ u1} P) P (fun (_x : P) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : P) => P) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u1} P P) (Equiv.pointReflection.{u2, u1} G P _inst_1 _inst_2 x) y) y) (Eq.{succ u1} P y x))
Case conversion may be inaccurate. Consider using '#align equiv.point_reflection_fixed_iff_of_injective_bit0 Equiv.pointReflection_fixed_iff_of_injective_bit0ₓ'. -/
/-- `x` is the only fixed point of `point_reflection x`. This lemma requires
`x + x = y + y ↔ x = y`. There is no typeclass to use here, so we add it as an explicit argument. -/
theorem pointReflection_fixed_iff_of_injective_bit0 {x y : P} (h : Injective (bit0 : G → G)) :
    pointReflection x y = y ↔ y = x := by
  rw [point_reflection_apply, eq_comm, eq_vadd_iff_vsub_eq, ← neg_vsub_eq_vsub_rev,
    neg_eq_iff_add_eq_zero, ← bit0, ← bit0_zero, h.eq_iff, vsub_eq_zero_iff_eq, eq_comm]
#align equiv.point_reflection_fixed_iff_of_injective_bit0 Equiv.pointReflection_fixed_iff_of_injective_bit0

omit G

/- warning: equiv.injective_point_reflection_left_of_injective_bit0 -> Equiv.injective_pointReflection_left_of_injective_bit0 is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} {P : Type.{u2}} [_inst_3 : AddCommGroup.{u1} G] [_inst_4 : AddTorsor.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_3)], (Function.Injective.{succ u1, succ u1} G G (bit0.{u1} G (AddZeroClass.toHasAdd.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G _inst_3))))))) -> (forall (y : P), Function.Injective.{succ u2, succ u2} P P (fun (x : P) => coeFn.{succ u2, succ u2} (Equiv.Perm.{succ u2} P) (fun (_x : Equiv.{succ u2, succ u2} P P) => P -> P) (Equiv.hasCoeToFun.{succ u2, succ u2} P P) (Equiv.pointReflection.{u1, u2} G P (AddCommGroup.toAddGroup.{u1} G _inst_3) _inst_4 x) y))
but is expected to have type
  forall {G : Type.{u2}} {P : Type.{u1}} [_inst_3 : AddCommGroup.{u2} G] [_inst_4 : AddTorsor.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_3)], (Function.Injective.{succ u2, succ u2} G G (bit0.{u2} G (AddZeroClass.toAdd.{u2} G (AddMonoid.toAddZeroClass.{u2} G (SubNegMonoid.toAddMonoid.{u2} G (AddGroup.toSubNegMonoid.{u2} G (AddCommGroup.toAddGroup.{u2} G _inst_3))))))) -> (forall (y : P), Function.Injective.{succ u1, succ u1} P ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : P) => P) y) (fun (x : P) => FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.Perm.{succ u1} P) P (fun (_x : P) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : P) => P) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u1} P P) (Equiv.pointReflection.{u2, u1} G P (AddCommGroup.toAddGroup.{u2} G _inst_3) _inst_4 x) y))
Case conversion may be inaccurate. Consider using '#align equiv.injective_point_reflection_left_of_injective_bit0 Equiv.injective_pointReflection_left_of_injective_bit0ₓ'. -/
theorem injective_pointReflection_left_of_injective_bit0 {G P : Type _} [AddCommGroup G]
    [AddTorsor G P] (h : Injective (bit0 : G → G)) (y : P) :
    Injective fun x : P => pointReflection x y :=
  fun x₁ x₂ (hy : pointReflection x₁ y = pointReflection x₂ y) => by
  rwa [point_reflection_apply, point_reflection_apply, vadd_eq_vadd_iff_sub_eq_vsub,
    vsub_sub_vsub_cancel_right, ← neg_vsub_eq_vsub_rev, neg_eq_iff_add_eq_zero, ← bit0, ← bit0_zero,
    h.eq_iff, vsub_eq_zero_iff_eq] at hy
#align equiv.injective_point_reflection_left_of_injective_bit0 Equiv.injective_pointReflection_left_of_injective_bit0

end Equiv

/- warning: add_torsor.subsingleton_iff -> AddTorsor.subsingleton_iff is a dubious translation:
lean 3 declaration is
  forall (G : Type.{u1}) (P : Type.{u2}) [_inst_1 : AddGroup.{u1} G] [_inst_2 : AddTorsor.{u1, u2} G P _inst_1], Iff (Subsingleton.{succ u1} G) (Subsingleton.{succ u2} P)
but is expected to have type
  forall (G : Type.{u2}) (P : Type.{u1}) [_inst_1 : AddGroup.{u2} G] [_inst_2 : AddTorsor.{u2, u1} G P _inst_1], Iff (Subsingleton.{succ u2} G) (Subsingleton.{succ u1} P)
Case conversion may be inaccurate. Consider using '#align add_torsor.subsingleton_iff AddTorsor.subsingleton_iffₓ'. -/
theorem AddTorsor.subsingleton_iff (G P : Type _) [AddGroup G] [AddTorsor G P] :
    Subsingleton G ↔ Subsingleton P := by
  inhabit P
  exact (Equiv.vaddConst default).subsingleton_congr
#align add_torsor.subsingleton_iff AddTorsor.subsingleton_iff


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Joey van Langen, Casper Putz
-/

import algebra.hom.iterate
import data.int.modeq
import data.nat.choose.dvd
import data.nat.choose.sum
import group_theory.order_of_element
import ring_theory.nilpotent
/-!
# Characteristic of semirings
-/

universes u v

variables (R : Type u)

/-- The generator of the kernel of the unique homomorphism ℕ → R for a semiring R.

*Warning*: for a semiring `R`, `char_p R 0` and `char_zero R` need not coincide.
* `char_p R 0` asks that only `0 : ℕ` maps to `0 : R` under the map `ℕ → R`;
* `char_zero R` requires an injection `ℕ ↪ R`.

For instance, endowing `{0, 1}` with addition given by `max` (i.e. `1` is absorbing), shows that
`char_zero {0, 1}` does not hold and yet `char_p {0, 1} 0` does.
This example is formalized in `counterexamples/char_p_zero_ne_char_zero`.
 -/
@[mk_iff]
class char_p [add_monoid_with_one R] (p : ℕ) : Prop :=
(cast_eq_zero_iff [] : ∀ x:ℕ, (x:R) = 0 ↔ p ∣ x)

theorem char_p.cast_eq_zero [add_monoid_with_one R] (p : ℕ) [char_p R p] :
  (p:R) = 0 :=
(char_p.cast_eq_zero_iff R p p).2 (dvd_refl p)

@[simp] lemma char_p.cast_card_eq_zero [add_group_with_one R] [fintype R] :
  (fintype.card R : R) = 0 :=
by rw [← nsmul_one, card_nsmul_eq_zero]

lemma char_p.int_cast_eq_zero_iff [add_group_with_one R] (p : ℕ) [char_p R p]
  (a : ℤ) :
  (a : R) = 0 ↔ (p:ℤ) ∣ a :=
begin
  rcases lt_trichotomy a 0 with h|rfl|h,
  { rw [← neg_eq_zero, ← int.cast_neg, ← dvd_neg],
    lift -a to ℕ using neg_nonneg.mpr (le_of_lt h) with b,
    rw [int.cast_coe_nat, char_p.cast_eq_zero_iff R p, int.coe_nat_dvd] },
  { simp only [int.cast_zero, eq_self_iff_true, dvd_zero] },
  { lift a to ℕ using (le_of_lt h) with b,
    rw [int.cast_coe_nat, char_p.cast_eq_zero_iff R p, int.coe_nat_dvd] }
end

lemma char_p.int_coe_eq_int_coe_iff [add_group_with_one R] (p : ℕ) [char_p R p] (a b : ℤ) :
  (a : R) = (b : R) ↔ a ≡ b [ZMOD p] :=
by rw [eq_comm, ←sub_eq_zero, ←int.cast_sub,
       char_p.int_cast_eq_zero_iff R p, int.modeq_iff_dvd]

theorem char_p.eq [add_monoid_with_one R] {p q : ℕ} (c1 : char_p R p) (c2 : char_p R q) :
  p = q :=
nat.dvd_antisymm
  ((char_p.cast_eq_zero_iff R p q).1 (char_p.cast_eq_zero _ _))
  ((char_p.cast_eq_zero_iff R q p).1 (char_p.cast_eq_zero _ _))

instance char_p.of_char_zero [add_monoid_with_one R] [char_zero R] : char_p R 0 :=
⟨λ x, by rw [zero_dvd_iff, ← nat.cast_zero, nat.cast_inj]⟩

theorem char_p.exists [non_assoc_semiring R] : ∃ p, char_p R p :=
by letI := classical.dec_eq R; exact
classical.by_cases
  (assume H : ∀ p:ℕ, (p:R) = 0 → p = 0, ⟨0,
    ⟨λ x, by rw [zero_dvd_iff]; exact ⟨H x, by rintro rfl; simp⟩⟩⟩)
  (λ H, ⟨nat.find (not_forall.1 H), ⟨λ x,
    ⟨λ H1, nat.dvd_of_mod_eq_zero (by_contradiction $ λ H2,
      nat.find_min (not_forall.1 H)
        (nat.mod_lt x $ nat.pos_of_ne_zero $ not_of_not_imp $
          nat.find_spec (not_forall.1 H))
        (not_imp_of_and_not ⟨by rwa [← nat.mod_add_div x (nat.find (not_forall.1 H)),
          nat.cast_add, nat.cast_mul, of_not_not (not_not_of_not_imp $ nat.find_spec
            (not_forall.1 H)),
          zero_mul, add_zero] at H1, H2⟩)),
    λ H1, by rw [← nat.mul_div_cancel' H1, nat.cast_mul,
      of_not_not (not_not_of_not_imp $ nat.find_spec (not_forall.1 H)), zero_mul]⟩⟩⟩)

theorem char_p.exists_unique [non_assoc_semiring R] : ∃! p, char_p R p :=
let ⟨c, H⟩ := char_p.exists R in ⟨c, H, λ y H2, char_p.eq R H2 H⟩

theorem char_p.congr {R : Type u} [add_monoid_with_one R] {p : ℕ} (q : ℕ) [hq : char_p R q]
  (h : q = p) :
  char_p R p :=
h ▸ hq

/-- Noncomputable function that outputs the unique characteristic of a semiring. -/
noncomputable def ring_char [non_assoc_semiring R] : ℕ :=
classical.some (char_p.exists_unique R)

namespace ring_char
variables [non_assoc_semiring R]

theorem spec : ∀ x:ℕ, (x:R) = 0 ↔ ring_char R ∣ x :=
by letI := (classical.some_spec (char_p.exists_unique R)).1;
unfold ring_char; exact char_p.cast_eq_zero_iff R (ring_char R)

theorem eq (p : ℕ) [C : char_p R p] : ring_char R = p :=
((classical.some_spec (char_p.exists_unique R)).2 p C).symm

instance char_p : char_p R (ring_char R) :=
⟨spec R⟩

variables {R}

theorem of_eq {p : ℕ} (h : ring_char R = p) : char_p R p :=
char_p.congr (ring_char R) h

theorem eq_iff {p : ℕ} : ring_char R = p ↔ char_p R p :=
⟨of_eq, @eq R _ p⟩

theorem dvd {x : ℕ} (hx : (x : R) = 0) : ring_char R ∣ x :=
(spec R x).1 hx

@[simp]
lemma eq_zero [char_zero R] : ring_char R = 0 := eq R 0

@[simp]
lemma nat.cast_ring_char : (ring_char R : R) = 0 :=
by rw ring_char.spec

end ring_char

theorem add_pow_char_of_commute [semiring R] {p : ℕ} [fact p.prime]
  [char_p R p] (x y : R) (h : commute x y) :
  (x + y)^p = x^p + y^p :=
begin
  rw [commute.add_pow h, finset.sum_range_succ_comm, tsub_self, pow_zero, nat.choose_self],
  rw [nat.cast_one, mul_one, mul_one], congr' 1,
  convert finset.sum_eq_single 0 _ _,
  { simp only [mul_one, one_mul, nat.choose_zero_right, tsub_zero, nat.cast_one, pow_zero] },
  { intros b h1 h2,
    suffices : (p.choose b : R) = 0, { rw this, simp },
    rw char_p.cast_eq_zero_iff R p,
    refine nat.prime.dvd_choose_self (pos_iff_ne_zero.mpr h2) _ (fact.out _),
    rwa ← finset.mem_range },
  { intro h1,
    contrapose! h1,
    rw finset.mem_range,
    exact nat.prime.pos (fact.out _) }
end

theorem add_pow_char_pow_of_commute [semiring R] {p : ℕ} [fact p.prime]
  [char_p R p] {n : ℕ} (x y : R) (h : commute x y) :
  (x + y) ^ (p ^ n) = x ^ (p ^ n) + y ^ (p ^ n) :=
begin
  induction n, { simp, },
  rw [pow_succ', pow_mul, pow_mul, pow_mul, n_ih],
  apply add_pow_char_of_commute, apply commute.pow_pow h,
end

theorem sub_pow_char_of_commute [ring R] {p : ℕ} [fact p.prime]
  [char_p R p] (x y : R) (h : commute x y) :
  (x - y)^p = x^p - y^p :=
begin
  rw [eq_sub_iff_add_eq, ← add_pow_char_of_commute _ _ _ (commute.sub_left h rfl)],
  simp, repeat {apply_instance},
end

theorem sub_pow_char_pow_of_commute [ring R] {p : ℕ} [fact p.prime]
  [char_p R p] {n : ℕ} (x y : R) (h : commute x y) :
  (x - y) ^ (p ^ n) = x ^ (p ^ n) - y ^ (p ^ n) :=
begin
  induction n, { simp, },
  rw [pow_succ', pow_mul, pow_mul, pow_mul, n_ih],
  apply sub_pow_char_of_commute, apply commute.pow_pow h,
end

theorem add_pow_char [comm_semiring R] {p : ℕ} [fact p.prime]
  [char_p R p] (x y : R) : (x + y)^p = x^p + y^p :=
add_pow_char_of_commute _ _ _ (commute.all _ _)

theorem add_pow_char_pow [comm_semiring R] {p : ℕ} [fact p.prime]
  [char_p R p] {n : ℕ} (x y : R) :
  (x + y) ^ (p ^ n) = x ^ (p ^ n) + y ^ (p ^ n) :=
add_pow_char_pow_of_commute _ _ _ (commute.all _ _)

theorem sub_pow_char [comm_ring R] {p : ℕ} [fact p.prime]
  [char_p R p] (x y : R) : (x - y)^p = x^p - y^p :=
sub_pow_char_of_commute _ _ _ (commute.all _ _)

theorem sub_pow_char_pow [comm_ring R] {p : ℕ} [fact p.prime]
  [char_p R p] {n : ℕ} (x y : R) :
  (x - y) ^ (p ^ n) = x ^ (p ^ n) - y ^ (p ^ n) :=
sub_pow_char_pow_of_commute _ _ _ (commute.all _ _)

lemma eq_iff_modeq_int [ring R] (p : ℕ) [char_p R p] (a b : ℤ) :
  (a : R) = b ↔ a ≡ b [ZMOD p] :=
by rw [eq_comm, ←sub_eq_zero, ←int.cast_sub,
       char_p.int_cast_eq_zero_iff R p, int.modeq_iff_dvd]

lemma char_p.neg_one_ne_one [ring R] (p : ℕ) [char_p R p] [fact (2 < p)] :
  (-1 : R) ≠ (1 : R) :=
begin
  suffices : (2 : R) ≠ 0,
  { symmetry, rw [ne.def, ← sub_eq_zero, sub_neg_eq_add], exact this },
  assume h,
  rw [show (2 : R) = (2 : ℕ), by norm_cast] at h,
  have := (char_p.cast_eq_zero_iff R p 2).mp h,
  have := nat.le_of_dvd dec_trivial this,
  rw fact_iff at *, linarith,
end

lemma char_p.neg_one_pow_char [comm_ring R] (p : ℕ) [char_p R p] [fact p.prime] :
  (-1 : R) ^ p = -1 :=
begin
  rw eq_neg_iff_add_eq_zero,
  nth_rewrite 1 ← one_pow p,
  rw [← add_pow_char, add_left_neg, zero_pow (fact.out (nat.prime p)).pos],
end

lemma char_p.neg_one_pow_char_pow [comm_ring R] (p n : ℕ) [char_p R p] [fact p.prime] :
  (-1 : R) ^ p ^ n = -1 :=
begin
  rw eq_neg_iff_add_eq_zero,
  nth_rewrite 1 ← one_pow (p ^ n),
  rw [← add_pow_char_pow, add_left_neg, zero_pow (pow_pos (fact.out (nat.prime p)).pos _)],
end

lemma ring_hom.char_p_iff_char_p {K L : Type*} [division_ring K] [semiring L] [nontrivial L]
  (f : K →+* L) (p : ℕ) :
  char_p K p ↔ char_p L p :=
by simp only [char_p_iff, ← f.injective.eq_iff, map_nat_cast f, f.map_zero]

section frobenius

section comm_semiring

variables [comm_semiring R] {S : Type v} [comm_semiring S] (f : R →* S) (g : R →+* S)
  (p : ℕ) [fact p.prime] [char_p R p] [char_p S p] (x y : R)

/-- The frobenius map that sends x to x^p -/
def frobenius : R →+* R :=
{ to_fun := λ x, x^p,
  map_one' := one_pow p,
  map_mul' := λ x y, mul_pow x y p,
  map_zero' := zero_pow (fact.out (nat.prime p)).pos,
  map_add' := add_pow_char R }

variable {R}

theorem frobenius_def : frobenius R p x = x ^ p := rfl

theorem iterate_frobenius (n : ℕ) : (frobenius R p)^[n] x = x ^ p ^ n :=
begin
  induction n, {simp},
  rw [function.iterate_succ', pow_succ', pow_mul, function.comp_apply, frobenius_def, n_ih]
end

theorem frobenius_mul : frobenius R p (x * y) = frobenius R p x * frobenius R p y :=
(frobenius R p).map_mul x y

theorem frobenius_one : frobenius R p 1 = 1 := one_pow _

theorem monoid_hom.map_frobenius : f (frobenius R p x) = frobenius S p (f x) :=
f.map_pow x p

theorem ring_hom.map_frobenius : g (frobenius R p x) = frobenius S p (g x) :=
g.map_pow x p

theorem monoid_hom.map_iterate_frobenius (n : ℕ) :
  f (frobenius R p^[n] x) = (frobenius S p^[n] (f x)) :=
function.semiconj.iterate_right (f.map_frobenius p) n x

theorem ring_hom.map_iterate_frobenius (n : ℕ) :
  g (frobenius R p^[n] x) = (frobenius S p^[n] (g x)) :=
g.to_monoid_hom.map_iterate_frobenius p x n

theorem monoid_hom.iterate_map_frobenius (f : R →* R) (p : ℕ) [fact p.prime] [char_p R p] (n : ℕ) :
  f^[n] (frobenius R p x) = frobenius R p (f^[n] x) :=
f.iterate_map_pow _ _ _

theorem ring_hom.iterate_map_frobenius (f : R →+* R) (p : ℕ) [fact p.prime] [char_p R p] (n : ℕ) :
  f^[n] (frobenius R p x) = frobenius R p (f^[n] x) :=
f.iterate_map_pow _ _ _

variable (R)

theorem frobenius_zero : frobenius R p 0 = 0 := (frobenius R p).map_zero

theorem frobenius_add : frobenius R p (x + y) = frobenius R p x + frobenius R p y :=
(frobenius R p).map_add x y

theorem frobenius_nat_cast (n : ℕ) : frobenius R p n = n := map_nat_cast (frobenius R p) n

open_locale big_operators
variables {R}

lemma list_sum_pow_char (l : list R) : l.sum ^ p = (l.map (^ p)).sum :=
(frobenius R p).map_list_sum _

lemma multiset_sum_pow_char (s : multiset R) : s.sum ^ p = (s.map (^ p)).sum :=
(frobenius R p).map_multiset_sum _

lemma sum_pow_char {ι : Type*} (s : finset ι) (f : ι → R) :
  (∑ i in s, f i) ^ p = ∑ i in s, f i ^ p :=
(frobenius R p).map_sum _ _

end comm_semiring

section comm_ring

variables [comm_ring R] {S : Type v} [comm_ring S] (f : R →* S) (g : R →+* S)
  (p : ℕ) [fact p.prime] [char_p R p]  [char_p S p] (x y : R)

theorem frobenius_neg : frobenius R p (-x) = -frobenius R p x := (frobenius R p).map_neg x

theorem frobenius_sub : frobenius R p (x - y) = frobenius R p x - frobenius R p y :=
(frobenius R p).map_sub x y

end comm_ring

end frobenius

theorem frobenius_inj [comm_ring R] [is_reduced R]
  (p : ℕ) [fact p.prime] [char_p R p] :
  function.injective (frobenius R p) :=
λ x h H, by { rw ← sub_eq_zero at H ⊢, rw ← frobenius_sub at H, exact is_reduced.eq_zero _ ⟨_,H⟩ }

/-- If `ring_char R = 2`, where `R` is a finite reduced commutative ring,
then every `a : R` is a square. -/
lemma is_square_of_char_two' {R : Type*} [fintype R] [comm_ring R] [is_reduced R] [char_p R 2]
 (a : R) : is_square a :=
exists_imp_exists (λ b h, pow_two b ▸ eq.symm h) $
  ((fintype.bijective_iff_injective_and_card _).mpr ⟨frobenius_inj R 2, rfl⟩).surjective a

namespace char_p

section
variables [non_assoc_ring R]

lemma char_p_to_char_zero (R : Type*) [add_group_with_one R] [char_p R 0] :
  char_zero R :=
char_zero_of_inj_zero $
  λ n h0, eq_zero_of_zero_dvd ((cast_eq_zero_iff R 0 n).mp h0)

lemma cast_eq_mod (p : ℕ) [char_p R p] (k : ℕ) : (k : R) = (k % p : ℕ) :=
calc (k : R) = ↑(k % p + p * (k / p)) : by rw [nat.mod_add_div]
         ... = ↑(k % p)               : by simp [cast_eq_zero]

/-- The characteristic of a finite ring cannot be zero. -/
theorem char_ne_zero_of_fintype (p : ℕ) [hc : char_p R p] [fintype R] : p ≠ 0 :=
assume h : p = 0,
have char_zero R := @char_p_to_char_zero R _ (h ▸ hc),
absurd (@nat.cast_injective R _ this) (not_injective_infinite_fintype coe)

lemma ring_char_ne_zero_of_fintype [fintype R] : ring_char R ≠ 0 :=
char_ne_zero_of_fintype R (ring_char R)

end

section comm_ring

variables [comm_ring R] [is_reduced R] {R}

@[simp]
lemma pow_prime_pow_mul_eq_one_iff (p k m : ℕ) [fact p.prime]
  [char_p R p] (x : R) :
  x ^ (p ^ k * m) = 1 ↔ x ^ m = 1 :=
begin
  induction k with k hk,
  { rw [pow_zero, one_mul] },
  { refine ⟨λ h, _, λ h, _⟩,
    { rw [pow_succ, mul_assoc, pow_mul', ← frobenius_def, ← frobenius_one p] at h,
      exact hk.1 (frobenius_inj R p h) },
    { rw [pow_mul', h, one_pow] } }
end

end comm_ring

section semiring
open nat

variables [non_assoc_semiring R]

theorem char_ne_one [nontrivial R] (p : ℕ) [hc : char_p R p] : p ≠ 1 :=
assume hp : p = 1,
have ( 1 : R) = 0, by simpa using (cast_eq_zero_iff R p 1).mpr (hp ▸ dvd_refl p),
absurd this one_ne_zero

section no_zero_divisors

variable [no_zero_divisors R]

theorem char_is_prime_of_two_le (p : ℕ) [hc : char_p R p] (hp : 2 ≤ p) : nat.prime p :=
suffices ∀d ∣ p, d = 1 ∨ d = p, from nat.prime_def_lt''.mpr ⟨hp, this⟩,
assume (d : ℕ) (hdvd : ∃ e, p = d * e),
let ⟨e, hmul⟩ := hdvd in
have (p : R) = 0, from (cast_eq_zero_iff R p p).mpr (dvd_refl p),
have (d : R) * e = 0, from (@cast_mul R _ d e) ▸ (hmul ▸ this),
or.elim (eq_zero_or_eq_zero_of_mul_eq_zero this)
  (assume hd : (d : R) = 0,
  have p ∣ d, from (cast_eq_zero_iff R p d).mp hd,
  show d = 1 ∨ d = p, from or.inr (dvd_antisymm ⟨e, hmul⟩ this))
  (assume he : (e : R) = 0,
  have p ∣ e, from (cast_eq_zero_iff R p e).mp he,
  have e ∣ p, from dvd_of_mul_left_eq d (eq.symm hmul),
  have e = p, from dvd_antisymm ‹e ∣ p› ‹p ∣ e›,
  have h₀ : p > 0, from gt_of_ge_of_gt hp (nat.zero_lt_succ 1),
  have d * p = 1 * p, by rw ‹e = p› at hmul; rw [one_mul]; exact eq.symm hmul,
  show d = 1 ∨ d = p, from or.inl (eq_of_mul_eq_mul_right h₀ this))

section nontrivial

variables [nontrivial R]

theorem char_is_prime_or_zero (p : ℕ) [hc : char_p R p] : nat.prime p ∨ p = 0 :=
match p, hc with
| 0,     _  := or.inr rfl
| 1,     hc := absurd (eq.refl (1 : ℕ)) (@char_ne_one R _ _ (1 : ℕ) hc)
| (m+2), hc := or.inl (@char_is_prime_of_two_le R _ _ (m+2) hc (nat.le_add_left 2 m))
end

lemma char_is_prime_of_pos (p : ℕ) [h : fact (0 < p)] [char_p R p] : fact p.prime :=
⟨(char_p.char_is_prime_or_zero R _).resolve_right (pos_iff_ne_zero.1 h.1)⟩

end nontrivial

end no_zero_divisors

end semiring

section ring

variables (R) [ring R] [no_zero_divisors R] [nontrivial R] [fintype R]

theorem char_is_prime (p : ℕ) [char_p R p] :
  p.prime :=
or.resolve_right (char_is_prime_or_zero R p) (char_ne_zero_of_fintype R p)

end ring

section char_one

variables {R} [non_assoc_semiring R]

@[priority 100]  -- see Note [lower instance priority]
instance [char_p R 1] : subsingleton R :=
subsingleton.intro $
suffices ∀ (r : R), r = 0,
  from assume a b, show a = b, by rw [this a, this b],
assume r,
calc r = 1 * r       : by rw one_mul
   ... = (1 : ℕ) * r : by rw nat.cast_one
   ... = 0 * r       : by rw char_p.cast_eq_zero
   ... = 0           : by rw zero_mul

lemma false_of_nontrivial_of_char_one [nontrivial R] [char_p R 1] : false :=
false_of_nontrivial_of_subsingleton R

lemma ring_char_ne_one [nontrivial R] : ring_char R ≠ 1 :=
by { intros h, apply @zero_ne_one R, symmetry, rw [←nat.cast_one, ring_char.spec, h], }

lemma nontrivial_of_char_ne_one {v : ℕ} (hv : v ≠ 1) [hr : char_p R v] :
  nontrivial R :=
⟨⟨(1 : ℕ), 0, λ h, hv $ by rwa [char_p.cast_eq_zero_iff _ v, nat.dvd_one] at h; assumption ⟩⟩

lemma ring_char_of_prime_eq_zero [nontrivial R] {p : ℕ}
  (hprime : nat.prime p) (hp0 : (p : R) = 0) : ring_char R = p :=
or.resolve_left ((nat.dvd_prime hprime).1 (ring_char.dvd hp0)) ring_char_ne_one

end char_one

end char_p

section

/-- We have `2 ≠ 0` in a nontrivial ring whose characteristic is not `2`. -/
-- Note: there is `two_ne_zero` (assuming `[ordered_semiring]`)
-- and `two_ne_zero'`(assuming `[char_zero]`), which both don't fit the needs here.
@[protected]
lemma ring.two_ne_zero {R : Type*} [non_assoc_semiring R] [nontrivial R] (hR : ring_char R ≠ 2) :
  (2 : R) ≠ 0 :=
begin
  rw [ne.def, (by norm_cast : (2 : R) = (2 : ℕ)), ring_char.spec, nat.dvd_prime nat.prime_two],
  exact mt (or_iff_left hR).mp char_p.ring_char_ne_one,
end

/-- Characteristic `≠ 2` and nontrivial implies that `-1 ≠ 1`. -/
-- We have `char_p.neg_one_ne_one`, which assumes `[ring R] (p : ℕ) [char_p R p] [fact (2 < p)]`.
-- This is a version using `ring_char` instead.
lemma ring.neg_one_ne_one_of_char_ne_two {R : Type*} [non_assoc_ring R] [nontrivial R]
 (hR : ring_char R ≠ 2) :
  (-1 : R) ≠ 1 :=
λ h, ring.two_ne_zero hR (neg_eq_iff_add_eq_zero.mp h)

/-- Characteristic `≠ 2` in a domain implies that `-a = a` iff `a = 0`. -/
lemma ring.eq_self_iff_eq_zero_of_char_ne_two {R : Type*} [non_assoc_ring R] [nontrivial R]
 [no_zero_divisors R] (hR : ring_char R ≠ 2) {a : R} :
  -a = a ↔ a = 0 :=
⟨λ h, (mul_eq_zero.mp $ (two_mul a).trans $ neg_eq_iff_add_eq_zero.mp h).resolve_left
         (ring.two_ne_zero hR),
 λ h, ((congr_arg (λ x, - x) h).trans neg_zero).trans h.symm⟩

end

section

variables (R) [non_assoc_ring R] [fintype R] (n : ℕ)

lemma char_p_of_ne_zero (hn : fintype.card R = n) (hR : ∀ i < n, (i : R) = 0 → i = 0) :
  char_p R n :=
{ cast_eq_zero_iff :=
  begin
    have H : (n : R) = 0, by { rw [← hn, char_p.cast_card_eq_zero] },
    intro k,
    split,
    { intro h,
      rw [← nat.mod_add_div k n, nat.cast_add, nat.cast_mul, H, zero_mul, add_zero] at h,
      rw nat.dvd_iff_mod_eq_zero,
      apply hR _ (nat.mod_lt _ _) h,
      rw [← hn, fintype.card_pos_iff],
      exact ⟨0⟩, },
    { rintro ⟨k, rfl⟩, rw [nat.cast_mul, H, zero_mul] }
  end }

lemma char_p_of_prime_pow_injective (R) [ring R] [fintype R] (p : ℕ) [hp : fact p.prime] (n : ℕ)
  (hn : fintype.card R = p ^ n) (hR : ∀ i ≤ n, (p ^ i : R) = 0 → i = n) :
  char_p R (p ^ n) :=
begin
  obtain ⟨c, hc⟩ := char_p.exists R, resetI,
  have hcpn : c ∣ p ^ n,
  { rw [← char_p.cast_eq_zero_iff R c, ← hn, char_p.cast_card_eq_zero], },
  obtain ⟨i, hi, hc⟩ : ∃ i ≤ n, c = p ^ i, by rwa nat.dvd_prime_pow hp.1 at hcpn,
  obtain rfl : i = n,
  { apply hR i hi, rw [← nat.cast_pow, ← hc, char_p.cast_eq_zero] },
  rwa ← hc
end

end

section prod

variables (S : Type v) [semiring R] [semiring S] (p q : ℕ) [char_p R p]

/-- The characteristic of the product of rings is the least common multiple of the
characteristics of the two rings. -/
instance [char_p S q] : char_p (R × S) (nat.lcm p q) :=
{ cast_eq_zero_iff :=
    by simp [prod.ext_iff, char_p.cast_eq_zero_iff R p,
      char_p.cast_eq_zero_iff S q, nat.lcm_dvd_iff] }

/-- The characteristic of the product of two rings of the same characteristic
  is the same as the characteristic of the rings -/
instance prod.char_p [char_p S p] : char_p (R × S) p :=
by convert nat.lcm.char_p R S p p; simp

end prod

section

/-- If two integers from `{0, 1, -1}` result in equal elements in a ring `R`
that is nontrivial and of characteristic not `2`, then they are equal. -/
lemma int.cast_inj_on_of_ring_char_ne_two {R : Type*} [non_assoc_ring R] [nontrivial R]
  (hR : ring_char R ≠ 2) :
  ({0, 1, -1} : set ℤ).inj_on (coe : ℤ → R) :=
begin
  intros a ha b hb h,
  apply eq_of_sub_eq_zero,
  by_contra hf,
  change a = 0 ∨ a = 1 ∨ a = -1 at ha,
  change b = 0 ∨ b = 1 ∨ b = -1 at hb,
  have hh : a - b = 1 ∨ b - a = 1 ∨ a - b = 2 ∨ b - a = 2 := by
  { rcases ha with ha | ha | ha; rcases hb with hb | hb | hb,
    swap 5, swap 9, -- move goals with `a = b` to the front
    iterate 3 { rw [ha, hb, sub_self] at hf, tauto, }, -- 6 goals remain
    all_goals { rw [ha, hb], norm_num, }, },
  have h' : ((a - b : ℤ) : R) = 0 := by exact_mod_cast sub_eq_zero_of_eq h,
  have h'' : ((b - a : ℤ) : R) = 0 := by exact_mod_cast sub_eq_zero_of_eq h.symm,
  rcases hh with hh | hh | hh | hh,
  { rw [hh, (by norm_cast : ((1 : ℤ) : R) = 1)] at h', exact one_ne_zero h', },
  { rw [hh, (by norm_cast : ((1 : ℤ) : R) = 1)] at h'', exact one_ne_zero h'', },
  { rw [hh, (by norm_cast : ((2 : ℤ) : R) = 2)] at h', exact ring.two_ne_zero hR h', },
  { rw [hh, (by norm_cast : ((2 : ℤ) : R) = 2)] at h'', exact ring.two_ne_zero hR h'', },
end

end