Section Theorems.
(** Represents an arbitrary abelian group.  Note: we use Variable rather than Parameter to ensure that the following theorems are generalized w.r.t ag. *)
Variable g : Abelian_Group.
(** Represents the set of group elements.  Note: We use Let to define E as a local abbreviation. *)
Let E := E g.
(** Accepts one group element, x, and asserts that x is the left identity element. *)
Definition op_is_id_l := Monoid.is_id_l E {+}.
(** Accepts one group element, x, and asserts that x is the right identity element. *)
Definition op_is_id_r := Monoid.is_id_r E {+}.
(** Accepts one group element, x, and asserts that x is the identity element. *)
Definition op_is_id := Monoid.is_id E {+}.
Proof
fun x H y
=> H y
|| a = y @a by op_is_comm y x.
Proof
fun x H
=> conj H (op_is_id_lr x H).
Proof op_is_id_lr 0 op_id_l.
Proof conj op_id_l op_id_r.
(** Accepts two group elements, x and y, and asserts that y is x's left inverse. *)
Definition op_is_inv_l := Monoid.is_inv_l E {+} 0 op_id.
(** Accepts two group elements, x and y, and asserts that y is x's right inverse. *)
Definition op_is_inv_r := Monoid.is_inv_r E {+} 0 op_id.
(** Accepts two group elements, x and y, and asserts that y is x's inverse. *)
Definition op_is_inv := Monoid.is_inv E {+} 0 op_id.
Proof
fun x
=> ex_ind
(fun (y : E) (H : op_is_inv_l x y)
=> ex_intro
(op_is_inv_r x)
y
(H || a = 0 @a by op_is_comm x y))
(op_inv_l_ex x).
(** Represents the group structure formed by op over E. *)
Definition op_group := Group.group E 0 {+} op_is_assoc op_id_l op_id_r op_inv_l_ex op_inv_r_ex.
(** Represents the monoid formed by op over E. *)
Definition op_monoid := Group.op_monoid op_group.
(** Accepts one argument, x, and asserts that x has a left inverse. *)
Definition has_inv_l := Group.has_inv_l op_group.
(** Accepts one argument, x, and asserts that x has a right inverse. *)
Definition has_inv_r := Group.has_inv_r op_group.
(** Accepts one argument, x, and asserts that x has an inverse. *)
Definition has_inv := Group.has_inv op_group.
Proof Group.op_id_l_uniq op_group.
Proof Group.op_id_r_uniq op_group.
Proof Group.op_id_uniq op_group.
Proof Group.op_inv_l_r_eq op_group.
Proof Group.op_inv_sym op_group.
Proof Group.op_inv_ex op_group.
Proof Group.op_intro_l op_group.
Proof Group.op_intro_r op_group.
Proof Group.op_cancel_l op_group.
Proof Group.op_cancel_r op_group.
Proof Group.op_inv_l_uniq op_group.
Proof Group.op_inv_r_uniq op_group.
Proof Group.op_inv_uniq op_group.
Proof Group.op_inv_uniq_ex op_group.
Proof Group.op_inv_0_l op_group.
Proof Group.op_inv_0_r op_group.
Proof Group.op_inv_0 op_group.
Proof Group.op_has_inv_l_0 op_group.
Proof Group.op_has_inv_r_0 op_group.
Proof Group.op_has_inv_0 op_group.
Proof Group.op_inv_0_eq_0 op_group.

(** Proves that 0 is the only element whose inverse is 0. *)
Theorem op_inv_0_uniq
:  unique (fun x => op_is_inv x 0) 0.