{-# OPTIONS --cubical --safe #-}
module Algebra where

open import Prelude

module _ {a} {A : Type a} (_∙_ : A  A  A) where
  Associative : Type a
  Associative =  x y z  (x  y)  z  x  (y  z)

  Commutative : Type _
  Commutative =  x y  x  y  y  x

  Idempotent : Type _
  Idempotent =  x  x  x  x

Identityˡ : (A  B  B)  A  Type _
Identityˡ _∙_ x =  y  x  y  y

Zeroˡ : (A  B  A)  A  Type _
Zeroˡ _∙_ x =  y  x  y  x

Zeroʳ : (A  B  B)  B  Type _
Zeroʳ _∙_ x =  y  y  x  x

Identityʳ : (A  B  A)  B  Type _
Identityʳ _∙_ x =  y  y  x  y

_Distributesʳ_ : (A  B  B)  (B  B  B)  Type _
_⊗_ Distributesʳ _⊕_ =  x y z  x  (y  z)  (x  y)  (x  z)

_Distributesˡ_ : (B  A  B)  (B  B  B)  Type _
_⊗_ Distributesˡ _⊕_ =  x y z  (x  y)  z  (x  z)  (y  z)

Cancellableˡ : (A  B  C)  A  Type _
Cancellableˡ _⊗_ c =  x y  c  x  c  y  x  y

Cancellableʳ : (A  B  C)  B  Type _
Cancellableʳ _⊗_ c =  x y  x  c  y  c  x  y

Cancellativeˡ : (A  B  C)  Type _
Cancellativeˡ _⊗_ =  c  Cancellableˡ _⊗_ c

Cancellativeʳ : (A  B  C)  Type _
Cancellativeʳ _⊗_ =  c  Cancellableʳ _⊗_ c

module _ {} (𝑆 : Type ) where
  record  Semigroup : Type  where
    infixl 6 _∙_
    field
      _∙_    : 𝑆  𝑆  𝑆
      assoc  :  x y z  (x  y)  z  x  (y  z)

  record  Monoid : Type  where
    infixl 6 _∙_
    field
      _∙_    : 𝑆  𝑆  𝑆
      ε      : 𝑆
      assoc  :  x y z  (x  y)  z  x  (y  z)
      ε∙     :  x  ε  x  x
      ∙ε     :  x  x  ε  x

    semigroup : Semigroup
    semigroup = record
      { _∙_ = _∙_; assoc = assoc }


  record Group : Type  where
    field
      monoid : Monoid
    open Monoid monoid public
    field
      -_ : 𝑆  𝑆
      ∙⁻ :  x  x  - x  ε
      ⁻∙ :  x  - x  x  ε

    open import Path.Reasoning

    cancelˡ : Cancellativeˡ _∙_
    cancelˡ x y z p =
      y ≡˘⟨ ε∙ y 
      ε  y ≡˘⟨ cong (_∙ y) (⁻∙ x) 
      (- x  x)  y ≡⟨ assoc (- x) x y 
      - x  (x  y) ≡⟨ cong (- x ∙_) p 
      - x  (x  z) ≡˘⟨ assoc (- x) x z 
      (- x  x)  z ≡⟨ cong (_∙ z) (⁻∙ x) 
      ε  z ≡⟨ ε∙ z 
      z 

    cancelʳ : Cancellativeʳ _∙_
    cancelʳ x y z p =
      y ≡˘⟨ ∙ε y 
      y  ε ≡˘⟨ cong (y ∙_) (∙⁻ x) 
      y  (x  - x) ≡˘⟨ assoc y x (- x) 
      (y  x)  - x ≡⟨ cong (_∙ - x) p 
      (z  x)  - x ≡⟨ assoc z x (- x) 
      z  (x  - x) ≡⟨ cong (z ∙_) (∙⁻ x) 
      z  ε ≡⟨ ∙ε z 
      z 

  record CommutativeMonoid : Type  where
    field
      monoid : Monoid
    open Monoid monoid public
    field
      comm : Commutative _∙_

  record Semilattice : Type  where
    field
      commutativeMonoid : CommutativeMonoid
    open CommutativeMonoid commutativeMonoid public
    field
      idem : Idempotent _∙_

  record NearSemiring : Type  where
    infixl 6 _+_
    infixl 7 _*_
    field
      _+_ : 𝑆  𝑆  𝑆
      _*_ : 𝑆  𝑆  𝑆
      1# : 𝑆
      0# : 𝑆
      +-assoc : Associative _+_
      *-assoc : Associative _*_
      0+ : Identityˡ _+_ 0#
      +0 : Identityʳ _+_ 0#
      1* : Identityˡ _*_ 1#
      *1 : Identityʳ _*_ 1#
      0* : Zeroˡ _*_ 0#
      ⟨+⟩* : _*_ Distributesˡ _+_

    +-monoid : Monoid
    +-monoid .Monoid._∙_ = _+_
    +-monoid .Monoid.ε = 0#
    +-monoid .Monoid.assoc = +-assoc
    +-monoid .Monoid.ε∙ = 0+
    +-monoid .Monoid.∙ε = +0

    *-monoid : Monoid
    *-monoid .Monoid._∙_ = _*_
    *-monoid .Monoid.ε = 1#
    *-monoid .Monoid.assoc = *-assoc
    *-monoid .Monoid.ε∙ = 1*
    *-monoid .Monoid.∙ε = *1

  record Semiring : Type  where
    field
      nearSemiring : NearSemiring
    open NearSemiring nearSemiring public
    field
      +-comm : Commutative _+_
      *0 : Zeroʳ _*_ 0#
      *⟨+⟩ : _*_ Distributesʳ _+_

  record IdempotentSemiring : Type  where
    field
      semiring : Semiring
    open Semiring semiring public
    field
      +-idem : Idempotent _+_

  record CommutativeSemiring : Type  where
    field
      semiring : Semiring
    open Semiring semiring public
    field
      *-comm : Commutative _*_

  record StarSemiring : Type  where
    field semiring : Semiring
    open Semiring semiring public
    field
      _⋆ : 𝑆  𝑆
      star-iterʳ :  x  x   1# + x * x 
      star-iterˡ :  x  x   1# + x  * x
    _⁺ : 𝑆  𝑆
    x  = x * x 

record LeftSemimodule {ℓ₁ ℓ₂} {𝑆 : Type ℓ₁} (semiring : Semiring 𝑆) (𝑉 : Type ℓ₂) : Type (ℓ₁ ℓ⊔ ℓ₂) where
  open Semiring semiring public
  field semimodule : CommutativeMonoid 𝑉
  open CommutativeMonoid semimodule renaming (_∙_ to _∪_) public
    renaming ( assoc to ∪-assoc
             ; ε∙ to ∅∪
             ; ∙ε to ∪∅
             ; ε to 
             )
  infixr 7 _⋊_
  field
    _⋊_ : 𝑆  𝑉  𝑉
    ⟨*⟩⋊ :  x y z  (x * y)  z  x  (y  z)
    ⟨+⟩⋊ :  x y z  (x + y)  z  (x  z)  (y  z)
    ⋊⟨∪⟩ : _⋊_ Distributesʳ _∪_
    1⋊ : Identityˡ _⋊_ 1#
    0⋊ :  x  0#  x  
    ⋊∅ :  x  x    

record MonoidHomomorphism_⟶_
        {ℓ₁ ℓ₂} {𝑆 : Type ℓ₁} {𝑇 : Type ℓ₂}
        (from : Monoid 𝑆)
        (to   : Monoid 𝑇)
      : Type (ℓ₁ ℓ⊔ ℓ₂) where
  open Monoid from
  open Monoid to
    renaming ( _∙_ to _⊙_
             ; ε to )
  field
    f : 𝑆  𝑇
    ∙-homo :  x y  f (x  y)  f x  f y
    ε-homo : f ε  

record SemimoduleHomomorphism[_]_⟶_
        {ℓ₁ ℓ₂ ℓ₃} {𝑆 : Type ℓ₁} {𝑉₁ : Type ℓ₂} {𝑉₂ : Type ℓ₃}
        (rng : Semiring 𝑆)
        (from : LeftSemimodule rng 𝑉₁)
        (to : LeftSemimodule rng 𝑉₂) : Type (ℓ₁ ℓ⊔ ℓ₂ ℓ⊔ ℓ₃) where

  open Semiring rng
  open LeftSemimodule from using (_⋊_; monoid)
  open LeftSemimodule to using () renaming (_⋊_ to _⋊′_; monoid to monoid′)

  field mon-homo : MonoidHomomorphism monoid  monoid′

  open MonoidHomomorphism_⟶_ mon-homo public

  field ⋊-homo :  r x  f (r  x)  r ⋊′ f x


module _ {ℓ₁ ℓ₂} (𝐹 : Type ℓ₁  Type ℓ₂) where

  record Functor : Type (ℓsuc ℓ₁ ℓ⊔ ℓ₂) where
    field
      map : (A  B)  𝐹 A  𝐹 B
      map-id : map (id {ℓ₁} {A})  id
      map-comp : (f : B  C)  (g : A  B)  map (f  g)  map f  map g

  record Applicative : Type (ℓsuc ℓ₁ ℓ⊔ ℓ₂) where
    infixl 5 _<*>_
    field
      pure : A  𝐹 A
      _<*>_ : 𝐹 (A  B)  𝐹 A  𝐹 B

    map : (A  B)  𝐹 A  𝐹 B
    map f = _<*>_ (pure f)
    field
      pure-homo : (f : A  B)  (x : A)  pure f <*> (pure x)  pure (f x)
      <*>-interchange : (u : 𝐹 (A  B))  (y : A)  u <*> pure y  map (_$ y) u
      <*>-comp : (u : 𝐹 (B  C))  (v : 𝐹 (A  B))  (w : 𝐹 A)  pure _∘′_ <*> u <*> v <*> w  u <*> (v <*> w)


  record Monad : Type (ℓsuc ℓ₁ ℓ⊔ ℓ₂) where
    infixl 1 _>>=_
    field
      _>>=_ : 𝐹 A  (A  𝐹 B)  𝐹 B
      return : A  𝐹 A

      >>=-idˡ : (f : A  𝐹 B)  (x : A)  (return x >>= f)  f x
      >>=-idʳ : (x : 𝐹 A)  (x >>= return)  x
      >>=-assoc : (xs : 𝐹 A) (f : A  𝐹 B) (g : B  𝐹 C)  ((xs >>= f) >>= g)  (xs >>=  x  f x >>= g))

    mmap : (A  B)  𝐹 A  𝐹 B
    mmap f xs = xs >>= (return  f)

  record Comonad : Type (ℓsuc ℓ₁ ℓ⊔ ℓ₂) where
    field
      extend : (𝐹 A  B)  𝐹 A  𝐹 B
      extract : 𝐹 A  A

    infixl 1 _=>>_
    _=>>_ : 𝐹 A  (𝐹 A  B)  𝐹 B
    _=>>_ = flip extend

    cmap : (A  B)  𝐹 A  𝐹 B
    cmap f xs = xs =>> (f  extract)

    -- liftA2 : (A → B → C) → 𝐹 A → 𝐹 B → 𝐹 C
    -- liftA2 f xs ys = xs =>> λ x → {!ys =>> λ y → {!!}!}


  record Alternative : Type (ℓsuc ℓ₁ ℓ⊔ ℓ₂) where
    field
      applicative : Applicative
    open Applicative applicative public
    field
      0# : 𝐹 A
      _<|>_ : 𝐹 A  𝐹 A  𝐹 A
      <|>-idˡ : (x : 𝐹 A)  0# <|> x  x
      <|>-idʳ : (x : 𝐹 A)  x <|> 0#  x
      0-annˡ : (x : 𝐹 A)  0# <*> x  0# {B}
      <|>-distrib : (x y : 𝐹 (A  B))  (z : 𝐹 A)  (x <|> y) <*> z  (x <*> z) <|> (y <*> z)

