{-# OPTIONS --safe --with-K #-}

module Examples.WithoutMacros.List where

open import Prelude

--open import Generics.Description
--open import Generics.Recursion
--open import Generics.Reflection
--
--open import Generics.RecursionScheme
--open import Generics.Ornament
--open import Generics.Ornament.Description
--open import Generics.Ornament.Algebraic
--open import Generics.Ornament.Algebraic.Isomorphism
--open import Generics.SimpleContainer
--open import Generics.SimpleContainer.All
--open import Generics.SimpleContainer.Any

open import Examples.WithoutMacros.Nat

--------
-- Connecting with the existing List datatype

--instance
--
--  ListC : Named (quote List) _
--  unNamed ListC = genDataC ListD (genDataT ListD List)
--    where ListD = genDataD List
--
--  ListO : DataO (findDataD (quote List)) (findDataD (quote ℕ))
--  ListO = record
--    { level  = λ _ → tt
--    ; applyL = λ (ℓ , _) → record
--        { param  = λ _ → tt
--        ; index  = λ _ _ → tt
--        ; applyP = λ _ → ι ∷ ∺ (Δ[ _ ] ρ ι ι) ∷ ∺ [] } }
--
--  ListFin : Finitary (findDataD (quote List))
--  ListFin = [] ∷ (tt ∷ refl ∷ []) ∷ []
--
--  ListS : SCᵈ (findDataD (quote List))
--  ListS = record
--    { El  = λ (A , _) → A
--    ; pos = [] ∷ (true ∷ tt ∷ []) ∷ []
--    ; coe = λ _ → lift tt ,ωω (refl ,ωω λ _ → lift tt) ,ωω lift tt }
--
----------
---- Connecting with the existing foldr function and deriving the fold fusion theorem
--
--private
--  foldListP : FoldP
--  foldListP = fold-operator (quote List)

-- Generate foldList and its wrapper and connection, and then replace it with foldr
--unquoteDecl foldList = defineFold foldListP foldList
foldList : { ℓ1 : Level} {p : Set ℓ1} {X : Set } (alg : X)
           (alg1 : (a : p) (z : X)  X) (l : List p) 
           X
foldList alg alg₁ [] = alg
foldList alg alg₁ (x  xs) = alg₁ x (foldList alg alg₁ xs)

--instance
--  foldrC = genFoldC' foldListP foldrT
--    where
--      foldrT : FoldT foldListP
--      foldrT _ ((A , tt) , B , e , f , tt) = foldr e f
--
--private
--  foldr-fusionP : IndP
--  foldr-fusionP = fold-fusion (quote List)
--
-- unquoteDecl foldr-fusion = defineInd foldr-fusionP foldr-fusion
foldr-fusion :
   {ℓ' ℓ'' } {A : Set } {B : Set ℓ'} {C : Set ℓ''} (h : B  C)
  {e : B} {f : A  B  B} {e' : C} {f' : A  C  C}
  (he : h e  e') (hf : (a : A) (b : B) (c : C)  h b  c  h (f a b)  f' a c)
  (as : List A)  h (foldr e f as)  foldr e' f' as
foldr-fusion h he hf []       = he
foldr-fusion h he hf (a  as) = hf a _ _ (foldr-fusion h he hf as)

--instance foldr-fusionC = genIndC foldr-fusionP foldr-fusion
--
----------
---- Algebraic ornamentation
--
--private
--  AlgListOD : DataOD (findDataD (quote List))
--  AlgListOD = AlgOD foldListP
--
--instance AlgListO = ⌈ AlgListOD ⌉ᵈ
--
-- unquoteDecl data AlgList constructor c0 c1 = defineByDataD ⌊ AlgListOD ⌋ᵈ AlgList (c0 ∷ c1 ∷ [])
data AlgList {ℓ' } {A : Set } {B : Set ℓ'}
  (e : B) (f : A  B  B) : B  Set (  ℓ') where
  []  : AlgList e f e
  _∷_ : (a : A) {b : B}  AlgList e f b  AlgList e f (f a b)

--instance AlgListC = genDataC ⌊ AlgListOD ⌋ᵈ (genDataT ⌊ AlgListOD ⌋ᵈ AlgList)
--
--private
--  lengthP : FoldP
--  lengthP = forget (quote List) (quote ℕ)

--instance lengthC = genFoldC lengthP length

--private
--  VecOD : DataOD (findDataD (quote List))
--  VecOD = AlgOD lengthP

--instance VecO = ⌈ VecOD ⌉ᵈ

-- unquoteDecl data Vec constructor c0 c1 = defineByDataD ⌊ VecOD ⌋ᵈ Vec (c0 ∷ c1 ∷ [])
data Vec (A : Set ) : (n : )  Set  where
  []  : Vec A 0
  _∷_ : (a : A) {n : }  Vec A n  Vec A (suc n)

--instance
--
--  VecC : Named (quote Vec) _
--  unNamed VecC = genDataC ⌊ VecOD ⌋ᵈ (genDataT ⌊ VecOD ⌋ᵈ Vec)

--  VecFin : Finitary ⌊ VecOD ⌋ᵈ
--  VecFin = [] ∷ (tt ∷ tt ∷ refl ∷ []) ∷ []
--
--private
--  fromVecP : FoldP
--  fromVecP = forget (quote Vec) (quote List)

-- unquoteDecl fromVec = defineFold fromVecP fromVec
fromVec : {A : Set } {n : } (v : Vec A n)  List A
fromVec []       = []
fromVec (a  as) = a  fromVec as

--instance fromVecC = genFoldC fromVecP fromVec
--
--private
--  toVecP : IndP
--  toVecP = remember (quote Vec)
--
-- unquoteDecl toVec = defineInd toVecP toVec
toVec :  {A : Set } (as : List A)  Vec A (length as)
toVec []       = []
toVec (a  as) = a  toVec as

--instance toVecC = genIndC toVecP toVec
--
--private
--  from-toVecP : IndP
--  from-toVecP = forget-remember-inv (quote Vec) (quote List) (inl it)
--
-- unquoteDecl from-toVec = defineInd from-toVecP from-toVec
-- from-toVec : {A : Set ℓ} (as : List A) → fromVec (toVec as) ≡ as
-- from-toVec []       = refl
-- from-toVec (a ∷ as) = cong (_∷_ a) (from-toVec as)

--instance from-toVecC = genIndC from-toVecP from-toVec
--
--private
--  to-fromVecP : IndP
--  to-fromVecP = remember-forget-inv (quote Vec) (quote List) (inl it)
--
-- unquoteDecl to-fromVec = defineInd to-fromVecP to-fromVec
--to-fromVec : {A : Set ℓ} {n : ℕ} (as : Vec A n) →
--             (length (fromVec as) , toVec (fromVec as)) ≡ ((n , as) ⦂ Σ ℕ (Vec A))
--to-fromVec [] = refl
--to-fromVec {ℓ} (a ∷ as) =
--  trans
--   (cong {ℓ}
--    (bimap {lzero} (λ x₁ → x₁) (DataC.toN (findDataC (quote Vec))))
--    (cong (bimap {lzero} (λ x₁ → x₁) inr)
--     (cong (bimap (λ x₁ → x₁) inl)
--      (cong (bimap (λ x₁ → x₁) (λ section → a , section))
--       (trans
--        (cong (λ p₁ → suc (fst p₁) , fst p₁ , snd p₁ , refl)
--         (to-fromVec as))
--        refl)))))
--   refl

--instance to-fromVecC = genIndC to-fromVecP to-fromVec
--
--private
--  LenOD : DataOD (findDataD (quote Vec))
--  LenOD = AlgOD fromVecP

--instance LenO = ⌈ LenOD ⌉ᵈ

-- unquoteDecl data Len constructor c0 c1 = defineByDataD ⌊ LenOD ⌋ᵈ Len (c0 ∷ c1 ∷ [])
data Len {A : Set } :   List A  Set  where
  zero : Len 0 []
  suc  : {a : A} {n : } {as : List A}  Len n as  Len (suc n) (a  as)

--instance
--  LenC : Named (quote Len) _
--  unNamed LenC = genDataC ⌊ LenOD ⌋ᵈ (genDataT ⌊ LenOD ⌋ᵈ Len)

--private
--  fromLenP : FoldP
--  fromLenP = forget (quote Len) (quote Vec)
--
-- unquoteDecl fromLen = defineFold fromLenP fromLen
fromLen : {A : Set } {n : } {as : List A}  Len n as  Vec A n
fromLen  zero       = []
fromLen (suc {a} l) = a  fromLen l

--instance fromLenC = genFoldC fromLenP fromLen
--
--private
--  toLenP : IndP
--  toLenP = remember (quote Len)
--
-- unquoteDecl toLen = defineInd toLenP toLen
toLen : {A : Set } {n : } (as : Vec A n)  Len n (fromVec as)
toLen []       = zero
toLen (a  as) = suc (toLen as)

--instance toLenC = genIndC toLenP toLen
--
--private
--  from-toLenP : IndP
--  from-toLenP = forget-remember-inv (quote Len) (quote Vec) (inl it)
--
-- unquoteDecl from-toLen = defineInd from-toLenP from-toLen
-- from-toLen : {A : Set ℓ} {n : ℕ} (as : Vec A n) → fromLen (toLen as) ≡ as
-- from-toLen []       = refl
-- from-toLen (a ∷ as) = cong (_∷_ a) (from-toLen as)

--instance from-toLenC = genIndC from-toLenP from-toLen
--
--private
--  to-fromLenP : IndP
--  to-fromLenP = remember-forget-inv (quote Len) (quote Vec) (inl it)
--
-- unquoteDecl to-fromLen = defineInd to-fromLenP to-fromLen
--to-fromLen : {A : Set ℓ} {n : ℕ} {as : List A} (l : Len n as)
--           → (fromVec (fromLen l) , toLen (fromLen l))
--           ≡ ((as , l) ⦂ Σ[ as' ∈ List A ] Len n as')
--to-fromLen                      zero   = refl
--to-fromLen {ℓ} {n = suc n} {a ∷ _} (suc l) =
--  trans
--   (cong
--    (bimap (λ x → x) (DataC.toN (findDataC (quote Len))))
--    (cong (bimap (λ x → x) inr)
--     (cong (bimap (λ x → x) inl)
--      (cong (bimap (λ x → x) (λ section → a , section))
--       (cong (bimap (λ x → x) (λ section → n , section))
--        (trans
--         (cong (λ p → a ∷ fst p , fst p , snd p , refl) (to-fromLen l))
--         refl))))))
--   refl

--instance to-fromLenC = genIndC to-fromLenP to-fromLen
--
----------
---- All predicate
--
--private
--  ListPOD : DataOD (findDataD (quote List))
--  ListPOD = PredOD (quote List)
--
--instance ListPO = ⌈ ListPOD ⌉ᵈ
--
-- unquoteDecl data ListP constructor c0 c1 = defineByDataD ⌊ ListPOD ⌋ᵈ ListP (c0 ∷ c1 ∷ [])
data ListP {ℓ' } {A : Set } (P : A  Set ℓ') : Set (  ℓ') where
  []      : ListP P
  ⟨_,_⟩∷_ : (a : A)  P a  ListP P  ListP P

--instance
--
--  ListPC : Named (quote ListP) _
--  unNamed ListPC = genDataC ⌊ ListPOD ⌋ᵈ (genDataT ⌊ ListPOD ⌋ᵈ ListP)
--
--  ListPFin : Finitary ⌊ ListPOD ⌋ᵈ
--  ListPFin = [] ∷ (tt ∷ tt ∷ refl ∷ []) ∷ []
--
--private
--  fromListPP : FoldP
--  fromListPP = forget (quote ListP) (quote List)
--
-- unquoteDecl fromListP = defineFold fromListPP fromListP
fromListP :  {ℓ' } {A : Set } {P : A  Set ℓ'}  ListP P  List A
fromListP []               = []
fromListP ( a , p ⟩∷ aps) = a  fromListP aps

--instance fromListPC = genFoldC fromListPP fromListP
--
--private
--  ListAllOD : DataOD ⌊ ListPOD ⌋ᵈ
--  ListAllOD = AllOD (quote List)
--
--instance ListAllO = ⌈ ListAllOD ⌉ᵈ
--
-- unquoteDecl data ListAll constructor c0 c1 = defineByDataD ⌊ ListAllOD ⌋ᵈ ListAll (c0 ∷ c1 ∷ [])
data ListAll {ℓ' } {A : Set } (P : A  Set ℓ') : List A  Set (  ℓ') where
  []  : ListAll P []
  _∷_ : {a : A}  P a  {as : List A}  ListAll P as  ListAll P (a  as)

--instance
--  ListAllC : Named (quote ListAll) _
--  unNamed ListAllC = genDataC ⌊ ListAllOD ⌋ᵈ ListAllT
--    where ListAllT = genDataT ⌊ ListAllOD ⌋ᵈ ListAll
--
--private
--  fromAllP : FoldP
--  fromAllP = forget (quote ListAll) (quote ListP)
--
-- unquoteDecl fromAll = defineFold fromAllP fromAll
fromAll :  {ℓ' } {A : Set } {P : A  Set ℓ'} {as : List A}  ListAll P as  ListP P
fromAll []       = []
fromAll (p  ps) =  _ , p ⟩∷ fromAll ps

--instance fromAllC = genFoldC fromAllP fromAll
--
--private
--  toAllP : IndP
--  toAllP = remember (quote ListAll)
--
-- unquoteDecl toAll = defineInd toAllP toAll
toAll :  {ℓ' } {A : Set } {P : A  Set ℓ'} (aps : ListP P)  ListAll P (fromListP aps)
toAll []               = []
toAll ( a , p ⟩∷ aps) = p  toAll aps

--instance toAllC = genIndC toAllP toAll
--
--private
--  from-toAllP : IndP
--  from-toAllP = forget-remember-inv (quote ListAll) (quote ListP) (inl it)
--
-- unquoteDecl from-toAll = defineInd from-toAllP from-toAll
-- from-toAll : {ℓ' ℓ : Level} {A : Set ℓ} {P : A → Set ℓ'}
--              (aps : ListP P) → fromAll (toAll aps) ≡ aps
-- from-toAll [] = refl
-- from-toAll (⟨ a , p ⟩∷ aps) = cong (⟨_,_⟩∷_ a p) (from-toAll aps)

--instance from-toAllC = genIndC from-toAllP from-toAll
--
--private
--  to-fromAllP : IndP
--  to-fromAllP = remember-forget-inv (quote ListAll) (quote ListP) (inl it)
--
---- unquoteDecl to-fromAll = defineInd to-fromAllP to-fromAll
--to-fromAll : {ℓ' ℓ : Level} {A : Set ℓ} {P : A → Set ℓ'}
--             {as : List A} (all : ListAll P as)
--           → (fromListP (fromAll all) , toAll (fromAll all))
--           ≡ ((as , all) ⦂ Σ (List A) (ListAll P))
--to-fromAll [] = refl
--to-fromAll (_∷_ {a = a} p all) =
--  trans
--   (cong
--    (bimap (λ x₂ → x₂) (DataC.toN (findDataC (quote ListAll))))
--    (cong (bimap (λ x₂ → x₂) inr)
--     (cong (bimap (λ x₂ → x₂) inl)
--      (cong (bimap (λ x₂ → x₂) (λ section → a , section))
--       (cong (bimap (λ x₂ → x₂) (λ section → p , section))
--        (trans
--         (cong (λ p₁ → a ∷ fst p₁ , fst p₁ , snd p₁ , refl) (to-fromAll all))
--         refl))))))
--   refl
--
--instance to-fromAllC = genIndC to-fromAllP to-fromAll
--
----------
---- Any predicate
--
--private
--  ListAnyOD : DataOD (findDataD (quote ℕ))
--  ListAnyOD = AnyOD (quote List)
--
--instance ListAnyO = ⌈ ListAnyOD ⌉ᵈ
--
-- unquoteDecl data ListAny constructor c0 c1 = defineByDataD ⌊ ListAnyOD ⌋ᵈ ListAny (c0 ∷ c1 ∷ [])
data ListAny {ℓ' } {A : Set } (P : A  Set ℓ') : List A  Set (  ℓ') where
  here  :  {a as}  P a  ListAny P (a  as)
  there :  {a as}  ListAny P as  ListAny P (a  as)

--instance
--  ListAnyC : Named (quote ListAny) _
--  unNamed ListAnyC = genDataC ⌊ ListAnyOD ⌋ᵈ ListAnyT
--    where ListAnyT = genDataT ⌊ ListAnyOD ⌋ᵈ ListAny
--
_∋_ : {A : Set }  List A  A  Set 
xs  x = ListAny (x ≡_) xs

--private
--  toℕP : FoldP
--  toℕP = forget (quote ListAny) (quote ℕ)
--
-- unquoteDecl toℕ = defineFold toℕP toℕ
toℕ :  {ℓ' } {A : Set } {P : A  Set ℓ'} {as : List A}  ListAny P as  
toℕ (here  p) = 0
toℕ (there i) = suc (toℕ i)

--instance toℕC = genFoldC toℕP toℕ