{-# OPTIONS --safe #-}
module Cubical.Data.Vec.NAry where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence

open import Cubical.Data.Nat
open import Cubical.Data.Vec.Base

private
  variable
     ℓ' : Level
    A : Type 
    B : Type ℓ'

nAryLevel : Level  Level    Level
nAryLevel ℓ₁ ℓ₂ zero    = ℓ₂
nAryLevel ℓ₁ ℓ₂ (suc n) = ℓ-max ℓ₁ (nAryLevel ℓ₁ ℓ₂ n)

nAryOp : (n : )  Type   Type ℓ'  Type (nAryLevel  ℓ' n)
nAryOp zero A B    = B
nAryOp (suc n) A B = A  nAryOp n A B


_$ⁿ_ :  {n}  nAryOp n A B  (Vec A n  B)
f $ⁿ []       = f
f $ⁿ (x  xs) = f x $ⁿ xs

curryⁿ :  {n}  (Vec A n  B)  nAryOp n A B
curryⁿ {n = zero}  f   = f []
curryⁿ {n = suc n} f x = curryⁿ  xs  f (x  xs))

$ⁿ-curryⁿ :  {n} (f : Vec A n  B)  _$ⁿ_ (curryⁿ f)  f
$ⁿ-curryⁿ {n = zero} f  = funExt λ { []  refl }
$ⁿ-curryⁿ {n = suc n} f = funExt λ { (x  xs) i  $ⁿ-curryⁿ {n = n}  ys  f (x  ys)) i xs}

curryⁿ-$ⁿ :  {n} (f : nAryOp { = } {ℓ' = ℓ'} n A B)  curryⁿ {A = A} {B = B} (_$ⁿ_ f)  f
curryⁿ-$ⁿ {n = zero} f  = refl
curryⁿ-$ⁿ {n = suc n} f = funExt λ x  curryⁿ-$ⁿ {n = n} (f x)

nAryOp≃VecFun :  {n}  nAryOp n A B  (Vec A n  B)
nAryOp≃VecFun {n = n} = isoToEquiv f
  where
  f : Iso (nAryOp n A B) (Vec A n  B)
  Iso.fun f      = _$ⁿ_
  Iso.inv f      = curryⁿ
  Iso.rightInv f = $ⁿ-curryⁿ
  Iso.leftInv f  = curryⁿ-$ⁿ {n = n}

-- In order to apply ua to nAryOp≃VecFun we probably need to change
-- the base-case of nAryLevel to "ℓ-max ℓ₁ ℓ₂". This will make it
-- necessary to add lots of Lifts in zero cases so it's not done yet,
-- but if the Path is ever needed then it might be worth to do.