Library functional_algebra.monoid_group
  Every monoid has a nonempty subgroup consisting of the monoid's
  invertible elements.
 
  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/>.
Require Import base.
Require Import monoid.
Require Import group.
Require Import Eqdep.
Module Monoid_Group.
  Represents the homomorphic mapping between
  the set of invertible elements within a monoid
  and the group formed over them by the monoid
  operation.
Represents the set of monoid elements. 
Represents the identity monoid element. 
Represents the monoid operation. 
Asserts that the monoid operator is associative. 
    Accepts one monoid element, x, and asserts
    that x is the left identity element.
  
    Accepts one monoid element, x, and asserts
    that x is the right identity element.
  
    Accepts one monoid element, x, and asserts
    that x is the identity element.
  
Asserts that 0 is the left identity monoid element. 
Asserts that 0 is the right identity monoid element. 
    Represents the monoid whose invertable
    elements we are going to map onto a group.
  
    Represents those monoid elements that are
    invertable.
 
    Note: each value can be seen intuitively as
    a pair, (x, H), where x is a monoid element
    and H is a proof that x is invertable.
  
    Accepts a monoid element and a proof that
    it is invertable and returns its projection
    in D.
  
    Asserts that any two equal invertable monoid
    elements, x and y, are equivalent (using
    dependent equality).
 
    Note: to compare sig elements that differ
    only in their proof terms, such as (x, H) and
    (x, H0), we must introduce a new notion of
    equality called "dependent equality". This
    relationship is defined in the Eqdep module.
  
  D_eq_dep
: forall (x : E) (H : Monoid.has_inv m x) (y : E) (H0 : Monoid.has_inv m y), y = x -> eq_dep E (Monoid.has_inv m) y H0 x H;
  
: forall (x : E) (H : Monoid.has_inv m x) (y : E) (H0 : Monoid.has_inv m y), y = x -> eq_dep E (Monoid.has_inv m) y H0 x H;
    Given that two invertable monoid elements x
    and y are equal (using dependent equality),
    this lemma proves that their projections
    into D are equal.
 
    Note: this proof is equivalent to:
 
      eq_dep_eq_sig E (Monoid.has_inv m) y x H0 H
        (D_eq_dep x H y H0 H1).
 
    The definition for eq_dep_eq_sig has been
    expanded however for compatability with
    Coq v8.4.
  
  D_eq
: forall (x : E) (H : Monoid.has_inv m x) (y : E) (H0 : Monoid.has_inv m y), y = x -> D_cons y H0 = D_cons x H
:= fun x H y H0 H1
=> eq_dep_ind E (Monoid.has_inv m) y H0
(fun (z : E) (H2 : Monoid.has_inv m z)
=> D_cons y H0 = D_cons z H2)
(eq_refl (D_cons y H0)) x H (D_eq_dep x H y H0 H1);
  
: forall (x : E) (H : Monoid.has_inv m x) (y : E) (H0 : Monoid.has_inv m y), y = x -> D_cons y H0 = D_cons x H
:= fun x H y H0 H1
=> eq_dep_ind E (Monoid.has_inv m) y H0
(fun (z : E) (H2 : Monoid.has_inv m z)
=> D_cons y H0 = D_cons z H2)
(eq_refl (D_cons y H0)) x H (D_eq_dep x H y H0 H1);
    Represents the group identity element.
  
    Represents the group operation.
 
    Note: intuitively this function accepts two
    invertable monoid elements, (x, H) and (y,
    H0), and returns (x + y, H1), where H, H0,
    and H1 are generalized invertability proofs.
  
  group_op
: D -> D -> D
:= sig_rec
(fun _ => D -> D)
(fun (u : E) (H : Monoid.has_inv m u)
=> sig_rec
(fun _ => D)
(fun (v : E) (H0 : Monoid.has_inv m v)
=> D_cons
(monoid_op u v)
(Monoid.op_inv_closed m u H v H0)));
  
: D -> D -> D
:= sig_rec
(fun _ => D -> D)
(fun (u : E) (H : Monoid.has_inv m u)
=> sig_rec
(fun _ => D)
(fun (v : E) (H0 : Monoid.has_inv m v)
=> D_cons
(monoid_op u v)
(Monoid.op_inv_closed m u H v H0)));
    Accepts a group element, x, and asserts
    that x is a left identity element.
  
    Accepts a group element, x, and asserts
    that x is a right identity element.
  
    Accepts a group element, x, and asserts
    that x is an/the identity element.
  
Proves that D_0 is a left identity element. 
  group_op_id_l
: group_op_is_id_l D_0
:= sig_ind
(fun x => group_op D_0 x = x)
(fun (u : E) (H : Monoid.has_inv m u)
=> D_eq u H (monoid_op E_0 u) (Monoid.op_inv_closed m E_0 (Monoid.op_has_inv_0 m) u H)
(monoid_op_id_l u));
  
