Section Theorems.
(** Represents an arbitrary ring.  Note: we use Variable rather than Parameter to ensure that the following theorems are generalized w.r.t r. *)
Variable r : Ring.
(** Represents the set of group elements.  Note: We use Let to define E as a local abbreviation. *)
Let E := E r.
(** A predicate that accepts one element, x, and asserts that x is nonzero. *)
Definition nonzero (x : E) : Prop := x <> 0.
(** Accepts one ring element, x, and asserts that x is the left identity element. *)
Definition sum_is_id_l := Monoid.is_id_l E sum.
(** Accepts one ring element, x, and asserts that x is the right identity element. *)
Definition sum_is_id_r := Monoid.is_id_r E sum.
(** Accepts one ring element, x, and asserts that x is the identity element. *)
Definition sum_is_id := Monoid.is_id E sum.
(** Represents the abelian group formed by addition over E. *)
Definition sum_abelian_group
:= Abelian_Group.abelian_group E 0 {+} sum_is_assoc sum_is_comm sum_id_l sum_inv_l_ex.
(** Represents the group formed by addition over E. *)
Definition sum_group
:= Abelian_Group.op_group sum_abelian_group.
(** Represents the monoid formed by addition over E. *)
Definition sum_monoid
:= Abelian_Group.op_monoid sum_abelian_group.
Proof Abelian_Group.op_id_r sum_abelian_group.
Proof Abelian_Group.op_id sum_abelian_group.
(** Accepts two elements, x and y, and asserts that y is x's left inverse. *)
Definition sum_is_inv_l
:= Abelian_Group.op_is_inv_l sum_abelian_group.
(** Accepts two elements, x and y, and asserts that y is x's right inverse. *)
Definition sum_is_inv_r
:= Abelian_Group.op_is_inv_r sum_abelian_group.
(** Accepts two elements, x and y, and asserts that y is x's inverse. *)
Definition sum_is_inv
:= Abelian_Group.op_is_inv sum_abelian_group.
(** Accepts one argument, x, and asserts that x has a left inverse. *)
Definition sum_has_inv_l := Abelian_Group.has_inv_l sum_abelian_group.
(** Accepts one argument, x, and asserts that x has a right inverse. *)
Definition sum_has_inv_r := Abelian_Group.has_inv_r sum_abelian_group.
(** Accepts one argument, x, and asserts that x has an inverse. *)
Definition sum_has_inv := Abelian_Group.has_inv sum_abelian_group.
Proof Abelian_Group.op_inv_r_ex sum_abelian_group.
Proof Abelian_Group.op_id_l_uniq sum_abelian_group.
Proof Abelian_Group.op_id_r_uniq sum_abelian_group.
Proof Abelian_Group.op_id_uniq sum_abelian_group.
Proof Abelian_Group.op_inv_l_r_eq sum_abelian_group.
Proof Abelian_Group.op_inv_sym sum_abelian_group.
Proof Abelian_Group.op_inv_uniq sum_abelian_group.
Proof Abelian_Group.op_inv_ex sum_abelian_group.
Proof Abelian_Group.op_inv_uniq_ex sum_abelian_group.
Proof Abelian_Group.op_intro_l sum_abelian_group.
Proof Abelian_Group.op_intro_r sum_abelian_group.
Proof Abelian_Group.op_cancel_l sum_abelian_group.
Proof Abelian_Group.op_cancel_r sum_abelian_group.
Proof Abelian_Group.op_inv_l_uniq sum_abelian_group.
Proof Abelian_Group.op_inv_r_uniq sum_abelian_group.
Proof Abelian_Group.op_inv_0_l sum_abelian_group.
Proof Abelian_Group.op_inv_0_r sum_abelian_group.
Proof Abelian_Group.op_inv_0 sum_abelian_group.
Proof Abelian_Group.op_has_inv_l_0 sum_abelian_group.
Proof Abelian_Group.op_has_inv_r_0 sum_abelian_group.
Proof Abelian_Group.op_has_inv_0 sum_abelian_group.
Proof Abelian_Group.op_inv_0_eq_0 sum_abelian_group.
Proof Abelian_Group.op_inv_0_uniq sum_abelian_group.
(** Represents strongly-specified negation. *)
Definition sum_neg_strong
:  forall x : E, { y | sum_is_inv x y }
:= Abelian_Group.op_neg_strong sum_abelian_group.
(** Represents negation. *)
Definition sum_neg
:  E -> E
:= Abelian_Group.op_neg sum_abelian_group.
Notation "{-}" := (sum_neg) : ring_scope.
Notation "- x" := (sum_neg x) : ring_scope.
(** Asserts that the negation returns the inverse of its argument. *)
Definition sum_neg_def
:  forall x : E, sum_is_inv x (- x)
:= Abelian_Group.op_neg_def sum_abelian_group.
(** Proves that negation is one-to-one *)
Definition sum_neg_inj
:  is_injective E E {-}
:= Abelian_Group.op_neg_inj sum_abelian_group.
Proof Abelian_Group.op_cancel_neg sum_abelian_group.
Proof Abelian_Group.op_neg_onto sum_abelian_group.
Proof Abelian_Group.op_neg_bijective sum_abelian_group.
Proof Abelian_Group.op_neg_rev sum_abelian_group.
Proof Abelian_Group.op_neg_distrib_inv_l sum_abelian_group.
Proof Abelian_Group.op_neg_distrib_inv_r sum_abelian_group.
Proof Abelian_Group.op_neg_distrib_inv sum_abelian_group.
Proof Abelian_Group.op_neg_distrib sum_abelian_group.
Proof
proj2 (sum_neg_def 0)
|| a = 0 @a by <- sum_id_l (- 0).
Proof
fun x H
=> proj2 (sum_neg_def x)
|| x + a = 0 @a by <- H
|| a = 0     @a by <- sum_id_r x.
Proof
conj sum_0_neg
(fun x H => eq_sym (sum_neg_0 x H)).
(** Accepts one element, x, and asserts that x is the left identity element. *)
Definition prod_is_id_l := Monoid.is_id_l E prod.
(** Accepts one element, x, and asserts that x is the right identity element. *)
Definition prod_is_id_r := Monoid.is_id_r E prod.
(** Accepts one element, x, and asserts that x is the identity element. *)
Definition prod_is_id := Monoid.is_id E prod.
(** Represents the monoid formed by op over E. *)
Definition prod_monoid := Monoid.monoid E 1 {#} prod_is_assoc prod_id_l prod_id_r.
Proof Monoid.op_id prod_monoid.
Proof Monoid.op_id_l_uniq prod_monoid.
Proof Monoid.op_id_r_uniq prod_monoid.
Proof Monoid.op_id_uniq prod_monoid.
Proof Monoid.op_intro_l prod_monoid.
Proof Monoid.op_intro_r prod_monoid.
(** Accepts two elements, x and y, and asserts that y is x's left inverse. *)
Definition prod_is_inv_l := Monoid.op_is_inv_l prod_monoid.
(** Accepts two elements, x and y, and asserts that y is x's right inverse. *)
Definition prod_is_inv_r := Monoid.op_is_inv_r prod_monoid.
(** Accepts two elements, x and y, and asserts that y is x's inverse. *)
Definition prod_is_inv := Monoid.op_is_inv prod_monoid.
(** Accepts one argument, x, and asserts that x has a left inverse. *)
Definition prod_has_inv_l := Monoid.has_inv_l prod_monoid.
(** Accepts one argument, x, and asserts that x has a right inverse. *)
Definition prod_has_inv_r := Monoid.has_inv_r prod_monoid.
(** Accepts one argument, x, and asserts that x has an inverse. *)
Definition prod_has_inv := Monoid.has_inv prod_monoid.
Proof Monoid.op_inv_l_r_eq prod_monoid.
Proof Monoid.op_inv_sym prod_monoid.
Proof Monoid.op_cancel_l prod_monoid.
Proof Monoid.op_cancel_r prod_monoid.
Proof Monoid.op_inv_l_uniq prod_monoid.
Proof Monoid.op_inv_r_uniq prod_monoid.
(** Proves that an element's inverse is unique.  Note: this theorem is defined as transparent to allow a theorem in the field module to compile. *)
Definition prod_inv_uniq
:  forall x y z : E, prod_is_inv x y -> prod_is_inv x z -> z = y
:= Monoid.op_inv_uniq prod_monoid.
Proof Monoid.op_inv_0_l prod_monoid.
Proof Monoid.op_inv_0_l prod_monoid.
Proof Monoid.op_inv_0 prod_monoid.
Proof Monoid.op_has_inv_l_0 prod_monoid.
Proof Monoid.op_has_inv_r_0 prod_monoid.
Proof Monoid.op_has_inv_0 prod_monoid.
Proof Monoid.op_inv_0_eq_0 prod_monoid.
Proof Monoid.op_inv_0_uniq prod_monoid.
Proof Monoid.op_inv_0_l prod_monoid.
Proof Monoid.op_inv_0_r prod_monoid.
Proof Monoid.op_inv_0 prod_monoid.
(** TODO Reciprical functions (op_neg) from Monoid. *)
Proof conj prod_sum_distrib_l prod_sum_distrib_r.

(** Proves that 0 times every number equals 0.  0 x = 0 x (0 + 0) x = 0 x 0 x + 0 x = 0 x 0 x = 0 *)
Theorem prod_0_l
:  forall x : E, 0 # x = 0.