{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Algebra.Group.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.SIP
open import Cubical.Data.Sigma
open import Cubical.Data.Int renaming (_+_ to _+Int_ ; _-_ to _-Int_; -_ to -Int_)
open import Cubical.Data.Unit
open import Cubical.Data.Bool

open import Cubical.Algebra.Monoid
open import Cubical.Algebra.Semigroup
open import Cubical.Foundations.HLevels

private
  variable
     : Level

record IsGroup {G : Type }
               (0g : G) (_+_ : G  G  G) (-_ : G  G) : Type  where

  constructor isgroup

  field
    isMonoid  : IsMonoid 0g _+_
    inverse   : (x : G)  (x + (- x)  0g) × ((- x) + x  0g)

  open IsMonoid isMonoid public

  infixl 6 _-_

  _-_ : G  G  G
  x - y = x + (- y)

  invl : (x : G)  (- x) + x  0g
  invl x = inverse x .snd

  invr : (x : G)  x + (- x)  0g
  invr x = inverse x .fst

record GroupStr (G : Type ) : Type (ℓ-suc ) where

  constructor groupstr

  field
    0g      : G
    _+_     : G  G  G
    -_      : G  G
    isGroup : IsGroup 0g _+_ -_

  infix  8 -_
  infixr 7 _+_

  open IsGroup isGroup public

Group : Type (ℓ-suc )
Group = TypeWithStr _ GroupStr

Group₀ : Type₁
Group₀ = Group {ℓ-zero}

group : (G : Type ) (0g : G) (_+_ : G  G  G) (-_ : G  G) (h : IsGroup 0g _+_ -_)  Group
group G 0g _+_ -_ h = G , groupstr 0g _+_ -_ h

isSetGroup : (G : Group {})  isSet  G 
isSetGroup G = GroupStr.isGroup (snd G) .IsGroup.isMonoid .IsMonoid.isSemigroup .IsSemigroup.is-set

makeIsGroup : {G : Type } {0g : G} {_+_ : G  G  G} { -_ : G  G}
              (is-setG : isSet G)
              (assoc : (x y z : G)  x + (y + z)  (x + y) + z)
              (rid : (x : G)  x + 0g  x)
              (lid : (x : G)  0g + x  x)
              (rinv : (x : G)  x + (- x)  0g)
              (linv : (x : G)  (- x) + x  0g)
             IsGroup 0g _+_ -_
IsGroup.isMonoid (makeIsGroup is-setG assoc rid lid rinv linv) = makeIsMonoid is-setG assoc rid lid
IsGroup.inverse (makeIsGroup is-setG assoc rid lid rinv linv) = λ x  rinv x , linv x

makeGroup : {G : Type } (0g : G) (_+_ : G  G  G) (-_ : G  G)
            (is-setG : isSet G)
            (assoc : (x y z : G)  x + (y + z)  (x + y) + z)
            (rid : (x : G)  x + 0g  x)
            (lid : (x : G)  0g + x  x)
            (rinv : (x : G)  x + (- x)  0g)
            (linv : (x : G)  (- x) + x  0g)
           Group
makeGroup 0g _+_ -_ is-setG assoc rid lid rinv linv = _ , helper
  where
  helper : GroupStr _
  GroupStr.0g helper = 0g
  GroupStr._+_ helper = _+_
  GroupStr.- helper = -_
  GroupStr.isGroup helper = makeIsGroup is-setG assoc rid lid rinv linv

makeGroup-right :  {} {A : Type }
   (id : A)
   (comp : A  A  A)
   (inv : A  A)
   (set : isSet A)
   (assoc :  a b c  comp a (comp b c)  comp (comp a b) c)
   (rUnit :  a  comp a id  a)
   (rCancel :  a  comp a (inv a)  id)
   Group
makeGroup-right id comp inv set assoc rUnit rCancel =
  makeGroup id comp inv set assoc rUnit lUnit rCancel lCancel
  where
    _⨀_ = comp
    abstract
      lCancel :  a  comp (inv a) a  id
      lCancel a =
        inv a  a
          ≡⟨ sym (rUnit (comp (inv a) a))  
        (inv a  a)  id
          ≡⟨ cong (comp (comp (inv a) a)) (sym (rCancel (inv a))) 
        (inv a  a)  (inv a  (inv (inv a)))
          ≡⟨ assoc _ _ _ 
        ((inv a  a)  (inv a))  (inv (inv a))
          ≡⟨ cong      _) (sym (assoc _ _ _)) 
        (inv a  (a  inv a))  (inv (inv a))
          ≡⟨ cong    (inv a  )  (inv (inv a))) (rCancel a) 
        (inv a  id)  (inv (inv a))
          ≡⟨ cong      (inv (inv a))) (rUnit (inv a)) 
        inv a  (inv (inv a))
          ≡⟨ rCancel (inv a) 
        id
          

      lUnit :  a  comp id a  a
      lUnit a =
        id  a
          ≡⟨ cong  b  comp b a) (sym (rCancel a)) 
        (a  inv a)  a
          ≡⟨ sym (assoc _ _ _) 
        a  (inv a  a)
          ≡⟨ cong (comp a) (lCancel a) 
        a  id
          ≡⟨ rUnit a 
        a
          