: group_op_is_id_l D_0
:= sig_ind
(fun x => group_op D_0 x = x)
(fun (u : E) (H : Monoid.has_inv m u)
=> D_eq u H (monoid_op E_0 u) (Monoid.op_inv_closed m E_0 (Monoid.op_has_inv_0 m) u H)
(monoid_op_id_l u));
Proves that D_0 is a right identity element. 
  group_op_id_r
: group_op_is_id_r D_0
:= sig_ind
(fun x => group_op x D_0 = x)
(fun (u : E) (H : Monoid.has_inv m u)
=> D_eq u H (monoid_op u E_0) (Monoid.op_inv_closed m u H E_0 (Monoid.op_has_inv_0 m))
(monoid_op_id_r u));
  
: group_op_is_id_r D_0
:= sig_ind
(fun x => group_op x D_0 = x)
(fun (u : E) (H : Monoid.has_inv m u)
=> D_eq u H (monoid_op u E_0) (Monoid.op_inv_closed m u H E_0 (Monoid.op_has_inv_0 m))
(monoid_op_id_r u));
Proves that D_0 is the identity element. 
    Proves that the group operation is
    associative.
  
  group_op_assoc
: Monoid.is_assoc D group_op
:= sig_ind
(fun x => forall y z : D, group_op x (group_op y z) = group_op (group_op x y) z)
(fun (u : E) (H : Monoid.has_inv m u)
=> sig_ind
(fun y => forall z : D, group_op (D_cons u H) (group_op y z) = group_op (group_op (D_cons u H) y) z)
(fun (v : E) (H0 : Monoid.has_inv m v)
=> sig_ind
(fun z => group_op (D_cons u H) (group_op (D_cons v H0) z) = group_op (group_op (D_cons u H) (D_cons v H0)) z)
(fun (w : E) (H1 : Monoid.has_inv m w)
=> let a
: E
:= monoid_op u (monoid_op v w) in
let H2
: Monoid.has_inv m a
:= Monoid.op_inv_closed m u H (monoid_op v w) (Monoid.op_inv_closed m v H0 w H1) in
let b
: E
:= monoid_op (monoid_op u v) w in
let H3
: Monoid.has_inv m b
:= Monoid.op_inv_closed m (monoid_op u v) (Monoid.op_inv_closed m u H v H0) w H1 in
let X
: D
:= D_cons a H2 in
let Y
: D
:= D_cons b H3 in
D_eq b H3 a H2
(monoid_op_is_assoc u v w)
)));
  
: Monoid.is_assoc D group_op
:= sig_ind
(fun x => forall y z : D, group_op x (group_op y z) = group_op (group_op x y) z)
(fun (u : E) (H : Monoid.has_inv m u)
=> sig_ind
(fun y => forall z : D, group_op (D_cons u H) (group_op y z) = group_op (group_op (D_cons u H) y) z)
(fun (v : E) (H0 : Monoid.has_inv m v)
=> sig_ind
(fun z => group_op (D_cons u H) (group_op (D_cons v H0) z) = group_op (group_op (D_cons u H) (D_cons v H0)) z)
(fun (w : E) (H1 : Monoid.has_inv m w)
=> let a
: E
:= monoid_op u (monoid_op v w) in
let H2
: Monoid.has_inv m a
:= Monoid.op_inv_closed m u H (monoid_op v w) (Monoid.op_inv_closed m v H0 w H1) in
let b
: E
:= monoid_op (monoid_op u v) w in
let H3
: Monoid.has_inv m b
:= Monoid.op_inv_closed m (monoid_op u v) (Monoid.op_inv_closed m u H v H0) w H1 in
let X
: D
:= D_cons a H2 in
let Y
: D
:= D_cons b H3 in
D_eq b H3 a H2
(monoid_op_is_assoc u v w)
)));
    Accepts two values, x and y, and asserts
    that y is a left inverse of x.
  
    Accepts two values, x and y, and asserts
    that y is a right inverse of x.
  
    Accepts two values, x and y, and asserts
    that y is an inverse of x.
  
    Accepts two invertable monoid elements, x
    and y, where y is a left inverse of x and
    proves that y's projection into D is the
    left inverse of x's.
  
  group_op_inv_l
: forall (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v),
Monoid.op_is_inv_l m u v ->
group_op_is_inv_l (D_cons u H) (D_cons v H0)
:= fun (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v)
=> D_eq E_0 (Monoid.op_has_inv_0 m) (monoid_op v u) (Monoid.op_inv_closed m v H0 u H);
        
  
: forall (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v),
Monoid.op_is_inv_l m u v ->
group_op_is_inv_l (D_cons u H) (D_cons v H0)
:= fun (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v)
=> D_eq E_0 (Monoid.op_has_inv_0 m) (monoid_op v u) (Monoid.op_inv_closed m v H0 u H);
    Accepts two invertable monoid elements, x
    and y, where y is a right inverse of x and
    proves that y's projection into D is the
    right inverse of x's.
  
  group_op_inv_r
