(** This module defines the Abelian Group record type which can be used to represent abelian groups and provides a collection of axioms and theorems describing them.  Copyright (C) 2018 Larry D. Lee Jr. <llee454@gmail.com>  This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.  You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. *)
Module Abelian_Group.
(** Accepts one argument: f, a binary function; and asserts that f is commutative. *)
Definition is_comm (T : Type) (f : T -> T -> T)
:  Prop
:= forall x y : T, f x y = f y x.
(** Represents algebraic abelian groups. *)
Structure Abelian_Group : Type := abelian_group {
(** Represents the set of group elements. *)
E : Set;
(** Represents the identity element. *)
E_0 : E;
(** Represents the group operation. *)
op : E -> E -> E;
(** Asserts that the group operator is associative. *)
op_is_assoc : Monoid.is_assoc E op;
(** Asserts that the group operator is commutative. *)
op_is_comm : is_comm E op;
(** Asserts that E_0 is the left identity element. *)
op_id_l : Monoid.is_id_l E op E_0;
(** Asserts that every element has a left inverse.  Strictly speaking, this axiom should be:  forall x : E, exists y : E, Monoid.is_inv_l E op E_0 op_id x y  which asserts and verifies that op y x equals the identity element. Technically, we haven't shown that E_0 is the identity element yet, so we're being a bit presumptuous defining inverses in this way. While we could prove op_id in this structure definition, we prefer not to to improve readability and instead use the form given below, which Monoid.is_inv_l reduces to anyway. *)
op_inv_l_ex : forall x : E, exists y : E, op y x = E_0
}.
(** Enable implicit arguments for group properties. *)
Arguments E_0 {a}.
Arguments op {a} x y.
Arguments op_is_assoc {a} x y z.
Arguments op_is_comm {a} x y.
Arguments op_id_l {a} x.
Arguments op_inv_l_ex {a} x.
(** Define notations for group properties. *)
Notation "0" := E_0 : abelian_group_scope.
Notation "x + y" := (op x y) (at level 50, left associativity) : abelian_group_scope.
Notation "{+}" := op : abelian_group_scope.
Open Scope abelian_group_scope.
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 {+}.

(** Proves that every left identity must also be a right identity. *)
Theorem op_is_id_lr
:  forall x : E, op_is_id_l x -> op_is_id_r x.