(** 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
}.(** 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
}.(** 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
}.(** 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
}.(** 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
}.(** 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
}.(** 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
}.(** 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
}.(**
  Coq does not export notations outside of
  sections. Consequently the notationsdefined
  above are not visible to other modules. To
  fix this, we gather all the notations here
  for export.
*)

Notation "0" := (Monoid.E_0) : monoid_scope.Notation "x + y" := (Monoid.op x y) (at level 50, left associativity) : monoid_scope.Notation "{+}" := (Monoid.op) : monoid_scope.(**
  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: *)
Theorem op_id : is_id M {+} 0.
Theorem op_id_l_uniq : forall x : M, (op_is_id_l x) -> x = 0.
Theorem op_id_r_uniq : forall x : M, (op_is_id_r x) -> x = 0.
Theorem op_id_uniq : forall x : M, (op_is_id x) -> x = 0.
Theorem op_intro_l : forall x y z : M, x = y -> z + x = z + y.
Theorem op_intro_r : forall x y z : M, x = y -> x + z = y + z.