module Calf.Computation.Potential where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Univalence using (ua→; ua-gluePath)

open import Calf.Core.Cost
open import Calf.Value
open import Calf.Value.Closed as 
open import Calf.Computation
open import Calf.Computation.Abstraction
open import Calf.Computation.Free

Potential : (X  )  𝒞
Potential {X} Φ = Abstractionᶜ (F X) (F X) (bind' λ x  F _ .charge (Φ x) (ret x))

Potential-0ℂ : Potential {X}  _  0ℂ)  F X
Potential-0ℂ =
    Abstractionᶜ (F _) (F _) (bind' λ x  F _ .charge 0ℂ (ret x))
  ≡⟨ cong (Abstractionᶜ _ _) (cong bind' (funExt λ _  F _ .charge/0)) 
    Abstractionᶜ (F _) (F _) (bind' ret)
  ≡⟨ cong (Abstractionᶜ _ _) bind'/η 
    Abstractionᶜ (F _) (F _) idᶜ
  ≡⟨ Abstractionᶜ-id 
    F _
  

square : {ΦX : X  } {ΦY : Y  }
   (f : X  Y)
   (c-⊤ c-abs : X  )
   (∀ x  c-⊤ x +ℂ ΦY (f x)  ΦX x +ℂ c-abs x)
   Potential ΦX  Potential ΦY
square {ΦX = ΦX} {ΦY = ΦY} f c-⊤ c-abs amortization =
  squareᶜ'
    (bind' λ x  F _ .charge (c-⊤ x) (ret (f x)))
    (bind' λ x  F _ .charge (c-abs x) (ret (f x)))
    λ a-⊤ 
        bind'  x  F _ .charge (ΦY x) (ret x)) .U
        (bind'  x  F _ .charge (c-⊤ x) (ret (f x))) .U a-⊤)
      ≡⟨ bind'-assoc _ _ a-⊤ 
        bind'  x 
          bind'  x  F _ .charge (ΦY x) (ret x)) .U
            (F _ .charge (c-⊤ x) (ret (f x)))) .U a-⊤
      ≡⟨ cong  e  bind' {A = F _} e .U a-⊤)
            (funExt λ x 
                bind'  x  F _ .charge (ΦY x) (ret x)) .U
                  (F _ .charge (c-⊤ x) (ret (f x)))
              ≡⟨ bind'  x  F _ .charge (ΦY x) (ret x)) .charge (c-⊤ x) (ret (f x)) 
                F _ .charge (c-⊤ x)
                  (bind'  x  F _ .charge (ΦY x) (ret x)) .U (ret (f x)))
              ≡⟨ cong (F _ .charge (c-⊤ x)) bind'/β 
                F _ .charge (c-⊤ x)
                  (F _ .charge (ΦY (f x)) (ret (f x)))
              ≡⟨ sym (F _ .charge/+) 
                F _ .charge (c-⊤ x +ℂ ΦY (f x)) (ret (f x))
              ) 
        bind'  x  F _ .charge (c-⊤ x +ℂ ΦY (f x)) (ret (f x))) .U a-⊤
      ≡⟨ cong  e  bind' {A = F _} e .U a-⊤) (funExt λ x  cong  e  F _ .charge e _) (amortization x)) 
        bind'  x  F _ .charge (ΦX x +ℂ c-abs x) (ret (f x))) .U a-⊤
      ≡⟨ cong  e  bind' {A = F _} e .U a-⊤)
            (funExt λ x 
                F _ .charge (ΦX x +ℂ c-abs x) (ret (f x))
              ≡⟨ F _ .charge/+ 
                F _ .charge (ΦX x)
                  (F _ .charge (c-abs x) (ret (f x)))
              ≡⟨ cong (F _ .charge (ΦX x)) (sym bind'/β) 
                F _ .charge (ΦX x)
                  (bind'  x  F _ .charge (c-abs x) (ret (f x))) .U (ret x))
              ≡⟨ sym (bind'  x  F _ .charge (c-abs x) (ret (f x))) .charge (ΦX x) (ret x)) 
                bind'  x  F _ .charge (c-abs x) (ret (f x))) .U
                  (F _ .charge (ΦX x) (ret x))
              ) 
        bind'  x 
          bind'  x  F _ .charge (c-abs x) (ret (f x))) .U
            (F _ .charge (ΦX x) (ret x))) .U a-⊤
      ≡⟨ sym (bind'-assoc _ _ a-⊤) 
        bind'  x  F _ .charge (c-abs x) (ret (f x))) .U
        (bind'  x  F _ .charge (ΦX x) (ret x)) .U a-⊤)
      


module _ where
  open import Calf.Computation.Open as ◯ᶜ
  open import Calf.Computation.Closed as ●ᶜ
  open import Calf.Computation.Glue
  open import Calf.Computation.Credit
  open import Calf.Computation.Copower
  open import Calf.Computation.Tensor

  private
    Σᶜ-◯ᶜ-in :  {X : 𝒱ₛ} {A :  X   𝒞} x 
      A x  ◯ᶜ (Σᶜ X A)
    Σᶜ-◯ᶜ-in x .U a◦ = η◦ (x , a◦)
    Σᶜ-◯ᶜ-in x .charge _ _ = refl

    Σᶜ-fracture-map' :  {X : 𝒱ₛ} {A B :  X   𝒞} 
      ((x :  X )  A x  ●ᶜ (B x)) 
      ●ᶜ (Σᶜ X A)  ●ᶜ (◯ᶜ (Σᶜ X B))
    Σᶜ-fracture-map' {X} {A} {B} α = ●ᶜ.bind k
      where
        k : Σᶜ X A  ●ᶜ (◯ᶜ (Σᶜ X B))
        k .U (x , a) = ●ᶜ.map (Σᶜ-◯ᶜ-in {X} {B} x) .U (α x .U a)
        k .charge c (x , a) =
            ●ᶜ.map (Σᶜ-◯ᶜ-in {X} {B} x) .U (α x .U (A x .charge c a))
          ≡⟨ cong (●ᶜ.map (Σᶜ-◯ᶜ-in {X} {B} x) .U) (α x .charge c a) 
            ●ᶜ.map (Σᶜ-◯ᶜ-in {X} {B} x) .U (●ᶜ (B x) .charge c (α x .U a))
          ≡⟨ ●ᶜ.map (Σᶜ-◯ᶜ-in {X} {B} x) .charge c (α x .U a) 
            ●ᶜ (◯ᶜ (Σᶜ X B)) .charge c (●ᶜ.map (Σᶜ-◯ᶜ-in {X} {B} x) .U (α x .U a))
          

  private opaque
    Σᶜ-fracture-map'-path :  {X : 𝒱ₛ} {A B :  X   𝒞}
       (m : Σᶜ X A  ◯ᶜ (Σᶜ X B))
       (α : (x :  X )  ●ᶜ (A x)  ●ᶜ (◯ᶜ (B x)))
       ((x :  X ) (a : U (A x))
           ●ᶜ.map (Σᶜ-◯ᶜ-in {X} {◯ᶜ  B} x) .U (α x .U (η• a))
             η• (Σᶜ-◯ᶜ-fwd {X} {B} .U (m .U (x , a))))
       PathP
           i  Σᶜ-●ᶜ {X} {A} i  ●ᶜ (Σᶜ-◯ᶜ {X} {B} i))
          (●ᶜ.map m)
          (Σᶜ-fracture-map' {X} α)
    Σᶜ-fracture-map'-path {X} {A} {B} m α coh =
      ⊸-path
        (Σᶜ-●ᶜ {X} {A})
        (cong ●ᶜ (Σᶜ-◯ᶜ {X} {B}))
        {f₀ = ●ᶜ.map m}
        {f₁ = Σᶜ-fracture-map' {X} α}
        (ua→
          {e = Σᶜ-●ᶜ-fwd {X} {A} .U , Σᶜ-●ᶜ-fwd-equiv {X} {A}}
           w 
            ●.elim
               w 
                ●.isModalPathP ●.isModal●
                  {x = ●ᶜ.map m .U w}
                  {x' = Σᶜ-fracture-map' {X} α .U (Σᶜ-●ᶜ-fwd {X} {A} .U w)})
               (x , a) 
                congP  _  η•)
                  (ua-gluePath
                    ( Σᶜ-◯ᶜ-fwd {X} {B} .U , Σᶜ-◯ᶜ-fwd-equiv {X} {B})
                    {x = m .U (x , a)}
                    refl)
                 sym (coh x a))
              w))

  potential-credit :  {X : 𝒱ₛ} Φ 
    Potential Φ  [ x  X ]  ▷[ Φ x ] 
  potential-credit {X = X} Φ =
      Potential Φ
    ≡⟨ sym (𝒞-glue-fracture-retract (Potential Φ)) 
      𝒞-Glue (𝒞-Fracture (Potential Φ))
    ≡⟨ cong 𝒞-Glue fracture-proof 
      𝒞-Glue (𝒞-Fracture ([ x  X ]  ▷[ Φ x ] ))
    ≡⟨ 𝒞-glue-fracture-retract ([ x  X ]  ▷[ Φ x ] ) 
      [ x  X ]  ▷[ Φ x ] 
    
    where
      ▷⊤-●ᶜ : (x :  X )  ●ᶜ   ●ᶜ (▷[ Φ x ] )
      ▷⊤-●ᶜ x = sym (▷-●ᶜ (Φ x) )

      ▷⊤-◯ᶜ : (x :  X )  ◯ᶜ   ◯ᶜ (▷[ Φ x ] )
      ▷⊤-◯ᶜ x = sym (▷-◯ᶜ (Φ x) )

      ▷⊤-coherence : (x :  X ) 
        PathP
           i  ▷⊤-●ᶜ x i  ●ᶜ (▷⊤-◯ᶜ x i))
          (●ᶜ.map (CHARGE (Φ x) ⨾ᶜ η◦ᶜ {}))
          (●ᶜ.map (η◦ᶜ {▷[ Φ x ] }))
      ▷⊤-coherence x = ▷-coherence (Φ x) 

      opaque
        unfolding Abstractionᶜ

        fracture-proof :
          𝒞-Fracture (Potential Φ) 
          𝒞-Fracture ([ x  X ]  ▷[ Φ x ] )
        fracture-proof =
            𝒞-Fracture (Potential Φ)
          ≡⟨ 𝒞-glue-fracture-section
                (Abstractionᶜ-FRAC _ _ (bind' λ x  F  X  .charge (Φ x) (ret x))) 
            Abstractionᶜ-FRAC (F  X ) (F  X ) (bind' λ x  F  X  .charge (Φ x) (ret x))
          ≡⟨ 𝒞-FRACTURE-pathᶜ
                (cong ●ᶜ F-Σᶜ)
                (cong ◯ᶜ F-Σᶜ)
                (congP  _ m  ●ᶜ.map (m ⨾ᶜ η◦ᶜ)) (F-Σᶜ-potential {X} Φ)) 
            record
              { A• = ●ᶜ• ([ x  X ]  )
              ; A◦ = ◯ᶜ◦ ([ x  X ]  )
              ; α• = ●ᶜ.map (Σᶜ-map {X} {const }  x  CHARGE (Φ x)) ⨾ᶜ η◦ᶜ {[ x  X ]  })
              }
          ≡⟨ 𝒞-FRACTURE-pathᶜ
                (Σᶜ-●ᶜ {X} {const })
                (Σᶜ-◯ᶜ {X} {const })
                (Σᶜ-fracture-map'-path {X} {const } {const }
                  (Σᶜ-map {X} {const }  x  CHARGE (Φ x)) ⨾ᶜ η◦ᶜ {[ x  X ]  })
                   x  ●ᶜ.map (CHARGE (Φ x) ⨾ᶜ η◦ᶜ {}))
                   x a  refl)) 
            record
              { A• = ●ᶜ• ([ x  X ]  ●ᶜ )
              ; A◦ = ◯ᶜ◦ ([ x  X ]  ◯ᶜ )
              ; α• = Σᶜ-fracture-map' {X} {const (●ᶜ )} {const (◯ᶜ )}  x  ●ᶜ.map (CHARGE (Φ x) ⨾ᶜ η◦ᶜ {}))
              }
          ≡⟨ 𝒞-FRACTURE-pathᶜ
                (cong (●ᶜ  Σᶜ X) (funExt ▷⊤-●ᶜ))
                (cong (◯ᶜ  Σᶜ X) (funExt ▷⊤-◯ᶜ))
                (congP  _  Σᶜ-fracture-map' {X}) (funExt ▷⊤-coherence)) 
            record
              { A• = ●ᶜ• ([ x  X ]  ●ᶜ (▷[ Φ x ] ))
              ; A◦ = ◯ᶜ◦ ([ x  X ]  ◯ᶜ (▷[ Φ x ] ))
              ; α• = Σᶜ-fracture-map' {X}  x  ●ᶜ (▷[ Φ x ] )}  x  ◯ᶜ (▷[ Φ x ] )}  x  ●ᶜ.map (η◦ᶜ {▷[ Φ x ] }))
              }
          ≡⟨ 𝒞-FRACTURE-pathᶜ
                (sym (Σᶜ-●ᶜ {X}  x  ▷[ Φ x ] }))
                (sym (Σᶜ-◯ᶜ {X}  x  ▷[ Φ x ] }))
                (symP (Σᶜ-fracture-map'-path {X}  x  ▷[ Φ x ] }  x  ▷[ Φ x ] }
                  (η◦ᶜ {[ x  X ]  ▷[ Φ x ] })
                   x  ●ᶜ.map (η◦ᶜ {▷[ Φ x ] }))
                   x a  refl))) 
            𝒞-Fracture ([ x  X ]  ▷[ Φ x ] )