makeGroup-left :  {} {A : Type }
   (id : A)
   (comp : A  A  A)
   (inv : A  A)
   (set : isSet A)
   (assoc :  a b c  comp a (comp b c)  comp (comp a b) c)
   (lUnit :  a  comp id a  a)
   (lCancel :  a  comp (inv a) a  id)
   Group
makeGroup-left id comp inv set assoc lUnit lCancel =
  makeGroup id comp inv set assoc rUnit lUnit rCancel lCancel
  where
    abstract
      rCancel :  a  comp a (inv a)  id
      rCancel a =
        comp a (inv a)
          ≡⟨ sym (lUnit (comp a (inv a)))  
        comp id (comp a (inv a))
          ≡⟨ cong  b  comp b (comp a (inv a))) (sym (lCancel (inv a))) 
        comp (comp (inv (inv a)) (inv a)) (comp a (inv a))
          ≡⟨ sym (assoc (inv (inv a)) (inv a) (comp a (inv a))) 
        comp (inv (inv a)) (comp (inv a) (comp a (inv a)))
          ≡⟨ cong (comp (inv (inv a))) (assoc (inv a) a (inv a)) 
        comp (inv (inv a)) (comp (comp (inv a) a) (inv a))
          ≡⟨ cong  b  comp (inv (inv a)) (comp b (inv a))) (lCancel a) 
        comp (inv (inv a)) (comp id (inv a))
          ≡⟨ cong (comp (inv (inv a))) (lUnit (inv a)) 
        comp (inv (inv a)) (inv a)
          ≡⟨ lCancel (inv a) 
        id
          

      rUnit :  a  comp a id  a
      rUnit a =
        comp a id
          ≡⟨ cong (comp a) (sym (lCancel a)) 
        comp a (comp (inv a) a)
          ≡⟨ assoc a (inv a) a 
        comp (comp a (inv a)) a
          ≡⟨ cong  b  comp b a) (rCancel a) 
        comp id a
          ≡⟨ lUnit a 
        a
          

open GroupStr hiding (0g ; _+_ ; -_)

isSetCarrier :  {}  (G : Group {})  isSet  G 
isSetCarrier G = IsSemigroup.is-set (IsMonoid.isSemigroup (GroupStr.isMonoid (snd G)))

open GroupStr
dirProd :  { ℓ'}  Group {}  Group {ℓ'}  Group
fst (dirProd G H) = fst G × fst H
0g (snd (dirProd G H)) = (0g (snd G)) , (0g (snd H))
_+_ (snd (dirProd G H)) x y = _+_ (snd G) (fst x) (fst y)
                            , _+_ (snd H) (snd x) (snd y)
