Section Theorems.
(** Represents an arbitrary group.  Note: we use Variable rather than Parameter to ensure that the following theorems are generalized w.r.t g. *)
Variable g : Group.
(** Represents the set of group elements. *)
Let E := E g.
(** Represents the monoid structure formed by op over E. *)
Definition op_monoid := Monoid.monoid E 0 {+} op_is_assoc op_id_l op_id_r.
(** Accepts one group element, x, and asserts that x is the left identity element. *)
Definition op_is_id_l := Monoid.op_is_id_l op_monoid.
(** Accepts one group element, x, and asserts that x is the right identity element. *)
Definition op_is_id_r := Monoid.op_is_id_r op_monoid.
(** Accepts one group element, x, and asserts that x is the identity element. *)
Definition op_is_id := Monoid.op_is_id op_monoid.
Proof Monoid.op_id op_monoid.
(** Accepts two group elements, x and y, and asserts that y is x's left inverse. *)
Definition op_is_inv_l := Monoid.op_is_inv_l op_monoid.
(** Accepts two group elements, x and y, and asserts that y is x's right inverse. *)
Definition op_is_inv_r := Monoid.op_is_inv_r op_monoid.
Proof Monoid.op_id_l_uniq op_monoid.
Proof Monoid.op_id_r_uniq op_monoid.
Proof Monoid.op_id_uniq op_monoid.
Proof Monoid.op_intro_l op_monoid.
Proof Monoid.op_intro_r op_monoid.
(** Accepts two group elements, x and y, and asserts that y is x's inverse. *)
Definition op_is_inv := Monoid.op_is_inv op_monoid.
(** Accepts one argument, x, and asserts that x has a left inverse. *)
Definition has_inv_l := Monoid.has_inv_l op_monoid.
(** Accepts one argument, x, and asserts that x has a right inverse. *)
Definition has_inv_r := Monoid.has_inv_r op_monoid.
(** Accepts one argument, x, and asserts that x has an inverse. *)
Definition has_inv := Monoid.has_inv op_monoid.
Proof Monoid.op_inv_l_r_eq op_monoid.
Proof Monoid.op_inv_sym op_monoid.
Proof
fun x : E
=> ex_ind
(fun y H
=> ex_ind
(fun z H0
=> let H1
:  op_is_inv_r x y
:=  H0
|| op_is_inv_r x a @a
by op_inv_l_r_eq x y H z H0 in
ex_intro
(fun a => op_is_inv x a)
y
(conj H H1))
(op_inv_r_ex x))
(op_inv_l_ex x).
Proof
fun x y z H
=> Monoid.op_cancel_l op_monoid x y z (op_inv_l_ex z) H.
Proof
fun x y z
=> Monoid.op_cancel_r op_monoid x y z (op_inv_r_ex z).
Proof
fun x
=> Monoid.op_inv_l_uniq op_monoid x (op_inv_r_ex x).
Proof
fun x
=> Monoid.op_inv_r_uniq op_monoid x (op_inv_l_ex x).
Proof Monoid.op_inv_uniq op_monoid.
Proof
fun x
=> ex_ind
(fun y (H : op_is_inv x y)
=> ex_intro
(fun y => op_is_inv x y /\ forall z, op_is_inv x z -> y = z)
y
(conj H (fun z H0 => eq_sym (op_inv_uniq x y z H H0))))
(op_inv_ex x).
Proof Monoid.op_inv_0_l op_monoid.
Proof Monoid.op_inv_0_r op_monoid.
Proof Monoid.op_inv_0 op_monoid.
Proof Monoid.op_has_inv_l_0 op_monoid.
Proof Monoid.op_has_inv_r_0 op_monoid.
Proof Monoid.op_has_inv_0 op_monoid.
Proof Monoid.op_inv_0_eq_0 op_monoid.
Proof Monoid.op_inv_0_uniq op_monoid.
(** Represents strongly-specified negation. *)
Definition op_neg_strong
:  forall x : E, { y | op_is_inv x y }
:= fun x => Monoid.op_neg_strong op_monoid x (op_inv_ex x).
(** Represents negation. *)
Definition op_neg
:  E -> E
:= fun x => Monoid.op_neg op_monoid x (op_inv_ex x).
Notation "{-}" := (op_neg) : group_scope.
Notation "- x" := (op_neg x) : group_scope.
Proof
fun x
=> Monoid.op_neg_def op_monoid x (op_inv_ex x).
Proof
fun x y
=> Monoid.op_neg_inj op_monoid x (op_inv_ex x) y (op_inv_ex y).
Proof
fun x
=> Monoid.op_cancel_neg_gen op_monoid x (op_inv_ex x) (op_inv_ex (- x)).
Proof
fun x => ex_intro (fun y => - y = x) (- x) (op_cancel_neg x).
Proof
conj op_neg_inj op_neg_onto.
Proof
fun x y H
=> eq_sym
(f_equal {-} H
|| a = - y @a by <- op_cancel_neg x).
Proof
fun x y
=> ((proj2 (op_neg_def (- y)))
|| - y + a = 0                 @a by <- op_cancel_neg y
|| - y + a = 0                 @a by op_id_l y
|| - y + (a + y) = 0           @a by proj2 (op_neg_def (- x))
|| - y + ((- x + a) + y) = 0 @a by <- op_cancel_neg x
|| - y + a = 0                 @a by op_is_assoc (- x) x y
|| a = 0                         @a by <- op_is_assoc (- y) (- x) (x + y)).
Proof
fun x y
=> ((proj2 (op_neg_def x))
|| x + a = 0           @a by op_id_l (- x)
|| x + (a + - x) = 0 @a by proj2 (op_neg_def y)
|| x + a = 0           @a by op_is_assoc y (- y) (- x)
|| a = 0               @a by <- op_is_assoc x y (- y + - x)).
Proof
fun x y
=> conj
(op_neg_distrib_inv_l x y)
(op_neg_distrib_inv_r x y).

(** Proves that negation is distributive: i.e. -(x + y) = -y + -x. *)
Theorem op_neg_distrib
:  forall x y : E, - (x + y) = - y + - x.