Section Theorems.
(** Represents an arbitrary monoid.  Note: we use Variable rather than Parameter to ensure that the following theorems are generalized w.r.t m. *)
Variable m : Monoid.
(** Represents the set of monoid elements. *)
Let M := E m.
(** Accepts one monoid element, x, and asserts that x is the left identity element. *)
Definition op_is_id_l := is_id_l M {+}.
(** Accepts one monoid element, x, and asserts that x is the right identity element. *)
Definition op_is_id_r := is_id_r M {+}.
(** Accepts one monoid element, x, and asserts that x is the identity element. *)
Definition op_is_id := is_id M {+}.
Proof conj op_id_l op_id_r.
Proof
fun x H
=> H 0 || a = 0 @a by <- op_id_r x.
Proof
fun x H
=> H 0 || a = 0 @a by <- op_id_l x.
Proof
fun x
=> and_ind (fun H _ => op_id_l_uniq x H).
Proof
fun x y z H
=> f_equal ({+} z) H.
Proof
fun x y z H
=>  eq_refl (x + z)
|| x + z = a + z @a by <- H.
(** Accepts two monoid elements, x and y, and asserts  that y is x's left inverse. *)
Definition op_is_inv_l := is_inv_l M {+} 0 op_id.
(** Accepts two monoid elements, x and y, and asserts  that y is x's right inverse. *)
Definition op_is_inv_r := is_inv_r M {+} 0 op_id.
(** Accepts two monoid elements, x and y, and asserts  that y is x's inverse. *)
Definition op_is_inv := is_inv M {+} 0 op_id.
(** Accepts one argument, x, and asserts that x has a left inverse. *)
Definition has_inv_l
:= fun x => exists y : M, op_is_inv_l x y.
(** Accepts one argument, x, and asserts that x has a right inverse. *)
Definition has_inv_r
:= fun x => exists y : M, op_is_inv_r x y.
(** Accepts one argument, x, and asserts that x has an inverse. *)
Definition has_inv
:= fun x => exists y : M, op_is_inv x y.

(** Proves that the left and right inverses of an element must be equal. *)
Theorem op_inv_l_r_eq
:  forall x y : M, op_is_inv_l x y -> forall z : M, op_is_inv_r x z -> y = z.