\import Function.Meta
\import Homotopy.Loop
\import Homotopy.Pointed \using  (Pointed \as HPointed)
\import Meta
\import Paths
\import Paths.Meta

{- | This is an algebraic formulation of Eckmann Hilton argument.
 - It is used, for example, to prove that a monoid object in category of monoids is commutative
 - -}
\class Algebraic-Eckmann-Hilton (X : \Type){
  | \infix 7 o : X -> X -> X
  | \infix 7 # : X -> X -> X
  | id_o : X
  | id_o-left {x : X} : id_o o x = x
  | id_o-right {x : X} : x o id_o = x

  | id_# : X
  | id_#-left {x : X} : id_# # x = x
  | id_#-right {x : X} : x # id_# = x

  | rel {a b c d : X} : (a # b) o (c # d) = (a o c) # (b o d)

  \func units-coincide : id_o = id_#
    => rewrite (inv id_o-right, pmap (o id_o) (inv $ id_#-left),
                pmap (`o (id_# # id_o)) (inv $ id_#-right), rel, id_o-left, id_o-right) id_#-right

  \func binop_rels_1 {a b : X} : a o b = b # a
    => rewrite (pmap (\lam z => z o b) (inv $ id_#-left {\this} {a}),
                pmap (\lam z => (id_# # a) o z) (inv $ id_#-right {\this} {b}), rel, inv units-coincide,
                id_o-left, id_o-right) idp
  \func binop_rels_2 {a b : X} : b # a = b o a
    => rewrite (pmap (\lam z => z # a) (inv $ id_o-right {\this} {b}),
                pmap (\lam z => (b o id_o) # z) (inv $ id_o-left {\this} {a}), inv rel,
                units-coincide, id_#-right, id_#-left) idp

  \func comm {a b : X} : a o b = b o a => binop_rels_1 *> binop_rels_2

  \func binops_coincide {a b : X} : a o b = a # b => comm *> binop_rels_1 {\this} {b} {a}

  \func comm-# {a b : X} : a # b = b # a => rewrite (inv binops_coincide, binop_rels_1) idp

  \func rel-o {a b c d : X} : (a o b) o (c o d) = (a o c) o (b o d) =>
    rewrite (binops_coincide {\this} {a} {b}, binops_coincide {\this} {c} {d}, rel, inv binops_coincide) idp

  \func assoc {a b c : X} : (a o b) o c = a o (b o c) => rewrite (inv $ id_o-left {\this} {c}, rel-o, id_o-left, id_o-right) idp
}

\func RightHorizontalWhiskering{X : \Type}{a b c : X}{p q : a = b}(alp : p = q)(r : b = c)
  : p *> r = q *> r => path (\lam i => (alp @ i) *> r)

\func RightHorizontalWhiskering-relation{X : \Type} {a b : X}{p q : a = b}(alp : p = q) :
  RightHorizontalWhiskering alp (idp {X} {b}) = alp => idp

\func LeftHorizontalWhiskering{X : \Type} {a b c : X}(q : a = b){r s : b = c}(bet : r = s)
  : q *> r = q *> s => path (\lam i => q *> (bet @ i))


{- | This is the proof that Omega^2 X given the composition structure
 - forms a commutative monoid. This proof follows the proof from the HoTT Book
 - It has some minor changes that are possible with Arend features.
 - -}
\class Omega^2-Commutative (X : HPointed){

  \func Omega^2_X => Omega^ 2 X

  \func LeftHorizontalWhiskering-relation {b c : X}{r s : b = c}(bet : r = s)
    : LeftHorizontalWhiskering (idp {X} {b}) bet = (idp_*> r *> bet *> (inv $ idp_*> s)) \elim bet
    | idp => unfold LeftHorizontalWhiskering (rewrite (inv $ *>-assoc (idp_*> r) idp (inv (idp_*> r)), aux, aux-2) idp)
    \where {
      \func aux : path (\lam _ => idp *> r) = idp => idp
      \func aux-2 {f : b = c} : idp_*> f *> inv (idp_*> f) = idp \elim f
        | idp => idp
    }

  \func \infix 7 st1 {a b c : X}{p q : a = b}{r s : b = c}(alp : p = q)(bet : r = s) : p *> r = q *> s =>
    RightHorizontalWhiskering alp r *> LeftHorizontalWhiskering q bet

  \func \infix 7 st2 {a b c : X}{p q : a = b}{r s : b = c}(alp : p = q)(bet : r = s) : p *> r = q *> s =>
    LeftHorizontalWhiskering p bet *> RightHorizontalWhiskering alp s

  \func Relation {a b c : X}{p q : a = b}{r s : b = c}(alp : p = q)(bet : r = s) : alp st1 bet = alp st2 bet \elim alp, bet
    | idp, idp => helper
    \where {
      \func helper{a b c : X}{p : a = b}{r : b = c}:
        idp {a = b} {p} st1 idp {b = c}{r} = idp {a = b} {p} st2 idp {b = c}{r} \elim p, r
        | idp, idp => idp
    }

  \func Commutative (alp bet : Omega^2_X) : alp *> bet = bet *> alp => rewrite (inv Relation1, Relation3, Relation2) idp
    \where {
      \func Relation1 : alp st1 bet = alp *> bet
        => rewrite (helper, pmap (\lam z => alp *> z) (LeftHorizontalWhiskering-relation bet), helper-2, idp_*>) idp
        \where {
          \func helper : alp st1 bet = alp *> LeftHorizontalWhiskering idp bet => idp
          \func helper-2 : path (\lam _ => path (\lam _ => base {X})) = idp => idp
        }

      \func Relation2  : alp st2 bet = bet *> alp
        => rewrite (helper, pmap (\lam z => z *> alp) (LeftHorizontalWhiskering-relation bet), helper-2, idp_*>)idp
        \where {
          \func helper : alp st2 bet = LeftHorizontalWhiskering idp bet *> alp => idp
          \func helper-2 : path (\lam _ => path (\lam _ => base {X})) = idp => idp
        }

      \func Relation3  : alp st1 bet = alp st2 bet => Relation alp bet
    }
}