{-# OPTIONS --without-K #-}

open import lib.Basics
open import lib.types.Pi

module lib.types.Group where

record GroupStructure {i} (El : Type i) --(El-level : has-level ⟨0⟩ El)
  : Type i where
  constructor group-structure
  field
    ident : El
    inv : El  El
    comp : El  El  El
    unitl :  a  comp ident a == a
    unitr :  a  comp a ident == a
    assoc   :  a b c  comp (comp a b) c == comp a (comp b c)
    invl    :  a  (comp (inv a) a) == ident
    invr    :  a  (comp a (inv a)) == ident

record Group i : Type (lsucc i) where
  constructor group
  field
    El : Type i
    El-level : has-level ⟨0⟩ El
    group-struct : GroupStructure El
  open GroupStructure group-struct public

  ⊙El : Σ (Type i)  A  A)
  ⊙El = (El , ident)

Group₀ : Type (lsucc lzero)
Group₀ = Group lzero

is-abelian :  {i}  Group i  Type i
is-abelian G = (a b : Group.El G)  Group.comp G a b == Group.comp G b a

module _ where
  open GroupStructure

  abstract
    group-structure= :  {i} {A : Type i} (pA : has-level ⟨0⟩ A)
      {id₁ id₂ : A} {inv₁ inv₂ : A  A} {comp₁ comp₂ : A  A  A}
        {unitl₁ unitl₂}   {unitr₁ unitr₂}   {assoc₁ assoc₂}
        {invr₁ invr₂}   {invl₁ invl₂}
       (id₁ == id₂)  (inv₁ == inv₂)  (comp₁ == comp₂)
       Path {A = GroupStructure A}
          (group-structure id₁ inv₁ comp₁ unitl₁ unitr₁ assoc₁ invl₁ invr₁)
          (group-structure id₂ inv₂ comp₂ unitl₂ unitr₂ assoc₂ invl₂ invr₂)
    group-structure= pA {id₁ = id₁} {inv₁ = inv₁} {comp₁ = comp₁} idp idp idp =
      ap5 (group-structure id₁ inv₁ comp₁)
        (prop-has-all-paths (Π-level  _  pA _ _)) _ _)
        (prop-has-all-paths (Π-level  _  pA _ _)) _ _)
        (prop-has-all-paths
          (Π-level  _  Π-level  _  Π-level  _  pA _ _)))) _ _)
        (prop-has-all-paths (Π-level  _  pA _ _)) _ _)
        (prop-has-all-paths (Π-level  _  pA _ _)) _ _)
      where
      ap5 :  {j} {C D E F G H : Type j}
        {c₁ c₂ : C} {d₁ d₂ : D} {e₁ e₂ : E} {f₁ f₂ : F} {g₁ g₂ : G}
        (f : C  D  E  F  G  H)
         (c₁ == c₂)  (d₁ == d₂)  (e₁ == e₂)  (f₁ == f₂)  (g₁ == g₂)
         f c₁ d₁ e₁ f₁ g₁ == f c₂ d₂ e₂ f₂ g₂
      ap5 f idp idp idp idp idp = idp

    ↓-group-structure= :  {i} {A B : Type i}
      (A-level : has-level ⟨0⟩ A)
      {GS : GroupStructure A} {HS : GroupStructure B} (p : A == B)
       (ident GS == ident HS [  C  C)  p ])
       (inv GS == inv HS [  C  C  C)  p ])
       (comp GS == comp HS [  C  C  C  C)  p ])
       GS == HS [ GroupStructure  p ]
    ↓-group-structure= A-level idp = group-structure= A-level

module _ {i} (G : Group i) where
  private
    open Group G
    infix 80 _⊙_
    _⊙_ = comp

  group-inv-unique-l : (g h : El)  (g  h == ident)  inv h == g
  group-inv-unique-l g h p =
    inv h              =⟨ ! (unitl (inv h)) 
    ident  inv h      =⟨ ! p |in-ctx  w  w  inv h) 
    (g  h)  inv h    =⟨ assoc g h (inv h) 
    g  (h  inv h)    =⟨ invr h |in-ctx  w  g  w) 
    g  ident          =⟨ unitr g 
    g                     

  group-inv-unique-r : (g h : El)  (g  h == ident)  inv g == h
  group-inv-unique-r g h p =
    inv g              =⟨ ! (unitr (inv g)) 
    inv g  ident      =⟨ ! p |in-ctx  w  inv g  w) 
    inv g  (g  h)    =⟨ ! (assoc (inv g) g h) 
    (inv g  g)  h    =⟨ invl g |in-ctx  w  w  h) 
    ident  h          =⟨ unitl h 
    h                     

  group-inv-ident : inv ident == ident
  group-inv-ident =
    group-inv-unique-l ident ident (unitl ident)

  group-inv-comp : (g₁ g₂ : El)  inv (g₁  g₂) == inv g₂  inv g₁
  group-inv-comp g₁ g₂ =
    group-inv-unique-r (g₁  g₂) (inv g₂  inv g₁) $
      (g₁  g₂)  (inv g₂  inv g₁)
        =⟨ assoc g₁ g₂ (inv g₂  inv g₁) 
      g₁  (g₂  (inv g₂  inv g₁))
        =⟨ ! (assoc g₂ (inv g₂) (inv g₁)) |in-ctx  w  g₁  w) 
      g₁  ((g₂  inv g₂)  inv g₁)
        =⟨ invr g₂ |in-ctx  w  g₁  (w  inv g₁)) 
      g₁  (ident  inv g₁)
        =⟨ unitl (inv g₁) |in-ctx  w  g₁  w) 
      g₁  inv g₁
        =⟨ invr g₁ 
      ident 

  group-inv-inv : (g : El)  inv (inv g) == g
  group-inv-inv g = group-inv-unique-r (inv g) g (invl g)

  group-cancel-l : (g : El) {h k : El}  g  h == g  k  h == k
  group-cancel-l g {h} {k} p =
    h                  =⟨ ! (unitl h) 
    ident  h          =⟨ ap  w  w  h) (! (invl g)) 
    (inv g  g)  h    =⟨ assoc (inv g) g h 
    inv g  (g  h)    =⟨ ap  w  inv g  w) p 
    inv g  (g  k)    =⟨ ! (assoc (inv g) g k) 
    (inv g  g)  k    =⟨ ap  w  w  k) (invl g) 
    ident  k          =⟨ unitl k 
    k 

  group-cancel-r : (g : El) {h k : El}  h  g == k  g  h == k
  group-cancel-r g {h} {k} p =
    h                  =⟨ ! (unitr h) 
    h  ident          =⟨ ap  w  h  w) (! (invr g)) 
    h  (g  inv g)    =⟨ ! (assoc h g (inv g)) 
    (h  g)  inv g    =⟨ ap  w  w  inv g) p 
    (k  g)  inv g    =⟨ assoc k g (inv g) 
    k  (g  inv g)    =⟨ ap  w  k  w) (invr g) 
    k  ident          =⟨ unitr k 
    k