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

open import lib.Basics
open import lib.types.Sigma
open import 2Semigroup

module 2Grp where

{-
  coherent 2-group structure on a type
  following Baez and Lauda (2004)
-}

record CohGrp {i} (X : Type i) : Type i where
  constructor cohgrp
  field
    instance {{1trunc}} : has-level 1 X
    
    {-
      monoidal groupoid structure on X (with
      X viewed as a category with == as hom)
    -}
    id : X
    mu : X  X  X
    lam : (x : X)  mu id x == x
    rho : (x : X)  mu x id == x
    al : (x y z : X)  mu x (mu y z) == mu (mu x y) z
    tr : (x y : X)  ap  z  mu z y) (rho x) == ! (al x id y)  ap (mu x) (lam y)
    pent : (w x y z : X) 
      al w x (mu y z)  al (mu w x) y z
      ==
      ap (mu w) (al x y z)  al w (mu x y) z  ap  v  mu v z) (al w x y)
    
    -- beginning of 2-group structure
    inv : X  X
    linv : (x : X)  mu (inv x) x == id
    rinv : (x : X)  id == mu x (inv x)
    
    -- adjoint equiv conditions on inv ("zz" short for "zig-zag")
    zz₁ : (x : X) 
      lam x
      ==
      ap  z  mu z x) (rinv x) 
      ! (al x (inv x) x) 
      ap (mu x) (linv x) 
      rho x
    zz₂ : (x : X) 
      ! (lam (inv x))
      ==
      ! (rho (inv x)) 
      ap (mu (inv x)) (rinv x) 
      al (inv x) x (inv x) 
      ap  z  mu z (inv x)) (linv x)
      
module _ {i} {G : Type i} where

  CohGrp-data : (mu : G  G  G) (al : (x y z : G)  mu x (mu y z) == mu (mu x y) z)  Type i
  CohGrp-data mu al = 
    [ id  G ] ×
      [ lam  ((x : G)  mu id x == x) ] × 
      [ rho  ((x : G)  mu x id == x) ] ×
        [ tr  ((x y : G)  ap  z  mu z y) (rho x) == ! (al x id y)  ap (mu x) (lam y)) ] ×
        [ pent  ((w x y z : G) 
            al w x (mu y z)  al (mu w x) y z
            ==
            ap (mu w) (al x y z)  al w (mu x y) z  ap  v  mu v z) (al w x y)) ] ×
        [ inv  (G  G) ] ×
          [ linv  ((x : G)  mu (inv x) x == id) ] ×
          [ rinv  ((x : G)  id == mu x (inv x)) ] ×
            (((x : G) 
              lam x
              ==
              ap  z  mu z x) (rinv x) 
              ! (al x (inv x) x) 
              ap (mu x) (linv x) 
              rho x) ×
            ((x : G) 
              ! (lam (inv x))
              ==
              ! (rho (inv x)) 
              ap (mu (inv x)) (rinv x) 
              al (inv x) x (inv x) 
              ap  z  mu z (inv x)) (linv x)))

  -- assembling CohGrp as a Σ-type
  CohGrp-Σ-≃ :
    Σ (WkSGrp G)  (wksgrp {{_}} mu al)  CohGrp-data mu al)
      
    CohGrp G
  CohGrp-Σ-≃ = equiv
     ((wksgrp {{1t}} mu al) , (id₁ , lam , rho , tr , pent , inv , linv , rinv , zz₁ , zz₂)) 
      cohgrp {{1t}} id₁ mu lam rho al tr pent inv linv rinv zz₁ zz₂ )
     (cohgrp {{1t}} id₁ mu lam rho al tr pent inv linv rinv zz₁ zz₂) 
      (wksgrp {{1t}} mu al) , (id₁ , lam , rho , tr , pent , inv , linv , rinv , zz₁ , zz₂))
     _  idp)
    λ _  idp
    
  -- underlying 2-semigroup structure of a 2-group
  sgrp : CohGrp G  WkSGrp G
  sgrp η = wksgrp (mu η) (al η) where open CohGrp

  instance
    sgrp-instance : {{η : CohGrp G}}  WkSGrp G
    sgrp-instance {{η}} = sgrp η

open CohGrp {{...}}

-- multiplication by id is an iso of the underlying 1-type
module _ {i} {G : Type i} {{η : CohGrp G}} where

  muid-iso : is-equiv (mu id)
  muid-iso =
    ∼-preserves-equiv
     x  ! (lam x))
    (idf-is-equiv G)

  -- <– (ap-equiv (muid , muid-iso) _ _)
  muid-iso<– : (z₁ z₂ : G)  (mu id z₁ == mu id z₂)  (z₁ == z₂)
  muid-iso<– z₁ z₂ p = 
    ! (ap  x  x) (! (! (lam z₁)))  idp) 
    ap  x  x) p 
    ap  x  x) (! (! (lam z₂))) 
    idp

  idmu-iso : is-equiv  x  mu x id)
  idmu-iso =
    ∼-preserves-equiv
     x  ! (rho x))
    (idf-is-equiv G)

  -- <– (ap-equiv ((λ x → mu x id) , idmu-iso) _ _)
  idmu-iso<– : (z₁ z₂ : G)  (mu z₁ id == mu z₂ id)  (z₁ == z₂)
  idmu-iso<– z₁ z₂ p = 
    ! (ap  x  x) (! (! (rho z₁)))  idp) 
    ap  x  x) p 
    ap  x  x) (! (! (rho z₂))) 
    idp

