Derivative.ChainRule

{-# OPTIONS --safe #-}
module Derivative.ChainRule where

open import Derivative.Prelude
open import Derivative.Basics.Decidable as Dec
open import Derivative.Basics.Embedding
open import Derivative.Basics.Sigma
open import Derivative.Basics.Sum as Sum using (_⊎_ ; inl ; inr)
open import Derivative.Basics.Unit

open import Derivative.Container
open import Derivative.Derivative
open import Derivative.Isolated
import      Derivative.Isolated.S1 as S1
open import Derivative.Remove

open import Cubical.Foundations.Equiv.Properties
open import Cubical.Foundations.Path using (congPathIso)
open import Cubical.Foundations.Transport using (substEquiv ; isSet-subst ; subst⁻)
open import Cubical.Data.Sigma
open import Cubical.Relation.Nullary.HLevels using (isPropDiscrete)

private
  variable
     ℓS ℓP : Level

open Container
open Cart

module _ (F G : Container  ) where
  open Container F renaming (Shape to S ; Pos to P)
  open Container G renaming (Shape to T ; Pos to Q)

  chain-shape-equiv-left :
    ((Σ[ (s , p)  (Σ[ s  S ] (P s °)) ] (P s ∖° p  T)) × (Σ[ t  T ] Q t °))
      
    (Σ[ (s , f)  Σ[ s  S ] (P s  T) ] (Σ[   (P s) ° ] Q (f ( .fst)) °))
  chain-shape-equiv-left =
    ((Σ[ (s , )  (Σ[ s  S ] (P s °)) ] (P s ∖°   T)) × (Σ[ t  T ] Q t °))
      ≃⟨ strictEquiv  (((s , ) , f) , t , )  ((s , ) , (f , t) , ))  ((s , ) , (f , t) , )  (((s , ) , f) , t , )) 
    ((Σ[ (s , )  (Σ[ s  S ] (P s °)) ] (Σ[ (_ , t)  (P s ∖°   T) × T ] Q t °)))
      ≃⟨ Σ-cong-equiv-snd  (s , )  invEquiv $ Σ-cong-equiv-fst $ ungraftEquiv ) 
    ((Σ[ (s , )  (Σ[ s  S ] (P s °)) ] (Σ[ f  (P s  T) ] Q (f ( .fst)) °)))
      ≃⟨ strictEquiv  ((s , ) , (f , q))  ((s , f) ,  , q))  ((s , f) ,  , q)  ((s , ) , (f , q))) 
    (Σ[ (s , f)  Σ[ s  S ] (P s  T) ] (Σ[   P s ° ] Q (f ( .fst)) °))
      ≃∎

  chain-rule : Cart ((( F) [ G ])   G) ( (F [ G ]))
  chain-rule =
    ((( F) [ G ])   G)
      ⊸⟨ Equiv→Cart η₀ 
    H
      ⊸⟨ η₁ 
    ( (F [ G ]))
      ⊸∎
    module chain-rule where
      H : Container _ _
      H .Shape = Σ[ (s , f)  Σ[ s  S ] (P s  T) ] (Σ[   (P s) ° ] Q (f ( .fst)) °)
      H .Pos ((s , f) , ( , )) = (Σ[ (p , _)  P s ∖°  ] Q (f p))  (Q (f ( .fst)) ∖° )

      η₀ : Equiv ((( F) [ G ])   G) H
      η₀ = Equiv-inv $ Equiv-fst $ invEquiv chain-shape-equiv-left

      η₁ : Cart H ( (F [ G ]))
      η₁ .shape = Σ-map-snd λ (s , f)  Σ-isolate (P s) (Q  f)
      η₁ .pos ((s , f) , ( , )) = invEquiv (isIsolatedFst→Σ-remove-equiv ( .snd))

  chain-shape-map :
    (Σ[ (s , p)  (Σ[ s  S ] (P s °)) ] (P s  (p .fst)  T)) × (Σ[ t  T ] Q t °)
      
    (Σ[ (s , f)  Σ[ s  S ] (P s  T) ] (Σ[ p  (P s) ] Q (f p)) °)
  chain-shape-map = chain-rule .shape

  isEmbedding-chain-shape-map : isEmbedding chain-shape-map
  isEmbedding-chain-shape-map = isEmbedding-∘
    {f = Σ-map-snd λ (s , f)  Σ-isolate (P s) (Q  f)}
    {h = equivFun $ chain-rule.η₀ .Equiv.shape}
    (isEmbedding-Σ-map-snd λ (s , f)  isEmbedding-Σ-isolate (P s) (Q  f))
    (isEquiv→isEmbedding $ equivIsEquiv $ chain-rule.η₀ .Equiv.shape)

  isEquivChainRule→isEquiv-Σ-isolated :
    isEquiv (chain-rule .shape)  (∀ s  (f : P s  T)  isEquiv (Σ-isolate (P s) (Q  f)))
  isEquivChainRule→isEquiv-Σ-isolated is-equiv-chain-shape-map = is-equiv-Σ-isolate where
    is-equiv-Σ-Σ-isolate : isEquiv (Σ-map-snd {A = Σ[ s  S ] (P s  T)}  (s , f)  Σ-isolate (P s) (Q  f)))
    is-equiv-Σ-Σ-isolate = isEquiv[f∘equivFunA≃B]→isEquiv[f]
      (Σ-map-snd _)
      chain-shape-equiv-left
      is-equiv-chain-shape-map

    is-equiv-Σ-isolate :  s f  isEquiv (Σ-isolate (P s) (Q  f))
    is-equiv-Σ-isolate = curry $ isEquiv-Σ-map-snd→isEquiv is-equiv-Σ-Σ-isolate

  isEquiv-Σ-isolated→isEquivChainRule :
    (∀ s  (f : P s  T)  isEquiv (Σ-isolate (P s) (Q  f)))
      
    isEquiv (chain-rule .shape)
  isEquiv-Σ-isolated→isEquivChainRule is-equiv-Σ-isolate = isEquiv-∘
    {f = equivFun $ chain-rule.η₀ .Equiv.shape}
    {g = Σ-map-snd λ ((s , f))  Σ-isolate (P s) (Q  f)}
    (isEquiv-Σ-map-snd $ uncurry is-equiv-Σ-isolate)
    (equivIsEquiv $ chain-rule.η₀ .Equiv.shape)

  isEquivChainRule≃isEquiv-Σ-isolated :
    isEquiv (chain-rule .shape)  (∀ s  (f : P s  T)  isEquiv (Σ-isolate (P s) (Q  f)))
  isEquivChainRule≃isEquiv-Σ-isolated = propBiimpl→Equiv
    (isPropIsEquiv _) (isPropΠ2 λ s f  isPropIsEquiv _)
    isEquivChainRule→isEquiv-Σ-isolated
    isEquiv-Σ-isolated→isEquivChainRule

