module fin where

open import Data.Product
open import Data.Unit
open import Data.Bool
open import Data.Nat
open import Relation.Binary.PropositionalEquality

open import Function

open import SPF  as SPF

finCode : SPF
finCode =    non-ind   n  done (suc n))
        +SPF non-ind   n  ind   _  n) (done (suc n)))

Fin :   Set
Fin = D finCode

fz : (n : )  Fin (suc n)
fz n = inn (true , n , refl)

fs : (n : )  Fin n  Fin (suc n)
fs n k = inn (false , n , ((λ _  k) , refl))

private
  finCode' : SPF
  finCode' = non-ind   n  done (suc n) +SPF ind   _  n) (done (suc n)))

  Fin' :   Set
  Fin' = D finCode'

  fz' : (n : )  Fin' (suc n)
  fz' n = inn (n , true , refl)

  fs' : (n : )  Fin' n  Fin' (suc n)
  fs' n k = inn (n , false , ((λ _  k) , refl))

sum : (n : )  (Fin n -> )  
sum zero f = 0
sum (suc n) f = f (fz _) + sum n (f  (fs _))

prod : (n : )  (Fin n -> )  
prod zero f = 1
prod (suc n) f = f (fz _) * prod n (f  (fs _))


mutual

  data FinType : Set where
    fin :   FinType
    _×f_ : FinType  FinType  FinType
    _+f_ : FinType  FinType  FinType
    σ : (a : FinType)  (Fin (size a)  FinType)  FinType
    π : (a : FinType)  (Fin (size a)  FinType)  FinType

  size : FinType  
  size (fin n) = n
  size (a ×f b) = size a * size b
  size (a +f b) = size a + size b
  size (σ a b) = sum (size a) (size  b)
  size (π a b) = prod (size a) (size  b)

  -- reduction to inductive family
data FinType' :   Set where
  fin : (n : )  FinType' n
  _×f_ : (n : )  FinType' n  (m : )  FinType' m  FinType' (n * m)
  _+f_ : (n : )  FinType' n  (m : )  FinType' m  FinType' (n + m)
  σ : (n : )  (a : FinType' n)  (m : Fin n  )  ((x : Fin n)  FinType' (m x))  FinType' (sum n m)
  π : (n : )  (a : FinType' n)  (m : Fin n  )  ((x : Fin n)  FinType' (m x))  FinType' (prod n m)

⟦FinType⟧ : Set
⟦FinType⟧ = Σ  FinType'

⟦size⟧ : ⟦FinType⟧  
⟦size⟧ = proj₁

⟦fin⟧ :   ⟦FinType⟧
⟦fin⟧ n = (n , fin n)

_⟦×f⟧_ : ⟦FinType⟧  ⟦FinType⟧  ⟦FinType⟧
(n , a) ⟦×f⟧ (m , b) = n * m , _×f_ n a m b

_⟦+f⟧_ : ⟦FinType⟧  ⟦FinType⟧  ⟦FinType⟧
(n , a) ⟦+f⟧ (m , b) = n + m , _+f_ n a m b

⟦σ⟧ : (a : ⟦FinType⟧)  (Fin (⟦size⟧ a)  ⟦FinType⟧)  ⟦FinType⟧
⟦σ⟧ (n , a) b = (sum n (proj₁  b)) , (σ n a (proj₁  b) (proj₂  b))

⟦π⟧ : (a : ⟦FinType⟧)  (Fin (⟦size⟧ a)  ⟦FinType⟧)  ⟦FinType⟧
⟦π⟧ (n , a) b = (prod n (proj₁  b)) , (π n a (proj₁  b) (proj₂  b))

-- Equations hold on the nose
_ :  {n}  ⟦size⟧ (⟦fin⟧ n)  n
_ = refl

_ :  {a b}  ⟦size⟧ (a ⟦×f⟧ b)  ⟦size⟧ a * ⟦size⟧ b
_ = refl

_ :  {a b}  ⟦size⟧ (a ⟦+f⟧ b)  ⟦size⟧ a + ⟦size⟧ b
_ = refl

_ :  {a b}  ⟦size⟧ (⟦σ⟧ a b)  sum (⟦size⟧ a) (⟦size⟧  b)
_ = refl

_ :  {a b}  ⟦size⟧ (⟦π⟧ a b)  prod (⟦size⟧ a) (⟦size⟧  b)
_ = refl