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

module Examples.WithMacros.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.WithMacros.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

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 [] _∷_ = defineByDataD  AlgListOD ⌋ᵈ AlgList (AlgList.[] List.∷ AlgList._∷_ List.∷ List.[])
--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 [] _∷_ = defineByDataD  VecOD ⌋ᵈ Vec (Vec.[] List.∷ Vec._∷_ List.∷ List.[])
--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 (λ x₁ → x₁) (DataC.toN (findDataC (quote Vec))))
--     (cong (bimap (λ 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 zero suc = defineByDataD  LenOD ⌋ᵈ Len (Len.zero  Len.suc  [])
--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 [] ⟨_,_⟩∷_ = defineByDataD  ListPOD ⌋ᵈ ListP (ListP.[]  ListP.⟨_,_⟩∷_  List.[])
--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 [] _∷_ = defineByDataD  ListAllOD ⌋ᵈ ListAll (ListAll.[] List.∷ ListAll._∷_ List.∷ List.[])
--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 (p ∷ all) =
--   trans
--    (cong
--     (bimap (λ x₂ → x₂) (DataC.toN (findDataD (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 here there = defineByDataD  ListAnyOD ⌋ᵈ ListAny (ListAny.here  ListAny.there  [])
--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 _
_∋_ {A = A} xs x = ListAny A (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ℕ