{-# OPTIONS --cubical --safe #-}

module Control.Monad.Levels.Zipping where

open import Prelude
open import Control.Monad.Levels.Definition
open import Control.Monad.Levels.Eliminators
open import Data.Bag
open import Path.Reasoning

open import Cubical.Foundations.HLevels using (isOfHLevelΠ)

zip₂-alg : Levels-ϕ[ A ]( A   (Levels A  Levels A)  Levels A)
[ zip₂-alg ]-set = isOfHLevelΠ 2 λ _  isOfHLevelΠ 2 λ _  trunc
[ zip₂-alg ] y  ys  _  x xs = (x  y)  xs ys
[ zip₂-alg ][] x xs = x  xs []
[ zip₂-alg ]-trail i x xs = ∪-idʳ x i  xs []

zip₂-alg-id : Levels-ψ[ xs  A ]≡ ((zip₂-alg  xs) [] id  xs)
 zip₂-alg-id ⟧≡ x  xs  Pxs  = refl
 zip₂-alg-id ⟧≡[] = trail

zip-alg : Levels-ϕ[ A ] (Levels A  Levels A)
[ zip-alg ]-set = isOfHLevelΠ 2 λ _  trunc
[ zip-alg ] x  _  k  ys = (zip₂-alg  ys) x k
[ zip-alg ][] ys = ys
[ zip-alg ]-trail = funExt (zip₂-alg-id ⇓≡_)

zip : Levels A  Levels A  Levels A
zip xs = zip-alg  xs

zip-idʳ : Levels-ψ[ xs  A ]≡ zip xs []  xs
 zip-idʳ ⟧≡ x  xs  Pxs  = cong (x ∷_) Pxs
 zip-idʳ ⟧≡[] = refl

mutual
  zip₂-comm : (x :  A ) (xs : Levels A) (pxs :  ys  zip xs ys  zip ys xs)  Levels-ψ[ ys  A ]≡  (zip (x  xs) ys  zip ys (x  xs))
   zip₂-comm x xs pxs ⟧≡[] = zip-idʳ ⇓≡ (x  xs)
   zip₂-comm x xs pxs ⟧≡ y  ys  Pys  = cong₂ _∷_ (∪-comm x y) (pxs ys)

  zip-comm : Levels-ψ[ xs  A ] (∀ ys  zip xs ys  zip ys xs)
   zip-comm ∥-prop = isOfHLevelΠ 1 λ _  trunc _ _
   zip-comm ∥[] ys = sym (zip-idʳ ⇓≡ ys)
   zip-comm  x  xs  Pxs  ys = zip₂-comm x xs Pxs ⇓≡ ys


zip₃-assoc : (x :  A ) (xs : Levels A) (pxs :  ys zs  zip (zip xs ys) zs  zip xs (zip ys zs)) 
             (y :  A ) (ys : Levels A) 
             Levels-ψ[ zs  A ]≡ (zip (zip (x  xs) (y  ys)) zs  zip (x  xs) (zip (y  ys) zs))
 zip₃-assoc x xs pxs y ys ⟧≡[] = (zip-idʳ ⇓≡ (zip (x  xs) (y  ys))) ; cong (zip (x  xs)) (sym (zip-idʳ ⇓≡ (y  ys)))
 zip₃-assoc x xs pxs y ys ⟧≡ z  zs  _  = cong₂ _∷_ (∪-assoc x y z) (pxs ys zs)

zip₂-assoc : (x :  A ) (xs : Levels A) (pxs :  ys zs  zip (zip xs ys) zs  zip xs (zip ys zs)) 
             Levels-ψ[ ys  A ] (∀ zs  zip (zip (x  xs) ys) zs  zip (x  xs) (zip ys zs))
 zip₂-assoc x xs pxs ∥-prop = isOfHLevelΠ 1 λ _  trunc _ _
 zip₂-assoc x xs pxs ∥[] zs = cong  xs  zip xs zs) (zip-idʳ ⇓≡ (x  xs))
 zip₂-assoc x xs pxs  y  ys  _  zs = zip₃-assoc x xs pxs y ys ⇓≡ zs

zip-assoc : Levels-ψ[ xs  A ] (∀ ys zs  zip (zip xs ys) zs  zip xs (zip ys zs))
 zip-assoc ∥-prop = isOfHLevelΠ 1 λ _  isOfHLevelΠ 1 λ _  trunc _ _
 zip-assoc  x  xs  Pxs  ys zs =  zip₂-assoc x xs Pxs ∥⇓ ys zs
 zip-assoc ∥[] _ _ = refl

open import Algebra

module _ {a} {A : Type a} where
  levels-cmon : CommutativeMonoid (Levels A)
  Monoid._∙_ (CommutativeMonoid.monoid levels-cmon) = zip
  Monoid.ε (CommutativeMonoid.monoid levels-cmon) = []
  Monoid.assoc (CommutativeMonoid.monoid levels-cmon) =  zip-assoc ∥⇓
  Monoid.ε∙ (CommutativeMonoid.monoid levels-cmon) _ = refl
  Monoid.∙ε (CommutativeMonoid.monoid levels-cmon) xs = zip-idʳ ⇓≡ xs
  CommutativeMonoid.comm levels-cmon =  zip-comm ∥⇓