--   record MonadPlus ℓ₁ ℓ₂ : Type (ℓsuc (ℓ₁ ℓ⊔ ℓ₂)) where
--     field
--       monad : Monad ℓ₁ ℓ₂
--     open Monad monad public
--     field
--       0# : 𝐹 A
--       _<|>_ : 𝐹 A → 𝐹 A → 𝐹 A
--       <|>-idˡ : (x : 𝐹 A) → 0# <|> x ≡ x
--       <|>-idʳ : (x : 𝐹 A) → x <|> 0# ≡ x
--       0-annˡ : (x : A → 𝐹 B) → (0# >>= x) ≡ 0#
--       <|>-distrib : (x y : 𝐹 A) → (z : A → 𝐹 B) → ((x <|> y) >>= z) ≡ (x >>= z) <|> (y >>= z)

--   Endo : Type a → Type a
--   Endo A = A → A

--   endoMonoid : ∀ {a} → Type a → Monoid a
--   endoMonoid A .Monoid.𝑆 = Endo A
--   endoMonoid A .Monoid.ε x = x
--   endoMonoid A .Monoid._∙_ f g x = f (g x)
--   endoMonoid A .Monoid.assoc _ _ _ = refl
--   endoMonoid A .Monoid.ε∙ _ = refl
--   endoMonoid A .Monoid.∙ε _ = refl

--   record Foldable ℓ₁ ℓ₂ : Type (ℓsuc (ℓ₁ ℓ⊔ ℓ₂)) where
--     field
--       𝐹 : Type ℓ₁ → Type ℓ₂
--     open Monoid ⦃ ... ⦄
--     field
--       foldMap : {A : Type ℓ₁} ⦃ _ : Monoid ℓ₁ ⦄ → (A → 𝑆) → 𝐹 A → 𝑆
--     foldr : {A B : Type ℓ₁} → (A → B → B) → B → 𝐹 A → B
--     foldr f b xs = foldMap ⦃ endoMonoid _ ⦄ f xs b
--     
record IsSetMonad {ℓ₁} {ℓ₂} (𝐹 : Type ℓ₁  Type ℓ₂) : Type (ℓsuc ℓ₁ ℓ⊔ ℓ₂) where
  infixl 1 _>>=_
  field
    _>>=_ : 𝐹 A  (A  𝐹 B)  𝐹 B
    return : A  𝐹 A

    trunc : isSet A  isSet (𝐹 A)

    >>=-idˡ   : isSet B  (f : A  𝐹 B)  (x : A)  (return x >>= f)  f x
    >>=-idʳ   : isSet A  (x : 𝐹 A)  (x >>= return)  x
    >>=-assoc : isSet C  (xs : 𝐹 A) (f : A  𝐹 B) (g : B  𝐹 C)  ((xs >>= f) >>= g)  (xs >>=  x  f x >>= g))