DiscreteContainer : (ℓS ℓP : Level)  Type _
DiscreteContainer ℓS ℓP = Σ[ F  Container ℓS ℓP ]  s  Discrete (F .Pos s)

hasChainEquiv : ( : Level)  Type (ℓ-suc )
hasChainEquiv  = (F G : Container  )  isEquiv (chain-shape-map F G)

isPropHasChainEquiv : isProp (hasChainEquiv )
isPropHasChainEquiv = isPropΠ2 λ F G  isPropIsEquiv _

DiscreteContainer→isEquivChainMap : (F G : DiscreteContainer  )  isEquiv (chain-shape-map (F .fst) (G .fst))
DiscreteContainer→isEquivChainMap (F , disc-F) (G , disc-G) = isEquiv-Σ-isolated→isEquivChainRule F G is-equiv-Σ-isolate where
  open Container F renaming (Shape to S ; Pos to P)
  open Container G renaming (Shape to T ; Pos to Q)

  is-equiv-Σ-isolate :  s f  isEquiv (Σ-isolate (P s) (Q  f))
  is-equiv-Σ-isolate s f = Discrete→isEquiv-Σ-isolate (disc-F s) (disc-G  f)

isEquivChainMap→AllTypesDiscrete : hasChainEquiv   (A : Type )  Discrete A
isEquivChainMap→AllTypesDiscrete {} is-equiv-chain-shape-map A = discrete-A where
  F : Container  
  F .Shape = 𝟙*
  F .Pos _ = A

  G : (a₀ : A)  Container  
  G a₀ .Shape = A
  G a₀ .Pos = a₀ ≡_

  is-equiv-Σ-isolate-singl : (a₀ : A)  isEquiv (Σ-isolate A (a₀ ≡_))
  is-equiv-Σ-isolate-singl a₀ = isEquivChainRule→isEquiv-Σ-isolated F (G a₀)
    (is-equiv-chain-shape-map F (G a₀))
    
    (idfun A)

  discrete-A : Discrete A
  discrete-A = isEquiv-Σ-isolate-singl→Discrete is-equiv-Σ-isolate-singl

AllTypesDiscrete→isEquivChainMap : ((A : Type )  Discrete A)  hasChainEquiv 
AllTypesDiscrete→isEquivChainMap discrete F G = DiscreteContainer→isEquivChainMap (F , discrete  Pos F) (G , discrete  Pos G)

