\begin{code}
{-# OPTIONS --safe --cubical #-}

module Data.Product.NAry where

open import Data.Sigma
open import Prelude hiding (; tt)
open import Data.Unit.UniversePolymorphic
open import Path.Reasoning

private
  variable
    n : 
     : Level

\end{code}
%<*levels>
\begin{code}
Levels :   Type₀
Levels zero = 
Levels (suc n) = Level × Levels n
\end{code}
%</levels>
\begin{code}
private
  variable
    ls : Levels n
\end{code}
%<*max-level>
\begin{code}
max-level : Levels n  Level
max-level {zero} _ = ℓzero
max-level {suc n} (x , xs) =
  x ℓ⊔ max-level xs
\end{code}
%</max-level>
%<*types>
\begin{code}
Types :  n  (ls : Levels n)  Type (ℓsuc (max-level ls))
Types zero ls = 
Types (suc n) (l , ls) = Type l × Types n ls
\end{code}
%</types>
%<*tuple-plus>
\begin{code}
⦅_⦆⁺ :  Types (suc n) ls 
        Type (max-level ls)
⦅_⦆⁺ {n = zero } (X , Xs) = X
⦅_⦆⁺ {n = suc n} (X , Xs) = X ×  Xs ⦆⁺
\end{code}
%</tuple-plus>
%<*tuple>
\begin{code}
⦅_⦆ :  Types n ls 
       Type (max-level ls)
⦅_⦆ {n = zero} _ = 
⦅_⦆ {n = suc n} = ⦅_⦆⁺ {n = n}
\end{code}
%</tuple>
\begin{code}
map-types : (fn :  {}  Type   Type )   {n ls}  Types n ls  Types n ls
map-types fn {zero} xs = xs
map-types fn {suc n} (x , xs) = fn x , map-types fn xs
\end{code}
%<*arg-form>
\begin{code}
data ArgForm : Type₀ where expl impl inst : ArgForm
\end{code}
%</arg-form>
\begin{code}

infixr 0 _[_]→_
\end{code}
%<*generic-function>
\begin{code}
_[_]→_ : Type a  ArgForm  Type b  Type (a ℓ⊔ b)
A [ expl  ]→ B =         A     B
A [ impl  ]→ B = {  _ :  A }   B
A [ inst  ]→ B =   _ :  A    B
\end{code}
%</generic-function>
%<*generic-function-iso>
\begin{code}
[_$] :  form  (A [ form ]→ B)  (A  B)
\end{code}
%</generic-function-iso>
\begin{code}
[ expl $] .fun f = f
[ impl $] .fun f x = f {x}
[ inst $] .fun f x = f  x 
[ expl $] .inv f = f
[ impl $] .inv f {x} = f x
[ inst $] .inv f  x  = f x
[ expl $] .leftInv f = refl
[ impl $] .leftInv f = refl
[ inst $] .leftInv f = refl
[ expl $] .rightInv f = refl
[ impl $] .rightInv f = refl
[ inst $] .rightInv f = refl

infixr 0 pi-arr
pi-arr : (A : Type a)  ArgForm  (A  Type b)  Type (a ℓ⊔ b)
pi-arr A expl B = (x : A)  B x
pi-arr A impl B = {x : A}  B x
pi-arr A inst B =  x : A   B x

syntax pi-arr a f  x  b ) = x  a Π[ f ]→ b
\end{code}
%<*pi-iso>
\begin{code}
Π[_$] :  {B : A  Type b} fr  (x  A Π[ fr ]→ B x)  ((x : A)  B x)
\end{code}
%</pi-iso>
\begin{code}
Π[ expl $] .fun f = f
Π[ impl $] .fun f x = f {x}
Π[ inst $] .fun f x = f  x 
Π[ expl $] .inv f x = f x
Π[ impl $] .inv f {x} = f x
Π[ inst $] .inv f  x  = f x
Π[ expl $] .leftInv f = refl
Π[ impl $] .leftInv f = refl
Π[ inst $] .leftInv f = refl
Π[ expl $] .rightInv f = refl
Π[ impl $] .rightInv f = refl
Π[ inst $] .rightInv f = refl

