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

module Fragment.Extensions.CSemigroup.Monomial where

open import Fragment.Equational.Theory.Bundles

open import Fragment.Algebra.Signature
open import Fragment.Algebra.Free Σ-magma hiding (_~_)
open import Fragment.Algebra.Homomorphism Σ-magma
open import Fragment.Algebra.Algebra Σ-magma
  using (Algebra; IsAlgebra; Interpretation; Congruence)
open import Fragment.Equational.Model Θ-csemigroup

open import Fragment.Setoid.Morphism using (_↝_)

open import Fragment.Extensions.CSemigroup.Nat

open import Data.Nat using (; zero; suc; _≟_)
open import Data.Fin using (Fin; #_; zero; suc; toℕ; fromℕ)
open import Data.Vec using (Vec; []; _∷_)
open import Data.Vec.Relation.Binary.Pointwise.Inductive using ([]; _∷_)

open import Relation.Nullary using (yes ; no)
open import Relation.Binary.PropositionalEquality as PE using (_≡_)
open import Relation.Binary using (Setoid; IsEquivalence)
import Relation.Binary.Reasoning.Setoid as Reasoning

data Monomial :   Set where
  leaf :  {n}  ℕ⁺  Monomial (suc n)
  skip :  {n}  Monomial n  Monomial (suc n)
  cons :  {n}  ℕ⁺  Monomial n  Monomial (suc n)

lift :  {n}  Monomial n  Monomial (suc n)
lift (leaf x)    = leaf x
lift (skip x)    = skip (lift x)
lift (cons x xs) = cons x (lift xs)

_⊗_ :  {n}  Monomial n  Monomial n  Monomial n
leaf x     leaf y    = leaf (x + y)
leaf x     skip y    = cons x y
leaf x     cons y ys = cons (x + y) ys
skip x     leaf y    = cons y x
skip x     skip y    = skip (x  y)
skip x     cons y ys = cons y (x  ys)
cons x xs  leaf y    = cons (x + y) xs
cons x xs  skip y    = cons x (xs  y)
cons x xs  cons y ys = cons (x + y) (xs  ys)

⊗-cong :  {n} {x y z w : Monomial n}
          x  y  z  w  x  z  y  w
⊗-cong = PE.cong₂ _⊗_

⊗-comm :  {n} (x y : Monomial n)  x  y  y  x
⊗-comm (leaf x)    (leaf y)    = PE.cong leaf (+-comm x y)
⊗-comm (leaf x)    (skip y)    = PE.refl
⊗-comm (leaf x)    (cons y ys) = PE.cong₂ cons (+-comm x y) PE.refl
⊗-comm (skip x)    (leaf y)    = PE.refl
⊗-comm (skip x)    (skip y)    = PE.cong skip (⊗-comm x y)
⊗-comm (skip x)    (cons y ys) = PE.cong (cons y) (⊗-comm x ys)
⊗-comm (cons x xs) (leaf y)    = PE.cong₂ cons (+-comm x y) PE.refl
⊗-comm (cons x xs) (skip y)    = PE.cong (cons x) (⊗-comm xs y)
⊗-comm (cons x xs) (cons y ys) = PE.cong₂ cons (+-comm x y) (⊗-comm xs ys)

⊗-assoc :  {n} (x y z : Monomial n)  (x  y)  z  x  (y  z)
⊗-assoc (leaf x)    (leaf y)    (leaf z)    = PE.cong leaf (+-assoc x y z)
⊗-assoc (leaf x)    (leaf y)    (skip z)    = PE.refl
⊗-assoc (leaf x)    (leaf y)    (cons z zs) = PE.cong₂ cons (+-assoc x y z) PE.refl
⊗-assoc (leaf x)    (skip y)    (leaf z)    = PE.refl
⊗-assoc (leaf x)    (skip y)    (skip z)    = PE.refl
⊗-assoc (leaf x)    (skip y)    (cons z zs) = PE.refl
⊗-assoc (leaf x)    (cons y ys) (leaf z)    = PE.cong₂ cons (+-assoc x y z) PE.refl
⊗-assoc (leaf x)    (cons y ys) (skip z)    = PE.refl
⊗-assoc (leaf x)    (cons y ys) (cons z zs) = PE.cong₂ cons (+-assoc x y z) PE.refl
⊗-assoc (skip x)    (leaf y)    (leaf z)    = PE.refl
⊗-assoc (skip x)    (leaf y)    (skip z)    = PE.refl
⊗-assoc (skip x)    (leaf y)    (cons z zs) = PE.refl
⊗-assoc (skip x)    (skip y)    (leaf z)    = PE.refl
⊗-assoc (skip x)    (skip y)    (skip z)    = PE.cong skip (⊗-assoc x y z)
⊗-assoc (skip x)    (skip y)    (cons z zs) = PE.cong (cons z) (⊗-assoc x y zs)
⊗-assoc (skip x)    (cons y ys) (leaf z)    = PE.refl
⊗-assoc (skip x)    (cons y ys) (skip z)    = PE.cong (cons y) (⊗-assoc x ys z)
⊗-assoc (skip x)    (cons y ys) (cons z zs) = PE.cong (cons (y + z)) (⊗-assoc x ys zs)
⊗-assoc (cons x xs) (leaf y)    (leaf z)    = PE.cong₂ cons (+-assoc x y z) PE.refl
⊗-assoc (cons x xs) (leaf y)    (skip z)    = PE.refl
⊗-assoc (cons x xs) (leaf y)    (cons z zs) = PE.cong₂ cons (+-assoc x y z) PE.refl
⊗-assoc (cons x xs) (skip y)    (leaf z)    = PE.refl
⊗-assoc (cons x xs) (skip y)    (skip z)    = PE.cong (cons x) (⊗-assoc xs y z)
⊗-assoc (cons x xs) (skip y)    (cons z zs) = PE.cong (cons (x + z)) (⊗-assoc xs y zs)
⊗-assoc (cons x xs) (cons y ys) (leaf z)    = PE.cong₂ cons (+-assoc x y z) PE.refl
⊗-assoc (cons x xs) (cons y ys) (skip z)    = PE.cong (cons (x + y)) (⊗-assoc xs ys z)
⊗-assoc (cons x xs) (cons y ys) (cons z zs) = PE.cong₂ cons (+-assoc x y z) (⊗-assoc xs ys zs)

module _ (n : ) where

  private

    Monomial/≡ : Setoid _ _
    Monomial/≡ = PE.setoid (Monomial n)

    Monomial⟦_⟧ : Interpretation Monomial/≡
    Monomial⟦   (x  y  []) = x  y

    Monomial⟦_⟧-cong : Congruence Monomial/≡ Monomial⟦_⟧
    Monomial⟦  ⟧-cong (p  q  []) = PE.cong₂ _⊗_ p q

    Monomial/≡-isAlgebra : IsAlgebra (Monomial/≡)
    Monomial/≡-isAlgebra = record { ⟦_⟧     = Monomial⟦_⟧
                                  ; ⟦⟧-cong = Monomial⟦_⟧-cong
                                  }

    Monomial/≡-algebra : Algebra
    Monomial/≡-algebra =
      record { ∥_∥/≈           = Monomial/≡
             ; ∥_∥/≈-isAlgebra = Monomial/≡-isAlgebra
             }

    Monomial/≡-models : Models Monomial/≡-algebra
    Monomial/≡-models comm θ  = ⊗-comm (θ (# 0)) (θ (# 1))
    Monomial/≡-models assoc θ = ⊗-assoc (θ (# 0)) (θ (# 1)) (θ (# 2))

    Monomial/≡-isModel : IsModel Monomial/≡
    Monomial/≡-isModel =
      record { isAlgebra = Monomial/≡-isAlgebra
             ; models    = Monomial/≡-models
             }

  J' : Model
  J' = record { ∥_∥/≈   = Monomial/≡
              ; isModel = Monomial/≡-isModel
              }

private

  module _ {n : } where

    open Setoid  J n ∥/≈ using (_≈_)

    _·_ :  J n    J n    J n 
    x · y = term  (x  y  [])

    ·-cong :  {x y z w}  x  y  z  w  x · z  y · w
    ·-cong p q = cong  (p  q  [])

    ·-comm :  x y  x · y  y · x
    ·-comm x y = axiom comm (env {A =  J n ∥ₐ} (x  y  []))

    ·-assoc :  x y z  (x · y) · z  x · (y · z)
    ·-assoc x y z = axiom assoc (env {A =  J n ∥ₐ} (x  y  z  []))

  module _ (n : ) where

    open Setoid  J (suc n) ∥/≈ using (_≈_)

    exp : ℕ⁺   J (suc n) 
    exp one     = atomise n
    exp (suc k) = atomise n · exp k

    exp-hom :  {j k}  exp j · exp k  exp (j + k)
    exp-hom {one}   {k} = refl
    exp-hom {suc j} {k} = begin
        (atomise n · exp j) · exp k
      ≈⟨ ·-assoc (atomise n) (exp j) (exp k) 
        atomise n · (exp j · exp k)
      ≈⟨ ·-cong refl exp-hom 
        atomise n · exp (j + k)
      
      where open Reasoning  J (suc n) ∥/≈

  ∣syn∣ :  {n}   J' n    J n 
  ∣syn∣ {suc n} (leaf k)    = exp n k
  ∣syn∣ {suc n} (skip x)    =  raise  (∣syn∣ x)
  ∣syn∣ {suc n} (cons k xs) = exp n k ·  raise  (∣syn∣ xs)

  ∣syn∣-cong :  {n}  Congruent _≡_ ≈[ J n ] ∣syn∣
  ∣syn∣-cong {n} p = Setoid.reflexive  J n ∥/≈ (PE.cong ∣syn∣ p)

  ∣syn∣⃗ :  {n}   J' n ∥/≈   J n ∥/≈
  ∣syn∣⃗ {n} = record { ∣_∣      = ∣syn∣ {n}
                      ; ∣_∣-cong = ∣syn∣-cong {n}
                      }

  ∣syn∣-hom :  {n}  Homomorphic  J' n ∥ₐ  J n ∥ₐ ∣syn∣
  ∣syn∣-hom {suc n}  (leaf x     leaf y     []) = exp-hom n
  ∣syn∣-hom {suc n}  (leaf x     skip y     []) = refl
  ∣syn∣-hom {suc n}  (leaf x     cons y ys  []) = begin
      exp n x · (exp n y ·  raise  (∣syn∣ ys))
    ≈⟨ sym (·-assoc (exp n x) (exp n y) _) 
      (exp n x · exp n y) ·  raise  (∣syn∣ ys)
    ≈⟨ ·-cong (exp-hom n) refl 
      exp n (x + y) ·  raise  (∣syn∣ ys)
    
    where open Reasoning  J (suc n) ∥/≈
  ∣syn∣-hom {suc n}  (skip x     leaf y     []) = ·-comm _ (exp n y)
  ∣syn∣-hom {suc n}  (skip x     skip y     []) = begin
       raise  (∣syn∣ x) ·  raise  (∣syn∣ y)
    ≈⟨  raise ∣-hom  (∣syn∣ x  ∣syn∣ y  []) 
       raise  (∣syn∣ x · ∣syn∣ y)
    ≈⟨  raise ∣-cong (∣syn∣-hom  (x  y  [])) 
       raise  (∣syn∣ (x  y))
    
    where open Reasoning  J (suc n) ∥/≈
  ∣syn∣-hom {suc n}  (skip x     cons y ys  []) = begin
       raise  (∣syn∣ x) · (exp n y ·  raise  (∣syn∣ ys))
    ≈⟨ sym (·-assoc _ (exp n y) _) 
      ( raise  (∣syn∣ x) · exp n y) ·  raise  (∣syn∣ ys)
    ≈⟨ ·-cong (·-comm _ (exp n y)) refl 
      (exp n y ·  raise  (∣syn∣ x)) ·  raise  (∣syn∣ ys)
    ≈⟨ ·-assoc (exp n y) _ _ 
      exp n y · ( raise  (∣syn∣ x) ·  raise  (∣syn∣ ys))
    ≈⟨ ·-cong refl ( raise ∣-hom  (∣syn∣ x  ∣syn∣ ys  [])) 
      exp n y ·  raise  (∣syn∣ x · ∣syn∣ ys)
    ≈⟨ ·-cong refl ( raise ∣-cong (∣syn∣-hom  (x  ys  []))) 
      exp n y ·  raise  (∣syn∣ (x  ys))
    
    where open Reasoning  J (suc n) ∥/≈
  ∣syn∣-hom {suc n}  (cons x xs  leaf y     []) = begin
      (exp n x ·  raise  (∣syn∣ xs)) · exp n y
    ≈⟨ ·-assoc (exp n x) _ (exp n y) 
      exp n x · ( raise  (∣syn∣ xs) · exp n y)
    ≈⟨ ·-cong refl (·-comm _ (exp n y)) 
      exp n x · (exp n y ·  raise  (∣syn∣ xs))
    ≈⟨ sym (·-assoc (exp n x) (exp n y) _) 
      (exp n x · exp n y) ·  raise  (∣syn∣ xs)
    ≈⟨ ·-cong (exp-hom n) refl 
      exp n (x + y) ·  raise  (∣syn∣ xs)
    
    where open Reasoning  J (suc n) ∥/≈
  ∣syn∣-hom {suc n}  (cons x xs  skip y     []) = begin
      (exp n x ·  raise  (∣syn∣ xs)) ·  raise  (∣syn∣ y)
    ≈⟨ ·-assoc (exp n x) _ _ 
      exp n x · ( raise  (∣syn∣ xs) ·  raise  (∣syn∣ y))
    ≈⟨ ·-cong refl ( raise ∣-hom  (∣syn∣ xs  ∣syn∣ y  [])) 
      exp n x ·  raise  (∣syn∣ xs · ∣syn∣ y)
    ≈⟨ ·-cong refl ( raise ∣-cong (∣syn∣-hom  (xs  y  []))) 
      exp n x ·  raise  (∣syn∣ (xs  y))
    
    where open Reasoning  J (suc n) ∥/≈
  ∣syn∣-hom {suc n}  (cons x xs  cons y ys  []) = begin
      (exp n x ·  raise  (∣syn∣ xs)) · (exp n y ·  raise  (∣syn∣ ys))
    ≈⟨ ·-assoc (exp n x) _ _ 
      exp n x · ( raise  (∣syn∣ xs) · (exp n y ·  raise  (∣syn∣ ys)))
    ≈⟨ ·-cong refl (sym (·-assoc _ (exp n y) _)) 
      exp n x · (( raise  (∣syn∣ xs) · exp n y) ·  raise  (∣syn∣ ys))
    ≈⟨ ·-cong refl (·-cong (·-comm ( raise  (∣syn∣ xs)) _) refl) 
      exp n x · ((exp n y ·  raise  (∣syn∣ xs)) ·  raise  (∣syn∣ ys))
    ≈⟨ ·-cong refl (·-assoc (exp n y) _ _) 
      exp n x · (exp n y · ( raise  (∣syn∣ xs) ·  raise  (∣syn∣ ys)))
    ≈⟨ sym (·-assoc (exp n x) (exp n y) _) 
      (exp n x · exp n y) · ( raise  (∣syn∣ xs) ·  raise  (∣syn∣ ys))
    ≈⟨ ·-cong (exp-hom n) ( raise ∣-hom  (∣syn∣ xs  ∣syn∣ ys  [])) 
      exp n (x + y) ·  raise  (∣syn∣ xs · ∣syn∣ ys)
    ≈⟨ ·-cong refl ( raise ∣-cong (∣syn∣-hom  (xs  ys  []))) 
      exp n (x + y) ·  raise  (∣syn∣ (xs  ys))
    
    where open Reasoning  J (suc n) ∥/≈

syn :  {n}   J' n ∥ₐ   J n ∥ₐ
syn = record { ∣_∣⃗    = ∣syn∣⃗
             ; ∣_∣-hom = ∣syn∣-hom
             }

private

  tab :  {n}  Fin n   J' n 
  tab {suc zero}    zero    = leaf one
  tab {suc (suc n)} zero    = skip (tab {suc n} zero)
  tab {suc (suc n)} (suc k) = lift (tab {suc n} k)

norm :  {n}   J n ∥ₐ   J' n ∥ₐ
norm {n} = interp (J' n) tab

private

  step-exp :  {n}  (k : ℕ⁺)
              ≈[ J (suc (suc n)) ] (exp (suc n) k)
                                    ( step  (exp n k))
  step-exp one     = refl
  step-exp (suc k) = ·-cong refl (step-exp k)

  step-lift :  {n}  (x :  J' n )
               ≈[ J (suc n) ] ( syn  (lift x))
                               ( step  ( syn  x))
  step-lift {suc n} (leaf x)    = step-exp x
  step-lift {suc n} (skip x)    = begin
       raise  ( syn  (lift x))
    ≈⟨  raise ∣-cong (step-lift x) 
       raise  ( step  ( syn  x))
    ≈⟨ sym (step-raise {x =  syn  x}) 
       step  ( raise  ( syn  x))
    
    where open Reasoning  J (suc (suc n)) ∥/≈
  step-lift {suc n} (cons x xs) = begin
      exp (suc n) x ·  raise  ( syn  (lift xs))
    ≈⟨ ·-cong refl ( raise ∣-cong (step-lift xs)) 
      exp (suc n) x ·  raise  ( step  ( syn  xs))
    ≈⟨ ·-cong (step-exp x) (sym (step-raise {x =  syn  xs})) 
       step  (exp n x) ·  step  ( raise  ( syn  xs))
    ≈⟨  step ∣-hom  (exp n x  ( raise  ( syn  xs))  []) 
       step  (exp n x ·  raise  ( syn  xs))
    
    where open Reasoning  J (suc (suc n)) ∥/≈

  syn-tab :  {n}  (k : Fin n)
             ≈[ J n ] ( syn  (tab k)) (atom (dyn k))
  syn-tab {suc zero}    zero    = refl
  syn-tab {suc (suc n)} zero    =  raise ∣-cong (syn-tab zero)
  syn-tab {suc (suc n)} (suc k) = begin
       syn  (lift (tab {suc n} k))
    ≈⟨ step-lift (tab {suc n} k) 
       step  ( syn  (tab {suc n} k))
    ≈⟨  step ∣-cong (syn-tab k) 
       step  (atom (dyn k))
    
    where open Reasoning  J (suc (suc n)) ∥/≈

syn⊙norm≗id :  {n}  syn {n}  norm {n}  id
syn⊙norm≗id {n} {atom (dyn k)}        = syn-tab k
syn⊙norm≗id {n} {term  (x  y  [])} = begin
     syn  ( norm  x   norm  y)
  ≈⟨ sym ( syn ∣-hom  ( norm  x   norm  y  [])) 
     syn  ( norm  x) ·  syn  ( norm  y)
  ≈⟨ ·-cong syn⊙norm≗id syn⊙norm≗id 
    x · y
  
  where open Reasoning  J n ∥/≈

private

  tab-diag :  n  tab {suc n} (fromℕ n)  leaf one
  tab-diag zero    = PE.refl
  tab-diag (suc n) = PE.cong lift (tab-diag n)

  norm-exp :  {n} (k : ℕ⁺)   norm  (exp n k)  leaf k
  norm-exp {n} one     = tab-diag n
  norm-exp {n} (suc k) = begin
       norm  (atomise n · exp n k)
    ≈⟨ PE.sym ( norm ∣-hom  (atomise n  exp n k  [])) 
       norm  (atomise n)   norm  (exp n k)
    ≈⟨ ⊗-cong (tab-diag n) (norm-exp k) 
      leaf one  leaf k
    
    where open Reasoning  J' (suc n) ∥/≈

  tab-skip :  {n}  (k : Fin n)  tab {suc n} (up k)  skip (tab {n} k)
  tab-skip {suc zero}    zero    = PE.refl
  tab-skip {suc (suc n)} zero    = PE.refl
  tab-skip {suc (suc n)} (suc k) = PE.cong lift (tab-skip k)

  norm-raise :  {n x}   norm {suc n}  ( raise  x)  skip ( norm {n}  x)
  norm-raise {n} {atom (dyn k)}        = tab-skip k
  norm-raise {n} {term  (x  y  [])} = begin
       norm  ( raise  (x · y))
    ≡⟨  norm ∣-cong (sym ( raise ∣-hom  (x  y  []))) 
       norm  ( raise  x ·  raise  y)
    ≡⟨ PE.sym ( norm ∣-hom  ( raise  x   raise  y  [])) 
       norm  ( raise  x)   norm  ( raise  y)
    ≡⟨ ⊗-cong (norm-raise {x = x}) (norm-raise {x = y}) 
      skip ( norm  x   norm  y)
    ≡⟨ PE.cong skip ( norm ∣-hom  (x  y  [])) 
      skip ( norm  (x · y))
    
    where open PE.≡-Reasoning

norm⊙syn≗id :  {n}  norm {n}  syn {n}  id
norm⊙syn≗id {suc n} {leaf x} = norm-exp x
norm⊙syn≗id {suc n} {skip x} = begin
     norm  ( raise  ( syn  x))
  ≈⟨ norm-raise {x =  syn  x} 
    skip ( norm  ( syn  x))
  ≈⟨ PE.cong skip norm⊙syn≗id 
    skip x
  
  where open Reasoning  J' (suc n) ∥/≈
norm⊙syn≗id {suc n} {cons x xs} = begin
     norm  (exp n x ·  raise  ( syn  xs))
  ≈⟨ PE.sym ( norm ∣-hom  (exp n x   raise  ( syn  xs)  [])) 
     norm  (exp n x)   norm  ( raise  ( syn  xs))
  ≈⟨ ⊗-cong (norm-exp x) (norm-raise {x =  syn  xs}) 
    leaf x  skip ( norm  ( syn  xs))
  ≈⟨ ⊗-cong (PE.refl {x = leaf x}) (PE.cong skip (norm⊙syn≗id {x = xs})) 
    leaf x  skip xs
  
  where open Reasoning  J' (suc n) ∥/≈

iso :  {n}   J n ∥ₐ   J' n ∥ₐ
iso = record { _⃗   = norm
             ; _⃖   = syn
             ; invˡ = norm⊙syn≗id
             ; invʳ = syn⊙norm≗id
             }