open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function
open import Cubical.Foundations.Structure

module Calf.Computation.CList1 where

open import Calf.Core.Abstract
open import Calf.Core.Cost
open import Calf.Value
open import Calf.Value.List
open import Calf.Value.Nat
import Calf.Value.Closed as 
import Calf.Value.Open as 
open import Calf.Computation
open import Calf.Computation.Free as F
open import Calf.Computation.Copower
open import Calf.Computation.Open as ◯ᶜ
open import Calf.Computation.Closed as ●ᶜ
open import Calf.Computation.Glue
open import Calf.Computation.Abstraction
open import Calf.Computation.Potential
open import Calf.Computation.Credit

opaque
  CList₁ :   𝒱  𝒞
  CList₁ c X = Potential {List X}  l  length l  c)

  cnil₁ : U (CList₁ c X)
  cnil₁ {c} =
    triangleᶜ'
      {F _} {F _} {bind'  l  F _ .charge (length l  c) (ret l))}
      (ret [])
      (ret [])
      (bind'/β  F _ .charge/0)

  ccons₁ : X  ▷[ c ] (CList₁ c X)  CList₁ c X
  ccons₁ {X} {c} x =
    subst (_⊸ CList₁ c X)
      ( Abstractionᶜ (F (List X)) (F (List X)) (CHARGE c ⨾ᶜ bind'  l  F _ .charge (length l  c) (ret l)))
      ≡⟨ cong (Abstractionᶜ _ _) (CHARGE-commute _ _) 
        Abstractionᶜ (F (List X)) (F (List X)) (bind'  l  F _ .charge (length l  c) (ret l)) ⨾ᶜ CHARGE c)
      ≡⟨ sym Abstractionᶜ-Abstractionᶜ 
        Abstractionᶜ
          (CList₁ c X)
          (CList₁ c X)
          (squareᶜ'
            (CHARGE c)
            (CHARGE c)
             e  bind'  l  F _ .charge (length l  c) (ret l)) .charge c e))
      ≡⟨ cong (Abstractionᶜ _ _) (squareᶜ'-charge _) 
        ▷[ c ] (CList₁ c X)
      ) $
    squareᶜ'
      (F.map (x ∷_))
      (F.map (x ∷_))
      λ e 
        bind'  l  F _ .charge (length l  c) (ret l)) .U (F.map (x ∷_) .U e)
      ≡⟨ refl 
        bind'  l  F _ .charge (length l  c) (ret l)) .U (bind' (ret  (x ∷_)) .U e)
      ≡⟨ bind'-assoc _ _ _ 
        bind'  l 
          bind'  l 
            F _ .charge (length l  c) (ret l))
          .U (ret (x  l)))
        .U e
      ≡⟨ cong  h  bind' {A = F _} h .U e) (funExt λ _  bind'/β) 
        bind'  l 
          F _ .charge (length (x  l)  c) (ret (x  l)))
        .U e
      ≡⟨ refl 
        bind'  l 
          F _ .charge (suc (length l)  c) (ret (x  l)))
        .U e
      ≡⟨ refl 
        bind'  l 
          F _ .charge (c +ℂ (length l  c)) (ret (x  l)))
        .U e
      ≡⟨ cong  h  bind' {A = F _} h .U e) (funExt  l  F _ .charge/+)) 
        bind'  l 
          F _ .charge c (F _ .charge (length l  c) (ret (x  l))))
        .U e
      ≡⟨ bind'-charge _ _ _ 
        bind'  l 
          F _ .charge (length l  c) (ret (x  l)))
        .U (F _ .charge c e)
      ≡⟨ sym
            (cong  h  bind' {A = F _} h .U (F _ .charge c e))
              (funExt λ l 
                cong (F _ .charge (length l  c)) bind'/β)) 
        bind'  l 
          F _ .charge (length l  c) (F.map (x ∷_) .U (ret l)))
        .U (F _ .charge c e)
      ≡⟨ sym
            (cong  h  bind' {A = F _} h .U (F _ .charge c e))
              (funExt λ l 
                F.map (x ∷_) .charge (length l  c) (ret l))) 
        bind'  l 
          F.map (x ∷_) .U (F _ .charge (length l  c) (ret l)))
        .U (F _ .charge c e)
      ≡⟨ sym (bind'-assoc _ _ _) 
        F.map (x ∷_) .U
          (bind'  l 
            F _ .charge (length l  c) (ret l))
          .U (F _ .charge c e))
      

  opaque
    unfolding Abstractionᶜ

    cfoldr₁ :
        U A
       (X  (▷[ c ] A  A))
       CList₁ c X  A
    cfoldr₁ {A = A} {X = X} {c} enil econs =
      subst (CList₁ c X ⊸_) (𝒞-glue-fracture-retract A) $
      squareᶜ go• go◦ go-•⊸◦
      where
        costᶜ : F (List X)  F (List X)
        costᶜ =
          bind' {A = F _} λ l 
          CHARGE {A = F _} (length l  c) .U (ret l)

        fold• : (List X)  U (●ᶜ A)
        fold• =
          foldr
             x  ●.map (econs x .U)  transport (cong U (sym (▷-●ᶜ c A))))
            (η• enil)

        go• : ●ᶜ (F (List X))  ●ᶜ A
        go• =
          ●ᶜ.bind $
          bind' fold•

        open-econs : X  ◯ᶜ A  ◯ᶜ A
        open-econs x .U a◦ =
          ◯.map (econs x .U) (transport (cong U (sym (▷-◯ᶜ c A))) a◦)
        open-econs x .charge d a◦ = funExt λ abs 
            econs x .U
              (transport (cong U (sym (▷-◯ᶜ c A)))
                (◯ᶜ A .charge d a◦)
                abs)
          ≡⟨ cong  q  econs x .U (q abs))
                (transport-charge (sym (▷-◯ᶜ c A)) d a◦) 
            econs x .U
              ((◯ᶜ (▷[ c ] A) .charge d
                (transport (cong U (sym (▷-◯ᶜ c A))) a◦)) abs)
          ≡⟨ econs x .charge d
                (transport (cong U (sym (▷-◯ᶜ c A))) a◦ abs) 
            A .charge d
              (econs x .U (transport (cong U (sym (▷-◯ᶜ c A))) a◦ abs))
          

        fold◦ : (List X)  U (◯ᶜ A)
        fold◦ =
          foldr
             x  open-econs x .U)
            (η◦ enil)

        go◦ : ◯ᶜ (F (List X))  ◯ᶜ A
        go◦ =
          ◯ᶜ.bind {A = F _} {B = A} $
          bind' fold◦

        fold•ᶜ : F (List X)  ●ᶜ A
        fold•ᶜ = bind' fold•

        go-•⊸◦ :
          go• ⨾ᶜ ●ᶜ.map η◦ᶜ  ●ᶜ.map (costᶜ ⨾ᶜ η◦ᶜ) ⨾ᶜ ●ᶜ.map go◦
        go-•⊸◦ =
            go• ⨾ᶜ ●ᶜ.map η◦ᶜ
          ≡⟨ refl 
            ●ᶜ.bind fold•ᶜ ⨾ᶜ ●ᶜ.map η◦ᶜ
          ≡⟨ ●ᶜ.bind-map _ _ 
            ●ᶜ.bind (fold•ᶜ ⨾ᶜ ●ᶜ.map η◦ᶜ)
          ≡⟨ cong ●ᶜ.bind (bind'-path _ _ (funExt fold•-coherence)) 
            ●ᶜ.bind (costᶜ ⨾ᶜ η◦ᶜ ⨾ᶜ go◦ ⨾ᶜ η•ᶜ)
          ≡⟨ ●ᶜ.bind-η• _ 
            ●ᶜ.map (costᶜ ⨾ᶜ η◦ᶜ ⨾ᶜ go◦)
          ≡⟨ sym (●ᶜ.map-∘ _ _) 
            ●ᶜ.map (costᶜ ⨾ᶜ η◦ᶜ) ⨾ᶜ ●ᶜ.map go◦
          
          where
            cost-cons :  x l 
              CHARGE {A = F _} c .U (F.map (x ∷_) .U (costᶜ .U (ret l)))
               costᶜ .U (ret (x  l))
            cost-cons x l =
                CHARGE {A = F _} c .U (F.map (x ∷_) .U (costᶜ .U (ret l)))
              ≡⟨ cong  e  CHARGE {A = F _} c .U (F.map (x ∷_) .U e)) bind'/β 
                CHARGE {A = F _} c .U
                  (F.map (x ∷_) .U
                    (CHARGE {A = F _} (length l  c) .U (ret l)))
              ≡⟨ cong (CHARGE {A = F _} c .U)
                    (cong ((_$ ret l)  U)
                      (CHARGE-commute
                        (length l  c) (F.map (x ∷_)))) 
                CHARGE {A = F _} c .U
                  (CHARGE {A = F _} (length l  c) .U
                    (F.map (x ∷_) .U (ret l)))
              ≡⟨ cong  e  CHARGE {A = F _} c .U (CHARGE {A = F _} (length l  c) .U e)) bind'/β 
                CHARGE {A = F _} c .U
                  (CHARGE {A = F _} (length l  c) .U (ret (x  l)))
              ≡⟨ sym (cong ((_$ ret (x  l))  U) (CHARGE-+ {A = F _} c (length l  c))) 
                CHARGE {A = F _} (length (x  l)  c) .U (ret (x  l))
              ≡⟨ sym bind'/β 
                costᶜ .U (ret (x  l))
              

            go◦-cons :  x (e : U (F (List X))) 
              go◦ .U (η◦ᶜ {A = F _} .U (F.map (x ∷_) .U e))
               open-econs x .U (go◦ .U (η◦ᶜ {A = F _} .U e))
            go◦-cons x e =
                go◦ .U (η◦ᶜ {A = F _} .U (F.map (x ∷_) .U e))
              ≡⟨ refl 
                bind' fold◦ .U (F.map (x ∷_) .U e)
              ≡⟨ bind'-assoc _ _ _ 
                bind'  l 
                  bind' fold◦ .U (ret (x  l)))
                .U e
              ≡⟨ cong  h  bind' {A = ◯ᶜ A} h .U e) (funExt λ l  bind'/β) 
                bind'  l 
                  open-econs x .U (fold◦ l))
                .U e
              ≡⟨ sym (bind'-map (open-econs x) _ _) 
                open-econs x .U (bind' fold◦ .U e)
              ≡⟨ refl 
                open-econs x .U (go◦ .U (η◦ᶜ {A = F _} .U e))
              

            open-cons-charge :  x l 
              ◯.map (econs x .U)
                (transport (cong U (sym (▷-◯ᶜ c A)))
                  (CHARGE {A = ◯ᶜ A} c .U
                    ((costᶜ ⨾ᶜ η◦ᶜ ⨾ᶜ go◦) .U (ret l))))
               (costᶜ ⨾ᶜ η◦ᶜ ⨾ᶜ go◦) .U (ret (x  l))
            open-cons-charge x l =
              let
                e = costᶜ .U (ret l)
                r = go◦ .U (η◦ᶜ {A = F _} .U e)
              in
                open-econs x .U (CHARGE {A = ◯ᶜ A} c .U r)
              ≡⟨ cong ((_$ r)  U) (CHARGE-commute c (open-econs x)) 
                CHARGE {A = ◯ᶜ A} c .U (open-econs x .U r)
              ≡⟨ cong (CHARGE {A = ◯ᶜ A} c .U) (sym (go◦-cons x e)) 
                CHARGE {A = ◯ᶜ A} c .U
                  (go◦ .U (η◦ᶜ {A = F _} .U (F.map (x ∷_) .U e)))
              ≡⟨ sym (cong ((_$ η◦ᶜ {A = F _} .U (F.map (x ∷_) .U e))  U)
                    (CHARGE-commute c go◦)) 
                go◦ .U
                  (η◦ᶜ {A = F _} .U
                    (CHARGE {A = F _} c .U (F.map (x ∷_) .U e)))
              ≡⟨ cong  e  go◦ .U (η◦ᶜ {A = F _} .U e)) (cost-cons x l) 
                go◦ .U (η◦ᶜ {A = F _} .U (costᶜ .U (ret (x  l))))
              

            fold•-coherence :  l 
              (fold•ᶜ ⨾ᶜ ●ᶜ.map η◦ᶜ) .U (ret l)
               (costᶜ ⨾ᶜ η◦ᶜ ⨾ᶜ go◦ ⨾ᶜ η•ᶜ) .U (ret l)
            fold•-coherence [] =
                (fold•ᶜ ⨾ᶜ ●ᶜ.map η◦ᶜ) .U (ret [])
              ≡⟨ cong (●.map (η◦ᶜ {A = A} .U)) bind'/β 
                η•ᶜ {A = ◯ᶜ A} .U (η◦ᶜ {A = A} .U enil)
              ≡⟨ cong (η•ᶜ {A = ◯ᶜ A} .U) (sym bind'/β) 
                η•ᶜ {A = ◯ᶜ A} .U
                  (bind' fold◦ .U (ret []))
              ≡⟨ refl 
                η•ᶜ {A = ◯ᶜ A} .U
                  (go◦ .U (η◦ᶜ {A = F _} .U (ret [])))
              ≡⟨ cong
                     e  η•ᶜ {A = ◯ᶜ A} .U (go◦ .U (η◦ᶜ {A = F _} .U e)))
                    (sym (cong ((_$ ret [])  U) (CHARGE-0 {A = F _}))) 
                η•ᶜ {A = ◯ᶜ A} .U
                  (go◦ .U (η◦ᶜ {A = F _} .U
                    (CHARGE {A = F _} 0ℂ .U (ret []))))
              ≡⟨ cong
                     e  η•ᶜ {A = ◯ᶜ A} .U
                      (go◦ .U (η◦ᶜ {A = F _} .U e)))
                    (sym bind'/β) 
                η•ᶜ {A = ◯ᶜ A} .U
                  (go◦ .U (η◦ᶜ {A = F _} .U
                    (costᶜ .U (ret []))))
              ≡⟨ refl 
                (costᶜ ⨾ᶜ η◦ᶜ ⨾ᶜ go◦ ⨾ᶜ η•ᶜ) .U (ret [])
              
            fold•-coherence (x  l) =
                (fold•ᶜ ⨾ᶜ ●ᶜ.map η◦ᶜ) .U (ret (x  l))
              ≡⟨ cong (●.map (η◦ᶜ {A = A} .U)) bind'/β 
                ●.map (η◦ᶜ {A = A} .U)
                  (●.map (econs x .U)
                    (transport (cong U (sym (▷-●ᶜ c A)))
                      (fold• l)))
              ≡⟨ cong
                   q  ●.map (η◦ᶜ {A = A} .U)
                    (●.map (econs x .U)
                      (transport (cong U (sym (▷-●ᶜ c A))) q)))
                  (sym bind'/β) 
                ●.map (η◦ᶜ {A = A} .U)
                  (●.map (econs x .U)
                    (transport (cong U (sym (▷-●ᶜ c A)))
                      (fold•ᶜ .U (ret l))))
              ≡⟨ (
                let
                    q▷• = transport (cong U (sym (▷-●ᶜ c A))) (fold•ᶜ .U (ret l))
                    q▷◦ = transport (cong U (sym (▷-◯ᶜ c A))) (CHARGE {A = ◯ᶜ A} c .U ((costᶜ ⨾ᶜ η◦ᶜ ⨾ᶜ go◦) .U (ret l)))

                    q▷-coh : ●ᶜ.map (η◦ᶜ {A = ▷[ c ] A}) .U q▷•  η• q▷◦
                    q▷-coh =
                        ●ᶜ.map (η◦ᶜ {A = ▷[ c ] A}) .U q▷•
                      ≡⟨ transport-▷ c A (fold•ᶜ .U (ret l)) 
                        transport (cong  C  U (●ᶜ C)) (sym (▷-◯ᶜ c A)))
                          ((▷-FRAC c A .𝒞-FRAC.α•) .U (fold•ᶜ .U (ret l)))
                      ≡⟨ cong
                          (transport (cong  C  U (●ᶜ C)) (sym (▷-◯ᶜ c A))))
                          (sym (●.map-∘ (η◦ᶜ {A = A} .U) (◯ᶜ A .charge c) (fold•ᶜ .U (ret l))) ) 
                        transport (cong  C  U (●ᶜ C)) (sym (▷-◯ᶜ c A)))
                          (●.map (CHARGE {A = ◯ᶜ A} c .U)
                            ((fold•ᶜ ⨾ᶜ ●ᶜ.map η◦ᶜ) .U (ret l)))
                      ≡⟨ cong
                          (transport (cong  C  U (●ᶜ C)) (sym (▷-◯ᶜ c A))))
                          (cong (●.map (CHARGE {A = ◯ᶜ A} c .U)) (fold•-coherence l)) 
                        η• q▷◦
                      
                  in
                  fracture-map-coh (econs x) q▷• q▷◦ q▷-coh
              ) 
                η•ᶜ {A = ◯ᶜ A} .U (◯.map (econs x .U)
                  (transport (cong U (sym (▷-◯ᶜ c A)))
                    (CHARGE {A = ◯ᶜ A} c .U ((costᶜ ⨾ᶜ η◦ᶜ ⨾ᶜ go◦) .U (ret l)))))
              ≡⟨ cong (η•ᶜ {A = ◯ᶜ A} .U) (open-cons-charge x l) 
                (costᶜ ⨾ᶜ η◦ᶜ ⨾ᶜ go◦ ⨾ᶜ η•ᶜ) .U (ret (x  l))