infixr 0 ⦅_⦆[_]→_
\end{code}
%<*multi-generic>
\begin{code}
⦅_⦆[_]→_ : Types n ls  ArgForm  Type   Type (max-level ls ℓ⊔ )
⦅_⦆[_]→_ {n = zero}  Xs fr Y = Y
⦅_⦆[_]→_ {n = suc n} (X , Xs) fr Y = X [ fr ]→  Xs ⦆[ fr ]→ Y
\end{code}
%</multi-generic>
\begin{code}

infixr 0 pi-arrs-plus
\end{code}
%<*pi-arrs-plus>
\begin{code}
pi-arrs-plus :
  (Xs : Types (suc n) ls) 
  ArgForm 
  (y :  Xs ⦆⁺  Type ) 
  Type (max-level ls ℓ⊔ )
pi-arrs-plus {n = zero   } (X , Xs) fr Y = x  X Π[ fr ]→ Y x
pi-arrs-plus {n = suc n  } (X , Xs) fr Y =
  x  X Π[ fr ]→ xs ⦂⦅ Xs ⦆⁺Π[ fr ]→ Y (x , xs)
\end{code}
%</pi-arrs-plus>
\begin{code}

syntax pi-arrs-plus Xs fr  xs  Y) = xs ⦂⦅ Xs ⦆⁺Π[ fr ]→ Y

pi-arrs :  {n ls }  (Xs : Types n ls)  ArgForm  (y :  Xs   Type )  Type (max-level ls ℓ⊔ )
pi-arrs {n = zero} xs fr Y = Y tt
pi-arrs {n = suc n} = pi-arrs-plus
syntax pi-arrs Xs fr  xs  Y) = xs ⦂⦅ Xs ⦆Π[ fr ]→ Y

[_^_$]⁺↓ :  n {ls } f {Xs : Types (suc n) ls} {y : Type }  ( Xs ⦆⁺  y)   Xs ⦆[ f ]→ y
[ zero  ^ fr $]⁺↓ f = [ fr $] .inv f
[ suc n ^ fr $]⁺↓ f = [ fr $] .inv ([ n ^ fr $]⁺↓  (f ∘_)  _,_)

[_^_$]↓ :  n {ls } f {xs : Types n ls} {y : Type }  ( xs   y)   xs ⦆[ f ]→ y
[ zero  ^ fr $]↓ f = f tt
[ suc n ^ fr $]↓ f = [ n ^ fr $]⁺↓ f

[_^_$]⁺↑ :  n {ls } f {xs : Types (suc n) ls} {y : Type }  ( xs ⦆[ f ]→ y)  ( xs ⦆⁺  y)
[ zero  ^ fr $]⁺↑ f = [ fr $] .fun f
[ suc n ^ fr $]⁺↑ f = uncurry ([ n ^ fr $]⁺↑  [ fr $] .fun f)

[_^_$]↑ :  n {ls } f {xs : Types n ls} {y : Type }  ( xs ⦆[ f ]→ y)  ( xs   y)
[ zero ^ fr  $]↑ f _ = f
[ suc n ^ fr $]↑ f = [ n ^ fr $]⁺↑ f

leftInvCurry⁺ :  n {ls } fr {Xs : Types (suc n) ls} {Y : Type }
               (f :  Xs ⦆[ fr ]→ Y )  [ n ^ fr $]⁺↓ ([ n ^ fr $]⁺↑ f)  f
