{-# OPTIONS --safe --sized-types #-}

module StateOfTheArt.CDMM where

open import Size
open import Data.Empty
open import Data.Bool
open import Data.Nat using (ℕ ; suc)
open import Data.Unit
open import Data.Product as Prod
open import Function
open import Relation.Unary
open import Relation.Binary.PropositionalEquality hiding ([_])


data Desc (I J : Set) : Set₁ where
  `σ : (A : Set) → (A → Desc I J) → Desc I J
  `X : J → Desc I J → Desc I J
  `∎ : I → Desc I J

private
  variable
    I J : Set
    i : I
    j : J
    d : Desc I J
    X Y : J → Set
    s : Size
    A B : Set

`K : Set → I → Desc I J
`K A i = `σ A (λ _ → `∎ i)

infixr 5 _`+_

_`+_ : Desc I J → Desc I J → Desc I J
d `+ e = `σ Bool $ λ { true  → d ; false → e }

⟦_⟧ : Desc I J → (J → Set) → (I → Set)
⟦ `σ A d  ⟧ X i = Σ[ a ∈ A ] (⟦ d a ⟧ X i)
⟦ `X j d  ⟧ X i = X j × ⟦ d ⟧ X i
⟦ `∎ i′   ⟧ X i = i ≡ i′

fmap : (d : Desc I J) → ∀[ X ⇒ Y ] → ∀[ ⟦ d ⟧ X ⇒ ⟦ d ⟧ Y ]
fmap (`σ A d)  f (a , v)  = (a , fmap (d a) f v)
fmap (`X j d)  f (r , v)  = (f r , fmap d f v)
fmap (`∎ i)    f t        = t

data μ (d : Desc I I) : Size → I → Set where
  `con : ⟦ d ⟧ (μ d s) i → μ d (↑ s) i

fold : (d : Desc I I) → ∀[ ⟦ d ⟧ X ⇒ X ] → ∀[ μ d s ⇒ X ]
fold d alg (`con t) = alg (fmap d (fold d alg) t)

finD : Desc ℕ ℕ
finD =  `σ ℕ $ λ index →
        `σ Bool $ λ isZero → if isZero
        then `∎ (suc index)
        else `X index (`∎ (suc index))

fin : ℕ → Set
fin = μ finD ∞

fin0-elim : fin 0 → ⊥
fin0-elim (`con (_ , true , ()))
fin0-elim (`con (_ , false , _ , ()))

fz : ∀ n → fin (suc n)
fz n = `con (n , true , refl)

fs : ∀ n → fin n → fin (suc n)
fs n k = `con (n , false , k , refl)

listD : Set → Desc ⊤ ⊤
listD A =  `σ Bool $ λ isNil →
           if isNil then `∎ tt
           else `σ A (λ _ → `X tt (`∎ tt))

List : Set → Set
List A = μ (listD A) ∞ tt

infixr 10 _∷_

pattern []'  = (true , refl)
pattern []   = `con []'

pattern _∷'_ x xs  = (false , x , xs , refl)
pattern _∷_ x xs   = `con (x ∷' xs)

example : List (List Bool)
example = (false ∷ []) ∷ (true ∷ []) ∷ []

vecD : Set → Desc ℕ ℕ
vecD A =  `σ Bool $ λ isNil →
          if isNil then `∎ 0
          else `σ ℕ (λ n → `σ A (λ _ → `X n (`∎ (suc n))))

Vec : Set → ℕ → Set
Vec A = μ (vecD A) ∞

foldr : (A → B → B) → B → List A → B
foldr c n = fold (listD _) $ λ where
  []'          → n
  (hd ∷' rec)  → c hd rec

_++_ : List A  → List A → List A
_++_ = foldr (λ hd rec → hd ∷_ ∘ rec) id

flatten : List (List A) → List A
flatten = foldr _++_ []

test : flatten example ≡ false ∷ true ∷ []
test = refl