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

module Categories.Tactic.Monoidal.Free where

open import Level using (Level)
open import Data.List.Base using (List; []; _∷_; _++_)
open import Data.List.Properties using (++-assoc; ++-identityʳ)
open import Relation.Binary.PropositionalEquality
  using (_≡_; refl; sym; trans; cong; cong₂)

-- Free structural expressions for monoidal coherence.  These are the objects
-- and arrows generated only by atoms, the monoidal unit, tensor, associators,
-- and unitors.
module Free {a : Level} (Atom : Set a) where

  infixr 9 _⊗_

  data Ob : Set a where
    ‹_› : Atom  Ob
    I   : Ob
    _⊗_ : Ob  Ob  Ob

  infixr 9 _∘_
  infixr 10 _⊗₁_

  data _⇒_ : Ob  Ob  Set a where
    idₘ  :  {X}      X  X
    _∘_  :  {X Y Z}  Y  Z  X  Y  X  Z
    _⊗₁_ :  {X Y Z W}  X  Y  Z  W  (X  Z)  (Y  W)
    α⇒   :  {X Y Z}  ((X  Y)  Z)  (X  (Y  Z))
    α⇐   :  {X Y Z}  (X  (Y  Z))  ((X  Y)  Z)
    λ⇒   :  {X}  (I  X)  X
    λ⇐   :  {X}  X  (I  X)
    ρ⇒   :  {X}  (X  I)  X
    ρ⇐   :  {X}  X  (X  I)

  invert :  {X Y}  X  Y  Y  X
  invert idₘ       = idₘ
  invert (g  f)   = invert f  invert g
  invert (f ⊗₁ g)  = invert f ⊗₁ invert g
  invert α⇒        = α⇐
  invert α⇐        = α⇒
  invert λ⇒        = λ⇐
  invert λ⇐        = λ⇒
  invert ρ⇒        = ρ⇐
  invert ρ⇐        = ρ⇒

  nf : Ob  List Atom
  nf  x    = x  []
  nf I       = []
  nf (X  Y) = nf X ++ nf Y

  ⌜_⌝ : List Atom  Ob
   []      = I
   x  xs  =  x    xs 

  nf-⌜⌝ : (w : List Atom)  nf  w   w
  nf-⌜⌝ []       = refl
  nf-⌜⌝ (x  xs) = cong (x ∷_) (nf-⌜⌝ xs)

  module NormalForm where
    assocₙ : (X Y Z : Ob)  nf ((X  Y)  Z)  nf (X  (Y  Z))
    assocₙ X Y Z = ++-assoc (nf X) (nf Y) (nf Z)

    assocₙ⁻¹ : (X Y Z : Ob)  nf (X  (Y  Z))  nf ((X  Y)  Z)
    assocₙ⁻¹ X Y Z = sym (assocₙ X Y Z)

    unitʳₙ : (X : Ob)  nf (X  I)  nf X
    unitʳₙ X = ++-identityʳ (nf X)

    unitʳₙ⁻¹ : (X : Ob)  nf X  nf (X  I)
    unitʳₙ⁻¹ X = sym (unitʳₙ X)

  ⇒⇒nf :  {X Y}  X  Y  nf X  nf Y
  ⇒⇒nf idₘ              = refl
  ⇒⇒nf (g  f)          = trans (⇒⇒nf f) (⇒⇒nf g)
  ⇒⇒nf (f ⊗₁ g)         = cong₂ _++_ (⇒⇒nf f) (⇒⇒nf g)
  ⇒⇒nf (α⇒ {X} {Y} {Z}) = NormalForm.assocₙ X Y Z
  ⇒⇒nf (α⇐ {X} {Y} {Z}) = NormalForm.assocₙ⁻¹ X Y Z
  ⇒⇒nf λ⇒               = refl
  ⇒⇒nf λ⇐               = refl
  ⇒⇒nf (ρ⇒ {X})         = NormalForm.unitʳₙ X
  ⇒⇒nf (ρ⇐ {X})         = NormalForm.unitʳₙ⁻¹ X