leftInvCurry⁺ zero fr f = [ fr $] .leftInv f
leftInvCurry⁺ (suc n) fr f =
  [ fr $] .inv ([ n ^ fr $]⁺↓  [ n ^ fr $]⁺↑  [ fr $] .fun f) ≡⟨ cong  r  [ fr $] .inv (r  [ fr $] .fun f)) (funExt (leftInvCurry⁺ n fr)) 
  [ fr $] .inv ([ fr $] .fun f) ≡⟨ [ fr $] .leftInv f 
  f 

leftInvCurry :  n {ls } fr {Xs : Types n ls} {Y : Type }
              (f :  Xs ⦆[ fr ]→ Y )  [ n ^ fr $]↓ ([ n ^ fr $]↑ f)  f
leftInvCurry zero fr f = refl
leftInvCurry (suc n) fr f = leftInvCurry⁺ n fr f

rightInvCurry⁺ :  n {ls } fr {Xs : Types (suc n) ls} {Y : Type } (f :  Xs   Y)
               [ n ^ fr $]⁺↑ ([ n ^ fr $]⁺↓ f)  f
rightInvCurry⁺ zero fr f = [ fr $] .rightInv f
rightInvCurry⁺ (suc n) fr f =
  uncurry ([ n ^ fr $]⁺↑  [ fr $] .fun ([ fr $] .inv ([ n ^ fr $]⁺↓  ((f ∘_)  _,_)))) ≡⟨ cong  h  uncurry ([ n ^ fr $]⁺↑  h)) ([ fr $] .rightInv _) 
  uncurry ([ n ^ fr $]⁺↑  [ n ^ fr $]⁺↓  ((f ∘_)  _,_)) ≡⟨ cong  r  uncurry (r  ((f ∘_)  _,_))) (funExt (rightInvCurry⁺ n fr)) 
  f 

rightInvCurry :  n {ls } fr {Xs : Types n ls} {Y : Type } (f :  Xs   Y)
               [ n ^ fr $]↑ ([ n ^ fr $]↓ f)  f
rightInvCurry zero fr f = refl
rightInvCurry (suc n) fr f = rightInvCurry⁺ n fr f

[_^_$] :  n {ls } fr {Xs : Types n ls} {Y : Type }
        ( Xs ⦆[ fr ]→ Y)  ( Xs   Y)
[ n ^ fr $] .fun = [ n ^ fr $]↑
[ n ^ fr $] .inv = [ n ^ fr $]↓
[ n ^ fr $] .leftInv  = leftInvCurry n fr
[ n ^ fr $] .rightInv = rightInvCurry n fr

↓Π[_^_$]⁺ :  n {ls } fr {Xs : Types (suc n) ls} {Y :  Xs   Type }  ((xs :  Xs )  Y xs)  xs ⦂⦅ Xs ⦆⁺Π[ fr ]→ Y xs
↓Π[ zero ^ fr  $]⁺ f = Π[ fr $] .inv f
↓Π[ suc n ^ fr $]⁺ f = Π[ fr $] .inv (↓Π[ n ^ fr $]⁺  (f ∘_)  _,_)

↓Π[_^_$] :  n {ls } fr {Xs : Types n ls} {Y :  Xs   Type }  ((xs :  Xs )  Y xs)  xs ⦂⦅ Xs ⦆Π[ fr ]→ Y xs
↓Π[ zero ^ fr $] f = f tt
↓Π[ suc n ^ fr $] f = ↓Π[ n ^ fr $]⁺ f

↑Π[_^_$]⁺ :  n {ls } fr {Xs : Types (suc n) ls} {Y :  Xs   Type }  (xs ⦂⦅ Xs ⦆⁺Π[ fr ]→ Y xs)  ((xs :  Xs )  Y xs)
↑Π[ zero ^ fr  $]⁺ f = Π[ fr $] .fun f
↑Π[ suc n ^ fr $]⁺ f = uncurry (↑Π[ n ^ fr $]⁺  Π[ fr $] .fun f)

