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.
Proof
fun x
=> let H
: (0 # x) + (0 # x) = (0 # x) + 0
:= eq_refl (0 # x)
|| a # x = 0 # x         @a by (sum_id_l 0)
|| a = 0 # x             @a by <- prod_sum_distrib_r x 0 0
|| (0 # x) + (0 # x) = a @a by sum_id_r (0 # x)
in sum_cancel_l (0 # x) 0 (0 # x) H.
Proof
fun x
=> let H
:  (x # 0) + (x # 0) = 0 + (x # 0)
:= eq_refl (x # 0)
|| x # a = x # 0         @a by sum_id_r 0
|| a = x # 0             @a by <- prod_sum_distrib_l x 0 0
|| (x # 0) + (x # 0) = a @a by sum_id_l (x # 0)
in sum_cancel_r (x # 0) 0 (x # 0) H.
Proof
ex_ind
(fun x (H : x # 0 = 1)
=> distinct_0_1 (H || a = 1 @a by <- prod_0_r x)).
Proof
ex_ind
(fun x (H : 0 # x = 1)
=> distinct_0_1 (H || a = 1 @a by <- prod_0_l x)).
Proof
ex_ind
(fun x H
=> prod_0_inv_l
(ex_intro
(fun x
=> prod_is_inv_l 0 x)
x (proj1 H))).
Proof
fun x y H (H0 : y = 0)
=> distinct_0_1
(proj1 H
|| a # x = 1 @a by <- H0
|| a = 1     @a by <- prod_0_l x).
(** Represents -1 and proves that it exists. *)
Definition E_n1_strong
:  { x : E | sum_is_inv 1 x }
:= constructive_definite_description (sum_is_inv 1) (sum_inv_uniq_ex 1).
(** Represents -1. *)
Definition E_n1 : E := proj1_sig E_n1_strong.
(** Defines a symbolic representation for -1  Note: here we represent the inverse of 1 rather than the negation of 1. Letter we prove that the negation equals the inverse.  Note: brackets are needed to ensure Coq parses the symbol as a single token instead of a prefixed function call. *)
Notation "{-1}" := E_n1 : ring_scope.
Proof proj2_sig E_n1_strong.
Proof proj1 E_n1_def.
Proof proj2 E_n1_def.
Proof fun x => sum_inv_uniq 1 {-1} x E_n1_def.
Proof
fun x
=> prod_0_l x
|| a # x = 0          @a by E_n1_inv_l
|| a = 0              @a by <- prod_sum_distrib_r x {-1} 1
|| ({-1} # x) + a = 0 @a by <- prod_id_l x.
Proof
fun x
=> prod_0_r x
|| x # a = 0          @a by E_n1_inv_l
|| a = 0              @a by <- prod_sum_distrib_l x {-1} 1
|| (x # {-1}) + a = 0 @a by <- prod_id_r x.
Proof
fun x
=> prod_0_l x
|| a # x = 0          @a by E_n1_inv_r
|| a = 0              @a by <- prod_sum_distrib_r x 1 {-1}
|| a + ({-1} # x) = 0 @a by <- prod_id_l x.
Proof
fun x
=> prod_0_r x
|| x # a = 0          @a by E_n1_inv_r
|| a = 0              @a by <- prod_sum_distrib_l x 1 {-1}
|| a + (x # {-1}) = 0 @a by <- prod_id_r x.

(** Proves that -1 x is the additive inverse of x. *)
Theorem prod_n1_x_inv
:  forall x : E, sum_is_inv x ({-1} # x).