isEquivChainMap≃AllTypesDiscrete : hasChainEquiv   ((A : Type )  Discrete A)
isEquivChainMap≃AllTypesDiscrete = propBiimpl→Equiv isPropHasChainEquiv (isPropΠ λ A  isPropDiscrete)
  isEquivChainMap→AllTypesDiscrete
  AllTypesDiscrete→isEquivChainMap

¬hasChainEquiv : ¬ hasChainEquiv ℓ-zero
¬hasChainEquiv is-equiv-chain-shape-map = S1.¬isIsolated-base $ Discrete→isIsolated discrete-S¹ base where
  open import Cubical.HITs.S1.Base

  discrete-S¹ : Discrete 
  discrete-S¹ = isEquivChainMap→AllTypesDiscrete is-equiv-chain-shape-map 

isEquivChainMapSets→AllSetsDiscrete :
  ((F G : SetContainer  )  isEquiv (chain-rule (F .fst) (G .fst) .shape))
    
  ((A : hSet )  Discrete  A )
isEquivChainMapSets→AllSetsDiscrete {} is-equiv-chain-shape-map (A , is-set-A) = discrete-A where
  F : SetContainer  
  F .fst .Shape = 𝟙*
  F .fst .Pos _ = A
  F .snd .fst = isSet-𝟙*
  F .snd .snd _ = is-set-A

  G : (a₀ : A)  SetContainer  
  G a₀ .fst .Shape = A
  G a₀ .fst .Pos = a₀ ≡_
  G a₀ .snd .fst = is-set-A
  G a₀ .snd .snd a = isOfHLevelPath 1 (is-set-A a₀ a)

  is-equiv-Σ-isolate-singl : (a₀ : A)  isEquiv (Σ-isolate A (a₀ ≡_))
  is-equiv-Σ-isolate-singl a₀ = isEquivChainRule→isEquiv-Σ-isolated _ _
    (is-equiv-chain-shape-map F (G a₀))
    
    (idfun A)

  discrete-A : Discrete A
  discrete-A = isEquiv-Σ-isolate-singl→Discrete is-equiv-Σ-isolate-singl

AllSetsDiscrete→isEquivChainMapSets :
  ((A : hSet )  Discrete  A )
    
  ((F G : SetContainer  )  isEquiv (chain-rule (F .fst) (G .fst) .shape))
AllSetsDiscrete→isEquivChainMapSets discrete (F , is-set-F) (G , is-set-G) = DiscreteContainer→isEquivChainMap
  (F , disc-F)
  (G , disc-G)
  where
    disc-F :  s  Discrete (F .Pos s)
    disc-F s = discrete (F .Pos s , is-set-F .snd s)

    disc-G :  t  Discrete (G .Pos t)
    disc-G t = discrete (G .Pos t , is-set-G .snd t)

isEquivChainMapSets≃AllSetsDiscrete :
  ((F G : SetContainer  )  isEquiv (chain-rule (F .fst) (G .fst) .shape))
    
  ((A : hSet )  Discrete  A )
isEquivChainMapSets≃AllSetsDiscrete = propBiimpl→Equiv
  (isPropΠ2 λ F G  isPropIsEquiv _)
  (isPropΠ λ A  isPropDiscrete)
  isEquivChainMapSets→AllSetsDiscrete
  AllSetsDiscrete→isEquivChainMapSets

impredicativeProp→hasChainEquiv→LEM : ( : Level)
   (Ω : Type )
   (resize : Ω  hProp )
   hasChainEquiv 
   (P : hProp )  Dec  P 
impredicativeProp→hasChainEquiv→LEM  Ω resize has-chain-equiv = dec-prop where
  open import Cubical.Relation.Nullary.Properties using (EquivPresDiscrete)

  all-types-discrete : (A : Type )  Discrete A
  all-types-discrete = isEquivChainMap→AllTypesDiscrete has-chain-equiv

  Ω-discrete : Discrete Ω
  Ω-discrete = all-types-discrete Ω

  hProp-discrete : Discrete (hProp )
  hProp-discrete = EquivPresDiscrete resize Ω-discrete

  𝟙ᴾ : hProp 
  𝟙ᴾ .fst = 𝟙*
  𝟙ᴾ .snd _ _ = refl

  dec-equal-𝟙 : (P : hProp )  Dec (P  𝟙ᴾ)
  dec-equal-𝟙 P = hProp-discrete P 𝟙ᴾ

  dec-prop :  P  Dec  P 
  dec-prop P = Dec.map
     P≡𝟙  subst ⟨_⟩ (sym P≡𝟙) )
     P≢𝟙 p  P≢𝟙 $ Σ≡Prop  _  isPropIsProp) (isContr→≡𝟙* (inhProp→isContr p (str P))))
    (dec-equal-𝟙 P)