: forall (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v),
Monoid.op_is_inv_r m u v ->
group_op_is_inv_r (D_cons u H) (D_cons v H0)
:= fun (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v)
=> D_eq E_0 (Monoid.op_has_inv_0 m) (monoid_op u v) (Monoid.op_inv_closed m u H v H0);
  
: forall (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v),
Monoid.op_is_inv_r m u v ->
group_op_is_inv_r (D_cons u H) (D_cons v H0)
:= fun (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v)
=> D_eq E_0 (Monoid.op_has_inv_0 m) (monoid_op u v) (Monoid.op_inv_closed m u H v H0);
    Accepts two invertable monoid elements, x
    and y, where y is the inverse of x and
    proves that y's projection into D is the
    inverse of x's.
  
  group_op_inv
: forall (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v),
Monoid.op_is_inv m u v ->
group_op_is_inv (D_cons u H) (D_cons v H0)
:= fun (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v) (H1 : Monoid.op_is_inv m u v)
=> conj (group_op_inv_l u H v H0 (proj1 H1))
(group_op_inv_r u H v H0 (proj2 H1));
  
: forall (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v),
Monoid.op_is_inv m u v ->
group_op_is_inv (D_cons u H) (D_cons v H0)
:= fun (u : E) (H : Monoid.has_inv m u) (v : E) (H0 : Monoid.has_inv m v) (H1 : Monoid.op_is_inv m u v)
=> conj (group_op_inv_l u H v H0 (proj1 H1))
(group_op_inv_r u H v H0 (proj2 H1));
    Accepts a group element and returns its
    inverse, y, along with a proof that y is
    x's inverse.
  
  group_op_neg_strong
: forall x : D, { y : D | group_op_is_inv x y }
:= sig_rec
(fun x => { y : D | group_op_is_inv x y })
(fun (u : E) (H : Monoid.has_inv m u)
=> let v
: E
:= Monoid.op_neg m u H in
let H0
: Monoid.op_is_inv m u v
:= Monoid.op_neg_def m u H in
let H1
: Monoid.has_inv m v
:= Monoid.op_neg_inv_ex m u H in
exist
(fun y : D => group_op_is_inv (D_cons u H) y)
(D_cons v H1)
(group_op_inv u H v H1 H0));
  
: forall x : D, { y : D | group_op_is_inv x y }
:= sig_rec
(fun x => { y : D | group_op_is_inv x y })
(fun (u : E) (H : Monoid.has_inv m u)
=> let v
: E
:= Monoid.op_neg m u H in
let H0
: Monoid.op_is_inv m u v
:= Monoid.op_neg_def m u H in
let H1
: Monoid.has_inv m v
:= Monoid.op_neg_inv_ex m u H in
exist
(fun y : D => group_op_is_inv (D_cons u H) y)
(D_cons v H1)
(group_op_inv u H v H1 H0));
    Proves that every group element has an
    inverse.
  
  group_op_inv_ex 
: forall x : D, exists y : D, group_op_is_inv x y
:= fun x
=> let (y, H) := group_op_neg_strong x in
ex_intro
(fun y => group_op_is_inv x y)
y H;
  
: forall x : D, exists y : D, group_op_is_inv x y
:= fun x
=> let (y, H) := group_op_neg_strong x in
ex_intro
(fun y => group_op_is_inv x y)
y H;
    Proves that every group element has a left
    inverse.
  
  group_op_inv_l_ex
: forall x : D, exists y : D, group_op_is_inv_l x y
:= fun x
=> ex_ind
(fun y (H : group_op_is_inv x y)
=> ex_intro (fun z => group_op_is_inv_l x z) y (proj1 H))
(group_op_inv_ex x);
  
: forall x : D, exists y : D, group_op_is_inv_l x y
:= fun x
=> ex_ind
(fun y (H : group_op_is_inv x y)
=> ex_intro (fun z => group_op_is_inv_l x z) y (proj1 H))
(group_op_inv_ex x);
    Proves that every group element has a right
    inverse.
  
  group_op_inv_r_ex
: forall x : D, exists y : D, group_op_is_inv_r x y
:= fun x
=> ex_ind
(fun y (H : group_op_is_inv x y)
=> ex_intro (fun z => group_op_is_inv_r x z) y (proj2 H))
(group_op_inv_ex x);
  
: forall x : D, exists y : D, group_op_is_inv_r x y
:= fun x
=> ex_ind
(fun y (H : group_op_is_inv x y)
=> ex_intro (fun z => group_op_is_inv_r x z) y (proj2 H))
(group_op_inv_ex x);
    Proves that the set of invertable monoid
    elements form a group over the monoid
    operation.