↑Π[_^_$] :  n {ls } fr {Xs : Types n ls} {Y :  Xs   Type }  (xs ⦂⦅ Xs ⦆Π[ fr ]→ Y xs)  ((xs :  Xs )  Y xs)
↑Π[ zero ^ fr  $] f _ = f
↑Π[ suc n ^ fr $] f = ↑Π[ n ^ fr $]⁺ f

leftInvCurryΠ⁺ :  n {ls } fr {Xs : Types (suc n) ls} {Y :  Xs   Type }
               (f : xs ⦂⦅ Xs ⦆⁺Π[ fr ]→ Y xs)  ↓Π[ n ^ fr $]⁺ (↑Π[ n ^ fr $]⁺ f)  f
leftInvCurryΠ⁺ zero fr f = Π[ fr $] .leftInv f
leftInvCurryΠ⁺ (suc n) fr f =
  Π[ fr $] .inv (↓Π[ n ^ fr $]⁺  ↑Π[ n ^ fr $]⁺  Π[ fr $] .fun f)  ≡⟨ cong (Π[ fr $] .inv  flip _∘_ (Π[ fr $] .fun f))  i x  leftInvCurryΠ⁺ n fr x i) 
  Π[ fr $] .inv (Π[ fr $] .fun f) ≡⟨ Π[ fr $] .leftInv f 
  f 

leftInvCurryΠ :  n {ls } fr {Xs : Types n ls} {Y :  Xs   Type }
               (f : xs ⦂⦅ Xs ⦆Π[ fr ]→ Y xs)  ↓Π[ n ^ fr $] (↑Π[ n ^ fr $] f)  f
leftInvCurryΠ zero fr f = refl
leftInvCurryΠ (suc n) fr f = leftInvCurryΠ⁺ n fr f

rightInvCurryΠ⁺ :  n {ls } fr {Xs : Types (suc n) ls} {Y :  Xs   Type } (f : (xs :  Xs )  Y xs)
                ↑Π[ n ^ fr $]⁺ (↓Π[ n ^ fr $]⁺ f)  f
rightInvCurryΠ⁺ zero fr f = Π[ fr $] .rightInv f
rightInvCurryΠ⁺ (suc n) fr f =
  uncurry (↑Π[ n ^ fr $]⁺  (Π[ fr $] .fun (Π[ fr $] .inv (↓Π[ n ^ fr $]⁺  (f ∘_)  _,_)))) ≡⟨ cong  h  uncurry (↑Π[ n ^ fr $]⁺  h)) (Π[ fr $] .rightInv _) 
  uncurry (↑Π[ n ^ fr $]⁺  ↓Π[ n ^ fr $]⁺  (f ∘_)  _,_) ≡⟨ cong (uncurry  flip _∘_ ((f ∘_)  _,_))  i x  rightInvCurryΠ⁺ n fr x i) 
  f 

rightInvCurryΠ :  n {ls } fr {Xs : Types n ls} {Y :  Xs   Type } (f : (xs :  Xs )  Y xs)
                ↑Π[ n ^ fr $] (↓Π[ n ^ fr $] f)  f
rightInvCurryΠ zero fr f = refl
rightInvCurryΠ (suc n) fr f = rightInvCurryΠ⁺ n fr f
\end{code}
%<*full-iso>
\begin{code}
Π[_^_$] :   n {ls } fr {Xs : Types n ls} {Y :  Xs   Type } 
           (xs ⦂⦅ Xs ⦆Π[ fr ]→ Y xs)  ((xs :  Xs )  Y xs)
\end{code}
%</full-iso>
\begin{code}
Π[ n ^ fr $] .fun = ↑Π[ n ^ fr $]
Π[ n ^ fr $] .inv = ↓Π[ n ^ fr $]
Π[ n ^ fr $] .leftInv  = leftInvCurryΠ n fr
Π[ n ^ fr $] .rightInv = rightInvCurryΠ n fr
\end{code}