(** This module defines the Monoid record type which represents algebraic structures called Monoids and provides a collection of theorems and axioms 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 Monoid.
(** Accepts a function, f, and asserts that f is associative. *)
Definition is_assoc (T : Type) (f : T -> T -> T) : Prop := forall x y z : T, f x (f y z) = f (f x y) z.
(** Accepts two arguments, f and x, and asserts that x is a left identity element w.r.t. f. *)
Definition is_id_l (T : Type) (f : T -> T -> T) (E : T) : Prop := forall x : T, f E x = x.
(** Accepts two arguments, f and x, and asserts that x is a right identity element w.r.t. f. *)
Definition is_id_r (T : Type) (f : T -> T -> T) (E : T) : Prop := forall x : T, f x E = x.
(** Accepts two arguments, f and x, and asserts that x is an identity element w.r.t. f. *)
Definition is_id (T : Type) (f : T -> T -> T) (E : T) : Prop := is_id_l T f E /\ is_id_r T f E.
(** Accepts three arguments, f, e, and H, where H proves that e is the identity element w.r.t. f, and returns a function that accepts two arguments, x and y, and asserts that y is x's left inverse. *)
Definition is_inv_l (T : Type) (f : T -> T -> T) (E : T) (_ : is_id T f E) (x y : T) : Prop := f y x = E.
(** Accepts three arguments, f, e, and H, where H proves that e is the identity element w.r.t. f, and returns a function that accepts two arguments, x and y, and asserts that y is x's right inverse. *)
Definition is_inv_r (T : Type) (f : T -> T -> T) (E : T) (_ : is_id T f E) (x y : T) : Prop := f x y = E.
(** Accepts three arguments, f, e, and H, where H proves that e is the identity element w.r.t. f, and returns a function that accepts two arguments, x and y, and asserts that y is x's inverse. *)
Definition is_inv (T : Type) (f : T -> T -> T) (E : T) (H : is_id T f E) (x y : T) : Prop := is_inv_l T f E H x y /\ is_inv_r T f E H x y.
(** Represents algebraic monoids. *)
Structure Monoid : Type := monoid {
(** Represents the set of monoid elements. *)
E: Set;
(** Represents the identity element. *)
E_0: E;
(** Represents the monoid operation. *)
op: E -> E -> E;
(** Asserts that the monoid operator is associative. *)
op_is_assoc : is_assoc E op;
(** Asserts that E_0 is the left identity element. *)
op_id_l : is_id_l E op E_0;
(** Asserts that E_0 is the right identity element. *)
op_id_r : is_id_r E op E_0
}.
(** Enable implicit arguments for monoid properties. *)
Arguments E_0 {m}.
Arguments op {m} x y.
Arguments op_is_assoc {m} x y z.
Arguments op_id_l {m} x.
Arguments op_id_r {m} x.
(** Define notations for monoid properties. *)
Notation "0" := E_0 : monoid_scope.
Notation "x + y" := (op x y) (at level 50, left associativity) : monoid_scope.
Notation "{+}" := op : monoid_scope.
Open Scope monoid_scope.
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 {+}.

(* Specifications: *)
(** Proves that 0 is the identity element. *)
Theorem op_id
:  is_id M {+} 0.