record SetMonad ℓ₁ ℓ₂ : Type (ℓsuc (ℓ₁ ℓ⊔ ℓ₂)) where
  field
    𝐹 : Type ℓ₁  Type ℓ₂
    isSetMonad : IsSetMonad 𝐹
  open IsSetMonad isSetMonad public

record SetMonadHomomorphism_⟶_
        {ℓ₁ ℓ₂ ℓ₃}
        (from : SetMonad ℓ₁ ℓ₂)
        (to : SetMonad ℓ₁ ℓ₃) : Type (ℓsuc ℓ₁ ℓ⊔ ℓ₂ ℓ⊔ ℓ₃) where
  module F = SetMonad from
  module T = SetMonad to

  field
    f : F.𝐹 A  T.𝐹 A
    >>=-homo : (xs : F.𝐹 A) (k : A  F.𝐹 B)  (f xs T.>>= (f  k))  f (xs F.>>= k)
    return-homo : (x : A)  f (F.return x)  T.return x

module _ {ℓ₁} {𝑆 : Type ℓ₁} (monoid : Monoid 𝑆) {ℓ₂} (𝐹 : 𝑆  Type ℓ₂  Type ℓ₂) where
  open Monoid monoid
  record GradedMonad : Type (ℓ₁ ℓ⊔ ℓsuc ℓ₂) where
    field
      return  : A  𝐹 ε A
      _>>=_ :  {x y}  𝐹 x A  (A  𝐹 y B)  𝐹 (x  y) B

    _<=<_ :  {x y}  (B  𝐹 y C)  (A  𝐹 x B)  A  𝐹 (x  y) C
    (g <=< f) x = f x >>= g

    _>=>_ :  {x y}  (A  𝐹 x B)  (B  𝐹 y C)  A  𝐹 (x  y) C
    (f >=> g) x = f x >>= g

    field
      >>=-idˡ :  {s} (f : A  𝐹 s B)  (x : A)  (return x >>= f) ≡[ i  𝐹 (ε∙ s i) B ]≡ (f x)
      >>=-idʳ :  {s} (x : 𝐹 s A)  (x >>= return) ≡[ i  𝐹 (∙ε s i) A ]≡ x
      >>=-assoc :  {x y z} (xs : 𝐹 x A) (f : A  𝐹 y B) (g : B  𝐹 z C)  ((xs >>= f) >>= g) ≡[ i  𝐹 (assoc x y z i) C ]≡ (xs >>=  x  f x >>= g))

    infixr 0 proven-bind

    proven-bind :  {x y z}  𝐹 x A  (A  𝐹 y B)  (x  y)  z  𝐹 z B
    proven-bind xs f proof = subst (flip 𝐹 _) proof (xs >>= f)

    syntax proven-bind xs f proof = xs >>=[ proof ] f

    infixr 0 proven-do
    proven-do :  {x y z}  𝐹 x A  (A  𝐹 y B)  (x  y)  z  𝐹 z B
    proven-do = proven-bind

    syntax proven-do xs  x  e) proof = x  xs [ proof ] e

    map :  {x}  (A  B)  𝐹 x A  𝐹 x B
    map f xs = xs >>=[ ∙ε _ ] (return  f)

    _<*>_ :  {x y}  𝐹 x (A  B)  𝐹 y A  𝐹 (x  y) B
    fs <*> xs = fs >>= flip map xs

    _>>=ε_ :  {x}  𝐹 x A  (A  𝐹 ε B)  𝐹 x B
    xs >>=ε f = xs >>=[ ∙ε _ ] f

  record GradedComonad : Type (ℓ₁ ℓ⊔ ℓsuc ℓ₂) where
    field
      extract : 𝐹 ε A  A
      extend  :  {x y}  (𝐹 y A  B)  𝐹 (x  y) A  𝐹 x B

    extend[_] :  {x y z}  x  y  z  (𝐹 y A  B)  𝐹 z A  𝐹 x B
    extend[ p ] k = subst  w  (𝐹 w _  _)) p (extend k)

    _=>>_ :  {x y}  𝐹 (x  y) A  (𝐹 y A  B)  𝐹 x B
    _=>>_ = flip extend

    proven-cobind :  {x y z}  (𝐹 y A  B)  x  y  z  𝐹 z A  𝐹 x B
    proven-cobind k prf = extend[ prf ] k

    syntax proven-cobind f prf xs = xs =>>[ prf ] f

    _=<=_ :  {x y}  (𝐹 x B  C)  (𝐹 y A  B)  𝐹 (x  y) A  C
    (g =<= f) x = g (extend f x)

    _=>=_ :  {x y}  (𝐹 y A  B)  (𝐹 x B  C)  𝐹 (x  y) A  C
    _=>=_ = flip _=<=_


    field
      =>>-idˡ :  {x} {B : Type ℓ₂}  (f : 𝐹 x A  B)  PathP  i  𝐹 (ε∙ x i) A  B) (extract =<= f) f
      =>>-idʳ :  {x} {B : Type ℓ₂}  (f : 𝐹 x A  B)  PathP  i  𝐹 (∙ε x i) A  B) (f =<= extract) f
      =>>-assoc :  {x y z} {D : Type ℓ₂}  (f : 𝐹 x C  D) (g : 𝐹 y B  C) (h : 𝐹 z A  B) 
            PathP  i  𝐹 (assoc x y z i) A  D) ((f =<= g) =<= h) (f =<= (g =<= h))

    {-# INLINE proven-cobind #-}
    {-# INLINE extend[_] #-}

    infixr 1 codo-syntax
    codo-syntax :  {x y}  𝐹 (x  y) A  (𝐹 y A  B)  𝐹 x B
    codo-syntax = _=>>_

    syntax codo-syntax xs  x  r) = x  xs ; r

    infixr 1 proven-codo-syntax
    proven-codo-syntax :  {x y z}  𝐹 z A  (𝐹 y A  B)  x  y  z  𝐹 x B
    proven-codo-syntax xs k p = proven-cobind k p xs

    syntax proven-codo-syntax xs  x  r) prf = x  xs [ prf  r

    map :  {x}  (A  B)  𝐹 x A  𝐹 x B
    map f = extend[ ∙ε _ ] (f ∘′ extract)
    {-# INLINE map #-}

    open import Cubical.Foundations.Prelude using (fromPathP; transportRefl; substRefl)

    map-id :  {x}  (xs : 𝐹 x A)  map id xs  xs
    map-id xs = cong (_$ xs) (fromPathP (=>>-idʳ id))

    extract-extend :  {x} (xs : 𝐹 x A)  extract (extend[ ε∙ x ] id xs)  xs
    extract-extend {A = A} {x = x} xs =
      cong extract (J  y p  (xs : 𝐹 y A)  extend[ p ] id xs  extend id (subst (flip 𝐹 A) (sym p) xs))  xs  cong (_$ xs) (transportRefl (extend id))  ; cong (extend id) (sym (transportRefl xs))) (ε∙ x) xs ) ;
      sym (transportRefl _) ;
      cong (_$ xs) (fromPathP (=>>-idˡ {x = x} id))

    open import Path.Reasoning

    -- map-comp : ∀ {x} (g : B → C) (f : A → B) → map {x = x} g ∘ map f ≡ map {x = x} (g ∘ f)
    -- map-comp {x = x} g f = funExt λ xs →
    --   map g (map f xs) ≡⟨⟩
    --   extend[ ∙ε x ] (g ∘ extract) (extend[ ∙ε x ] (f ∘ extract) xs) ≡⟨ {!!} ⟩
    --   extend[ ∙ε x ] (g ∘ f ∘ extract) xs ≡⟨⟩
    --   map (g ∘ f) xs ∎

