module Calf.Computation.Tensor.Base where

open import Calf.Core.Cost
open import Calf.Value
open import Calf.Computation

open import Cubical.HITs.SetTruncation

 : 𝒞
 .U = 
 .is-set = isSetℂ
 .charge = _+ℂ_
 .charge/0 = +ℂ-identityˡ _
 .charge/+ = +ℂ-assoc _ _ _

module _ where
  data _⊛_ (A B : 𝒞) : 𝒱 where
    inj : (a : U A) (b : U B)  A  B
    law :  c a b  inj (A .charge c a) b  inj a (B .charge c b)

  charge⊛ :   A  B  A  B
  charge⊛ {A} c (inj a b) = inj (A .charge c a) b
  charge⊛ {A} {B} c (law c' a b i) =
    ( cong  z  inj {A} {B} z b) (charge/comm A)
     law c' (A .charge c a) b ) i

  ∥∥₂-≡
    : isSet Y
     (f g :  X ∥₂  Y)
     (∀ x  f  x ∣₂  g  x ∣₂)
      z  f z  g z
  ∥∥₂-≡ isSetY f g p = elim  z  isProp→isSet (isSetY (f z) (g z))) p

  module _ {A B : 𝒞} where
    ⊛-elimProp
      : {P : A  B  𝒱}
       (∀ w  isProp (P w))
       (∀ a b  P (inj a b))
        w  P w
    ⊛-elimProp pP f (inj a b) = f a b
    ⊛-elimProp pP f (law c a b i) =
      isProp→PathP  i  pP (law c a b i))
        (f (A .charge c a) b)
        (f a (B .charge c b))
        i

    ⊛-≡
      : isSet Y
       (f g :  A  B ∥₂  Y)
       (∀ a b  f  inj a b ∣₂  g  inj a b ∣₂)
        z  f z  g z
    ⊛-≡ isSetY f g p = ∥∥₂-≡ isSetY f g (⊛-elimProp  _  isSetY _ _) p)

  _⊗_ : 𝒞  𝒞  𝒞
  (A  B) .U =  A  B ∥₂
  (A  B) .is-set = squash₂
  (A  B) .charge c = map (charge⊛ c)
  (A  B) .charge/0 {x} =
    ⊛-≡ squash₂ (map (charge⊛ 0ℂ))  z  z)
       a b  cong  z   inj {A} z b ∣₂) (A .charge/0 {a}))
      x
  (A  B) .charge/+ {x} {c₁} {c₂} =
    ⊛-≡ squash₂ (map (charge⊛ (c₁ +ℂ c₂)))  z  map (charge⊛ c₁) (map (charge⊛ c₂) z))
       a b  cong  z   inj {A} z b ∣₂) (A .charge/+ {a} {c₁} {c₂}))
      x

  _∥_ : U A  U B  U (A  B)
  a  b =  inj a b ∣₂

  ⊗-rec : {A B C : 𝒞}
     (h : U A  U B  U C)
     (∀ c a b  h (A .charge c a) b  C .charge c (h a b))
     (∀ c a b  h a (B .charge c b)  C .charge c (h a b))
     (A  B)  C
  ⊗-rec {A} {B} {C} h hl hr .U = rec (C .is-set) h₀
    where
      h₀ : A  B  U C
      h₀ (inj a b) = h a b
      h₀ (law c a b i) = (hl c a b  sym (hr c a b)) i
  ⊗-rec {A} {B} {C} h hl hr .charge c =
    ⊛-≡ (C .is-set)
       w  ⊗-rec {A} {B} {C} h hl hr .U ((A  B) .charge c w))
       w  C .charge c (⊗-rec {A} {B} {C} h hl hr .U w))
      (hl c)

  map₂ :  {A₁ A₂ B₁ B₂}
     (A₁  A₂)  (B₁  B₂)
     (A₁  B₁)  (A₂  B₂)
  map₂ {A₁} {A₂} {B₁} {B₂} f g =
    ⊗-rec  a b   inj (f .U a) (g .U b) ∣₂)
       c a b  cong  z   inj z (g .U b) ∣₂) (f .charge c a))
       c a b 
          cong  z   inj (f .U a) z ∣₂) (g .charge c b)
         sym (cong ∣_∣₂ (law c (f .U a) (g .U b))))

⊗-identityʳ : A    A
⊗-identityʳ {A = A} = conservativity fwd fwd-equiv
  where
    fwd-U : A    U A
    fwd-U (inj a c) = A .charge c a
    fwd-U (law c' a c i) =
      ( sym (A .charge/+ {a} {c} {c'})
       cong  d  A .charge d a) (+ℂ-comm c c') ) i

    fwd : A    A
    fwd .U = rec (A .is-set) fwd-U
    fwd .charge c₀ =
      ⊛-≡ (A .is-set)
         z  fwd .U ((A  ) .charge c₀ z))
         z  A .charge c₀ (fwd .U z))
         a c  charge/comm A)

    fwd-equiv : isEquivᶜ fwd
    fwd-equiv = isoToIsEquiv (iso (fwd .U) inv sect retr)
      where
        inv : U A  U (A  )
        inv a =  inj a 0ℂ ∣₂

        sect :  a  fwd .U (inv a)  a
        sect a = A .charge/0

        retr :  z  inv (fwd .U z)  z
        retr =
          ⊛-≡ squash₂  z  inv (fwd .U z))  z  z)
             a c 
                cong ∣_∣₂ (law c a 0ℂ)
               cong  d   inj a d ∣₂) (+ℂ-identityʳ c))

opaque
  map₂-equivᶜ :  {A₁ A₂ B₁ B₂} {f : A₁  A₂} {g : B₁  B₂}
     isEquivᶜ f  isEquivᶜ g
     isEquivᶜ (map₂ f g)
  map₂-equivᶜ {f = f} {g = g} fe ge =
    isoToIsEquiv
      (iso (map₂ f g .U) (map₂ (invEquivᶜ f fe) (invEquivᶜ g ge) .U)
        (⊛-≡ squash₂
           z  map₂ f g .U (map₂ (invEquivᶜ f fe) (invEquivᶜ g ge) .U z))  z  z)
           a b i   inj (secEq (f .U , fe) a i) (secEq (g .U , ge) b i) ∣₂))
        (⊛-≡ squash₂
           z  map₂ (invEquivᶜ f fe) (invEquivᶜ g ge) .U (map₂ f g .U z))  z  z)
           a b i   inj (retEq (f .U , fe) a i) (retEq (g .U , ge) b i) ∣₂)))

⊗-isContr : isContr (U A)  isContr (U B)  isContr (U (A  B))
⊗-isContr {A} {B} cA cB .fst =  inj (cA .fst) (cB .fst) ∣₂
⊗-isContr {A} {B} cA cB .snd =
  ⊛-≡ squash₂  _   inj (cA .fst) (cB .fst) ∣₂)  w  w)
     a b i   inj (cA .snd a i) (cB .snd b i) ∣₂)