-- multiplication on either side is an iso
module _ {i} {G : Type i} {{η : CohGrp G}} (x : G) where

  mu-pre-iso : is-equiv (mu x)
  mu-pre-iso =
    is-eq (mu x) (mu (inv x))
       b  al x (inv x) b  ap2 mu (! (rinv x)) idp  lam b)
      λ a  al (inv x) x a  ap2 mu (linv x) idp  lam a

  mu-pre-ff<– : (z₁ z₂ : G)  (mu x z₁ == mu x z₂)  (z₁ == z₂)
  mu-pre-ff<– z₁ z₂ p =
    ! (al (inv x) x z₁  ap2 mu (linv x) idp  lam z₁) 
    ap (mu (inv x)) p 
    al (inv x) x z₂ 
    ap2 mu (linv x) idp 
    lam z₂

  mu-post-iso : is-equiv  z  mu z x)
  mu-post-iso =
    is-eq  z  mu z x)  z  mu z (inv x))
       b  ! (al b (inv x) x)  ap (mu b) (linv x)  rho b )
      λ a  ! (al a x (inv x))  ! (ap (mu a) (rinv x))  rho a

  mu-post-ff<– : (z₁ z₂ : G)  (mu z₁ x == mu z₂ x)  (z₁ == z₂)
  mu-post-ff<– z₁ z₂ p =
    ! (! (al z₁ x (inv x))  ! (ap (mu z₁) (rinv x))  rho z₁) 
    ap  z  mu z (inv x)) p 
    ! (al z₂ x (inv x)) 
    ! (ap (mu z₂) (rinv x)) 
    rho z₂

-- morphisms of 2-groups

module _ {i j} {G₁ : Type i} {G₂ : Type j} {{η₁ : CohGrp G₁}} {{η₂ : CohGrp G₂}} where

  -- definition explicitly accounting for all 2-group data
  record CohGrpHomStrFull (map : G₁  G₂) : Type (lmax i j) where
    constructor cohgrphomstrfull
    field
      map-comp : (x y : G₁)  mu (map x) (map y) == map (mu x y)
      map-al : (x y z : G₁) 
        ! (al (map x) (map y) (map z)) 
        ap (mu (map x)) (map-comp y z) 
        map-comp x (mu y z)
        ==
        ap  v  mu v (map z)) (map-comp x y) 
        map-comp (mu x y) z 
        ! (ap map (al x y z))
      map-id : id == map id
      map-rho : (x : G₁) 
        ! (map-comp x id)
        ==
        ap map (rho x) 
        ! (rho (map x)) 
        ap (mu (map x)) map-id
      map-lam : (x : G₁) 
        ! (lam (map x))
        ==
        ! (ap map (lam x)) 
        ! (map-comp id x) 
        ! (ap  z  mu z (map x)) map-id)
      map-inv : (x : G₁)  inv (map x) == map (inv x)
      map-rinv : (x : G₁) 
        ! (ap (mu (map x)) (map-inv x))
        ==
        map-comp x (inv x) 
        ! (ap map (rinv x)) 
        ! map-id 
        rinv (map x)
      map-linv : (x : G₁)  
        ! (ap  z  mu z (map x)) (map-inv x))
        ==
        map-comp (inv x) x 
        ap map (linv x) 
        ! map-id 
        ! (linv (map x))

  -- shorter definition, easier to work with
  CohGrpHomStr : (map : G₁  G₂)  Type (lmax i j)
  CohGrpHomStr map = WkSGrpHomStr {{sgrp η₁}} {{sgrp η₂}} map

  record CohGrpHom : Type (lmax i j) where
    constructor cohgrphom
    field
      map : G₁  G₂
      {{str}} : CohGrpHomStr map
  open CohGrpHom

  grphom-forg : CohGrpHom  WkSGrpWkHom
  map-wk (grphom-forg f) = map f
  map-comp-wk (grphom-forg f) = map-comp (str f) where open WkSGrpHomStr

  -- natural isomorphisms between 2-group morphisms
  CohGrpNatIso : CohGrpHom  CohGrpHom  Type (lmax i j)
  CohGrpNatIso μ₁ μ₂ = WkSGrpNatIso (grphom-forg μ₁) (grphom-forg μ₂)

-- categorical structure on 2-groups

open CohGrpHom

module _ {i} {G : Type i} {{η : CohGrp G}} where

  open WkSGrpHomStr

  idf2G : CohGrpHomStr (idf G)
  map-comp idf2G x y = idp
  map-al idf2G = map-al-idfG
    where abstract
      map-al-idfG : (x y z : G)  ! (al x y z)  idp == ! (ap (idf G) (al x y z))
      map-al-idfG x y z = ∙-unit-r (! (al x y z))  ap ! (! (ap-idf (al x y z)))

module _{i j k} {G₁ : Type i} {G₂ : Type j} {G₃ : Type k}
  {{η₁ : CohGrp G₁}} {{η₂ : CohGrp G₂}} {{η₃ : CohGrp G₃}} where

  open WkSGrpHomStr {{...}}

  -- composition of 2-group morphisms
  infixr 60 _∘2Gσ_
  _∘2Gσ_ :  (F₂ : CohGrpHom {{η₂}} {{η₃}}) (F₁ : CohGrpHom {{η₁}} {{η₂}})  CohGrpHomStr (map F₂  map F₁)
  _∘2Gσ_ F₂ F₁ = cohsgrphom (map F₂) ∘2Mσ cohsgrphom (map F₁)

  infixr 50 _∘2G_
  _∘2G_ : CohGrpHom {{η₂}} {{η₃}}  CohGrpHom {{η₁}} {{η₂}}  CohGrpHom {{η₁}} {{η₃}}
  map (F₂ ∘2G F₁) = map F₂  map F₁
  str (F₂ ∘2G F₁) = F₂ ∘2Gσ F₁