record SGradedComonad {ℓ₁ ℓ₂} {𝑆 : Type ℓ₁} (semiring : Semiring 𝑆) (𝐹 : 𝑆  Type ℓ₂  Type ℓ₂) : Type (ℓ₁ ℓ⊔ ℓsuc ℓ₂) where
  open Semiring semiring
  field gradedComonad : GradedComonad *-monoid 𝐹
  open GradedComonad gradedComonad
  field
    pure  :  {x}  𝐹 x A
    _<*>_ :  {x}  𝐹 x (A  B)  𝐹 x A  𝐹 x B
    separate :  {x y}  𝐹 (x + y) A  𝐹 x A × 𝐹 y A

record MatchedPair {ℓ₁ ℓ₂} {𝑆 : Type ℓ₁} {𝐸 : Type ℓ₂} (R : Monoid 𝑆) (E : Monoid 𝐸) : Type (ℓ₁ ℓ⊔ ℓ₂) where
  open Monoid R using ()    renaming (_∙_ to _*_; ε to r1)
  open Monoid E using (_∙_) renaming (ε to e1)

  field
    ι : 𝑆  𝐸  𝑆
    κ : 𝑆  𝐸  𝐸

    law1 :  x  ι x e1  x
    law2 :  x  ι r1 x  r1
    law3 :  x  κ r1 x  x
    law4 :  x  κ x e1  e1

    law5 :  x y z  ι x (y  z)  ι (ι x y) z
    law6 :  x y z  ι (x * y) z  ι x (κ y z) * ι y z
    law7 :  x y z  κ (x * y) z  κ x (κ y z)
    law8 :  x y z  κ x (y  z)  κ x y  κ (ι x y) z