(- snd (dirProd G H)) x = (-_ (snd G) (fst x)) , (-_ (snd H) (snd x))
IsSemigroup.is-set (IsMonoid.isSemigroup (IsGroup.isMonoid (isGroup (snd (dirProd G H))))) =
  isSet× (is-set (snd G)) (is-set (snd H))
IsSemigroup.assoc (IsMonoid.isSemigroup (IsGroup.isMonoid (isGroup (snd (dirProd G H))))) x y z i =
  assoc (snd G) (fst x) (fst y) (fst z) i , assoc (snd H) (snd x) (snd y) (snd z) i
fst (IsMonoid.identity (IsGroup.isMonoid (isGroup (snd (dirProd G H)))) x) i =
  rid (snd G) (fst x) i , rid (snd H) (snd x) i
snd (IsMonoid.identity (IsGroup.isMonoid (isGroup (snd (dirProd G H)))) x) i =
  lid (snd G) (fst x) i , lid (snd H) (snd x) i
fst (IsGroup.inverse (isGroup (snd (dirProd G H))) x) i =
  (invr (snd G) (fst x) i) , invr (snd H) (snd x) i
snd (IsGroup.inverse (isGroup (snd (dirProd G H))) x) i =
  (invl (snd G) (fst x) i) , invl (snd H) (snd x) i

trivialGroup : Group₀
trivialGroup = Unit , groupstr tt  _ _  tt)  _  tt)
                      (makeIsGroup isSetUnit  _ _ _  refl)  _  refl)  _  refl)
                                    _  refl)  _  refl))

intGroup : Group₀
fst intGroup = Int
0g (snd intGroup) = 0
_+_ (snd intGroup) = _+Int_
- snd intGroup = _-Int_ 0
isGroup (snd intGroup) = isGroupInt
  where
  abstract
    isGroupInt : IsGroup (pos 0) _+Int_ (_-Int_ (pos 0))
    isGroupInt = makeIsGroup isSetInt +-assoc  x  refl)  x  +-comm 0 x)
                               x  +-comm x (pos 0 -Int x)  minusPlus x 0)  x  minusPlus x 0)
open IsGroup
open IsMonoid
open IsSemigroup renaming (assoc to assoc')

BoolGroup : Group₀
fst BoolGroup = Bool
0g (snd BoolGroup) = true
(snd BoolGroup GroupStr.+ false) false = true
(snd BoolGroup GroupStr.+ false) true = false
(snd BoolGroup GroupStr.+ true) y = y
(- snd BoolGroup) false = false
(- snd BoolGroup) true = true
is-set (isSemigroup (isMonoid (isGroup (snd BoolGroup)))) = isSetBool
assoc' (isSemigroup (isMonoid (isGroup (snd BoolGroup)))) false false false = refl
assoc' (isSemigroup (isMonoid (isGroup (snd BoolGroup)))) false false true = refl
assoc' (isSemigroup (isMonoid (isGroup (snd BoolGroup)))) false true false = refl
assoc' (isSemigroup (isMonoid (isGroup (snd BoolGroup)))) false true true = refl
assoc' (isSemigroup (isMonoid (isGroup (snd BoolGroup)))) true false false = refl
assoc' (isSemigroup (isMonoid (isGroup (snd BoolGroup)))) true false true = refl
assoc' (isSemigroup (isMonoid (isGroup (snd BoolGroup)))) true true false = refl
assoc' (isSemigroup (isMonoid (isGroup (snd BoolGroup)))) true true true = refl
identity (IsGroup.isMonoid (isGroup (snd BoolGroup))) false = refl , refl
identity (IsGroup.isMonoid (isGroup (snd BoolGroup))) true = refl , refl
inverse (isGroup (snd BoolGroup)) false = refl , refl
inverse (isGroup (snd BoolGroup)) true = refl , refl