module Calf.Computation.Tensor.Closed where

open import Cubical.Foundations.Structure

open import Calf.Core.Abstract
open import Calf.Value
open import Calf.Computation
open import Calf.Computation.Tensor.Base

open import Cubical.HITs.SetTruncation
open import Cubical.Foundations.Equiv.Properties using (isEquiv[equivFunA≃B∘f]→isEquiv[f])

open import Calf.Computation.Closed as ●ᶜ hiding (map)
import Calf.Value.Closed as 
import Calf.Value.Open as 

module _ {A B : 𝒞} where
  private
    ⊗•-isContr : (abs :  ABS )  isContr (U (●ᶜ A  ●ᶜ B))
    ⊗•-isContr abs = ⊗-isContr (◯-isConnected abs) (◯-isConnected abs)

  private
    ⊗• : 𝒞•
    ⊗• = (●ᶜ A  ●ᶜ B) , isConnected◯→isModal● (◯.◯isContr→isConnected ⊗•-isContr)

  ●ᶜ-⊗-fwd : ●ᶜ (A  B)  (●ᶜ A  ●ᶜ B)
  ●ᶜ-⊗-fwd = ●ᶜ-rec ⊗• (map₂ η•ᶜ η•ᶜ)

  private
    comb-in : U (●ᶜ B)  A  ●ᶜ (A  B)
    comb-in b• .U a = ●.map  b   inj a b ∣₂) b•
    comb-in b• .charge c a =
      sym (●.map-∘  b   inj a b ∣₂) ((A  B) .charge c) b•)

    combᶜ : U (●ᶜ B)  ●ᶜ A  ●ᶜ (A  B)
    combᶜ b• = ●ᶜ.bind (comb-in b•)

    combᶜ-charge :  c b•  combᶜ b• ⨾ᶜ CHARGE c  combᶜ (●ᶜ B .charge c b•)
    combᶜ-charge c b• =
        combᶜ b• ⨾ᶜ CHARGE c
      ≡⟨ ⊸-path refl refl refl 
        combᶜ b• ⨾ᶜ ●ᶜ.map (CHARGE c)
      ≡⟨ ●ᶜ.bind-map (comb-in b•) (CHARGE c) 
        ●ᶜ.bind (comb-in b• ⨾ᶜ ●ᶜ.map (CHARGE c))
      ≡⟨ cong ●ᶜ.bind
            (⊸-path refl refl (funExt λ a 
                ●.map-∘  b   inj a b ∣₂) ((A  B) .charge c) b•
               cong  h  ●.map h b•) (funExt λ b  cong ∣_∣₂ (law c a b))
               sym (●.map-∘ (B .charge c)  b   inj a b ∣₂) b•))) 
        ●ᶜ.bind (comb-in (●ᶜ B .charge c b•))
      

    comb : ●.● (U A)  ●.● (U B)  ●.●  A  B ∥₂
    comb a• b• = combᶜ b• .U a•

    sect-pt :  a• b•  ●ᶜ-⊗-fwd .U (comb a• b•)   inj a• b• ∣₂
    sect-pt a• b• =
      ●.ind-prop  a•  ●ᶜ-⊗-fwd .U (comb a• b•)   inj a• b• ∣₂)
         _  squash₂ _ _)
         a 
          ●.ind-prop  b•  ●ᶜ-⊗-fwd .U (comb (●.η• a) b•)   inj (●.η• a) b• ∣₂)
             _  squash₂ _ _)
             b  ●ᶜ-rec-β ⊗• (map₂ η•ᶜ η•ᶜ)  inj a b ∣₂)
             abs  isContr→isProp (⊗•-isContr abs) _ _)
            b•)
         abs  isContr→isProp (⊗•-isContr abs) _ _)
        a•

  ⊗-str● : (●ᶜ A  ●ᶜ B)  ●ᶜ (A  B)
  ⊗-str● =
    ⊗-rec comb
       c a• b•  combᶜ b• .charge c a•)
       c a• b•  cong  h  h .U a•) (sym (combᶜ-charge c b•)))

  ●ᶜ-⊗-equiv : isEquivᶜ ●ᶜ-⊗-fwd
  ●ᶜ-⊗-equiv = isoToIsEquiv (iso (●ᶜ-⊗-fwd .U) (⊗-str● .U) sect retr)
    where
      sect :  y  ●ᶜ-⊗-fwd .U (⊗-str● .U y)  y
      sect = ⊛-≡ squash₂  y  ●ᶜ-⊗-fwd .U (⊗-str● .U y))  y  y) sect-pt

      retr :  x  ⊗-str● .U (●ᶜ-⊗-fwd .U x)  x
      retr =
        ●.ind-prop _  _  ●.isSet● squash₂ _ _)
          (⊛-≡ (●.isSet● squash₂)  w  ⊗-str● .U (●ᶜ-⊗-fwd .U (●.η• w))) ●.η•
             a b  cong (⊗-str● .U) (●ᶜ-rec-β ⊗• (map₂ η•ᶜ η•ᶜ)  inj a b ∣₂)))
           abs  ●.◯-isProp● abs _ _)

  ●ᶜ-⊗ : ●ᶜ (A  B)  (●ᶜ A  ●ᶜ B)
  ●ᶜ-⊗ = conservativity ●ᶜ-⊗-fwd ●ᶜ-⊗-equiv

●ᶜ-⊗-natural : {A A' B B' : 𝒞} (f : A  A') (g : B  B')
    w 
    map₂ (●ᶜ.map f) (●ᶜ.map g) .U (●ᶜ-⊗-fwd .U w)
     ●ᶜ-⊗-fwd .U (●ᶜ.map (map₂ f g) .U w)
●ᶜ-⊗-natural {A} {A'} {B} {B'} f g =
  ●.ind-prop _  _  squash₂ _ _)
    (⊛-≡ squash₂
       z  map₂ (●ᶜ.map f) (●ᶜ.map g) .U (●ᶜ-⊗-fwd .U (●.η• z)))
       z  ●ᶜ-⊗-fwd .U (●ᶜ.map (map₂ f g) .U (●.η• z)))
       a b  refl))
     abs  isContr→isProp (⊗-isContr (◯-isConnected abs) (◯-isConnected abs)) _ _)

opaque
  ●ᶜ-map₂-equiv : {A A' B B' : 𝒞} {f : A  A'} {g : B  B'}
     isEquiv (●ᶜ.map f .U)  isEquiv (●ᶜ.map g .U)
     isEquiv (●ᶜ.map (map₂ f g) .U)
  ●ᶜ-map₂-equiv {A} {A'} {B} {B'} {f} {g} fe ge =
    isEquiv[equivFunA≃B∘f]→isEquiv[f] (●ᶜ.map (map₂ f g) .U) (●ᶜ-⊗-fwd .U , ●ᶜ-⊗-equiv)
      (subst isEquiv (funExt (●ᶜ-⊗-natural f g))
        (compEquiv (●ᶜ-⊗-fwd .U , ●ᶜ-⊗-equiv)
          (map₂ (●ᶜ.map f) (●ᶜ.map g) .U , map₂-equivᶜ {f = ●ᶜ.map f} {g = ●ᶜ.map g} fe ge) .snd))