{-# OPTIONS --without-K #-}

open import lib.Basics
open import lib.NType2
open import lib.PathGroupoid

open import lib.types.Bool
open import lib.types.IteratedSuspension
open import lib.types.Lift
open import lib.types.LoopSpace
open import lib.types.Nat
open import lib.types.Paths
open import lib.types.Pi
open import lib.types.Pointed
open import lib.types.Sigma
open import lib.types.Suspension
open import lib.types.TLevel
open import lib.types.Unit

open SuspensionRec public using () renaming (f to Susp-rec)

open import nicolai.pseudotruncations.Preliminary-definitions
open import nicolai.pseudotruncations.Liblemmas
open import nicolai.pseudotruncations.pointed-O-Sphere


module nicolai.pseudotruncations.LoopsAndSpheres where

{- We greatly benefit from Evan Cavallo's code - thank you! -}
open import homotopy.PtdAdjoint
open import homotopy.SuspAdjointLoop




isNull :  {i j} {A : Type i} {B : Type j} (b : B) (f : A  B)  Type _
isNull {A = A} b f = (a : A)  f a == b


module null {i} {j} { : Ptd i} { : Ptd j} ( :  →̇ ) where

  A = fst 
  a₀ = snd  
  B = fst 
  b₀ = snd 
  g = fst 
  p = snd  

  -- derived isNull
  isNulld = (a : fst )  g a == b₀ 

  -- pointed isNull; we state it in the equivalence form (slightly easier to handle)
  isNull∙' = Σ ((a : A)  g a == b₀) λ pr  pr a₀ == p

  -- the 'real' pointed isNull
  isNull∙ =  == ((λ _  b₀) , idp)

  {- The two versions are equivalent -}
  isNull-equiv : isNull∙  isNull∙'
  isNull-equiv =
                == ((λ _  b₀) , idp)
                 ≃⟨ (=Σ-eqv _ _) ⁻¹ 
                 ((λ _  b₀) , idp)
                 ≃⟨ equiv-Σ' {A₀ = g == λ _  b₀}
                             app=-equiv
                              h  (p == idp [  f  f a₀ == b₀)  h ])
                                      ≃⟨ to-transp-equiv _ _ 
                                    (transport  f  f a₀ == b₀) h p) == idp
                                      ≃⟨ coe-equiv
                                           (ap  x  x == idp)
                                               (trans-ap₁  f  f a₀)
                                                          b₀ h p)) 
                                    (! (app= h a₀)  p) == idp
                                      ≃⟨ adhoc-=-eqv (app= h a₀) p 
                                    (app= h a₀ == p)
                                      ≃∎)  
               (Σ ((a : A)  g a == b₀) λ pr  pr a₀ == p)
                 ≃∎ 

-- Lemma 4.4: pointed and non-pointed 'nullness' are logically equivalent;
-- First, one direction:
  null-lequiv : isNulld  isNull∙'
  null-lequiv isnull =  a  isnull a  ! (isnull a₀)  p) , (
                 isnull a₀  ! (isnull a₀)  p
                   =⟨ ! (∙-assoc (isnull a₀) _ _) 
                 (isnull a₀  ! (isnull a₀))  p
                   =⟨ ap  t  t  p) (!-inv-r (isnull a₀)) 
                 p
                   )

-- The other direction is very easy; we do it using the non-prime variant:
  null-lequiv-easy : isNull∙  isNulld
  null-lequiv-easy isn = app= (ap fst isn)





-- uncomment this if you want to wait forever for typechecking...
-- Σ⊣Ω-unitCounit : CounitUnitAdjoint Σ⊣Ω.SuspFunctor Σ⊣Ω.LoopFunctor
Σ⊣Ω-unitCounit = Σ⊣Ω.adj

Σ⊣Ω-homset :  {i}  HomAdjoint {i} {i} Σ⊣Ω.SuspFunctor Σ⊣Ω.LoopFunctor
Σ⊣Ω-homset = counit-unit-to-hom Σ⊣Ω-unitCounit

module hom-adjoint {i} ( : Ptd i) ( : Ptd i) where

  A = fst 
  B = fst 
  a₀ = snd 
  b₀ = snd 

  Φeq : (⊙Susp  →̇ )  ( →̇ ⊙Ω )
  Φeq = HomAdjoint.eq Σ⊣Ω-homset    

  {- This is Lemma 4.1 -}
  Φ : (⊙Susp  →̇ )  ( →̇ ⊙Ω )
  Φ = –> Φeq  

  Φ⁻¹ : ( →̇ ⊙Ω )  (⊙Susp  →̇ )
  Φ⁻¹ = <– Φeq
  

  open PtdFunctor
  open Σ⊣Ω
  open CounitUnitAdjoint

  {- Some lemmas which are easy on paper and thus not explicitly 
     mentioned in the paper. It still takes some effort to
     formalize them. -}
  module simplify where

    simpl-⊙ap : (⊙ap {X = obj SuspFunctor } ((λ _  b₀) , idp))
                 ==
                ((λ _  idp) , idp)
    simpl-⊙ap =  →̇-maps-to
                   ⊙ap ((λ _  b₀) , idp)
                   ((λ _  idp) , idp)
                   (λ=  _  ap-cst b₀ _))

                   ((app= (λ=  _  ap-cst b₀ _)) _)  idp
                     =⟨ ∙-unit-r _ 
                   app= (λ=  _  ap-cst b₀ _)) _
                     =⟨ app=-β _ _ 
                   ap-cst b₀ (idp {a = snd }) 
                     =⟨ idp  --  !
                   idp
                     =⟨ idp  --  ! 
                   snd (⊙ap {X = obj SuspFunctor } ((λ _  b₀) , idp))
                      )

    simpl-comp : ((λ (_ : Ω (⊙Susp ))  idp {a = b₀}) , idp)
                   ⊙∘ (⊙η )
                  ==
                  _  idp) , idp
    simpl-comp = pair= idp ((ap-cst idp (snd (⊙η ))) ∙ᵣ idp)


  open simplify

  {- Lemma 4.2 -}
  Φ-is-pointed-map : Φ ((λ _  b₀) , idp) == ((λ _  idp) , idp)
  Φ-is-pointed-map = Φ ((λ _  b₀) , idp)
                       =⟨ idp 
                     (    arr LoopFunctor ((λ _  b₀) , idp)
                      ⊙∘ (CounitUnitAdjoint.η adj ))
                       =⟨ idp  
                     (    (⊙ap {X = obj SuspFunctor } ((λ _  b₀) , idp)
                      ⊙∘ (⊙η )))
                       =⟨ ap  f  f ⊙∘ (⊙η )) simpl-⊙ap 
                     ((λ _  idp) , idp) ⊙∘ (⊙η )
                       =⟨ simpl-comp  
                      _  idp) , idp
                        

-- fix i
module _ {i} where

  open hom-adjoint

  open HomAdjoint
  open null

  -- Lemma 4.3
  Φ-snd-nat : {   : Ptd i} (f : ⊙Susp  →̇ ) (g :  →̇ )
               Φ   (g ⊙∘ f) == ⊙ap g ⊙∘ Φ   f
  Φ-snd-nat {} {} {} f g = ! (nat-cod Σ⊣Ω-homset  {} {} g f)

  -- Lemma 4.4 is above (before 4.2).

  -- Lemma 4.5
  isnull-Φ : {  : Ptd i} (g : ⊙Susp  →̇ )  (isNull∙ g)  isNull∙ (Φ   g)
  isnull-Φ {} {} g =
    isNull∙ g
      ≃⟨ equiv-ap (Φeq  ) _ _ 
    (Φ   g) == Φ   ((λ _  snd ) , idp)
      ≃⟨ coe-equiv
           {A = (Φ   g) == Φ   ((λ _  snd ) , idp)}
           {B = (Φ   g) ==  _  idp) , idp}
           (ap  q  (Φ   g == q)) (Φ-is-pointed-map _ _ )) 
    (Φ   g) ==  _  idp) , idp
      ≃∎ 


  -- combination of 4.3 and 4.5
  combine-isnull-nat : {   : Ptd i} (f : ⊙Susp  →̇ ) (g :  →̇ )
                (isNull∙ (g ⊙∘ f))  (isNull∙ (⊙ap g ⊙∘ Φ   f)) --   
  combine-isnull-nat {} {} {} f g = 
    isNull∙ (g ⊙∘ f)
      ≃⟨ isnull-Φ _ 
    isNull∙ (Φ   (g ⊙∘ f))
      ≃⟨ coe-equiv (ap  q  isNull∙ q) (Φ-snd-nat f g)) 
    isNull∙ (⊙ap g ⊙∘ Φ   f)
      ≃∎

  combine-isnull-nat' : {   : Ptd i} (f :  →̇ ⊙Ω ) (g :  →̇ )
                (isNull∙ (g ⊙∘ (Φ⁻¹   f)))  (isNull∙ (⊙ap g ⊙∘ f))
  combine-isnull-nat' {} {} {} f g = 
   isNull∙ (g ⊙∘ (Φ⁻¹   f))
      ≃⟨ combine-isnull-nat (Φ⁻¹   f) g 
    isNull∙ (⊙ap g ⊙∘ (Φ   (Φ⁻¹   f)))
      ≃⟨ coe-equiv (ap  h  isNull∙ (⊙ap g ⊙∘ h)) (<–-inv-r (Φeq  ) f)) 
    isNull∙ (⊙ap g ⊙∘ f)
      ≃∎ 
    
  


module _ {i} where

  open hom-adjoint

  -- This was tricky (todo: could explain why)
  Φ-iter : (  : Ptd i) (n : Nat)
            ((⊙Susp-iter' n ) →̇ )
            ( →̇ (⊙Ω^ n ))
  Φ-iter   O f = f
  Φ-iter   (S n) f = Φ  (⊙Ω^ n ) (Φ-iter (⊙Susp )  n f)

  Φ-iter-equiv : (  : Ptd i) (n : Nat)  is-equiv (Φ-iter   n)
  Φ-iter-equiv   O = snd (ide _)
  Φ-iter-equiv   (S n) =
    snd ((Φeq  (⊙Ω^ n )) ∘e ((Φ-iter (⊙Susp )  n) , Φ-iter-equiv (⊙Susp )  n) )



module _ {i} where

  open null
  open hom-adjoint
  
  -- Lemma 4.7 -- generalized, because we need to do it for Susp first before it works for Sphere!
  isNull-Φ-many : (m : Nat)
                   (   : Ptd i)
                   (f : ⊙Susp-iter' m  →̇ ) (g :  →̇ )
                   isNull∙ (g ⊙∘ f)
                    
                    isNull∙ ((ap^ m g) ⊙∘ Φ-iter   m f)
  isNull-Φ-many O    f g = ide _
  isNull-Φ-many (S m)    f g = 
                    isNull∙ (g ⊙∘ f)
                      ≃⟨ isNull-Φ-many m (⊙Susp )   f g   
                    isNull∙ ((ap^ m g) ⊙∘ Φ-iter (⊙Susp )  m f)
                      ≃⟨ combine-isnull-nat (Φ-iter (⊙Susp )  m f) (ap^ m g) 
                    (isNull∙
                      (⊙ap (ap^ m g) ⊙∘
                        Φ  (⊙Ω^ m )
                          (Φ-iter (⊙Susp )  m f)))
                      ≃∎

  -- Lemma 4.7 (special with k = 0)
  module _ {  : Ptd i} (m : Nat)
           (f : ⊙Sphere' {i} m →̇ ) (g :  →̇ ) where

    isNull-Φ-Sphere : isNull∙ (g ⊙∘ f)
                      
                      isNull∙ ((ap^ m g) ⊙∘ Φ-iter (⊙Sphere' {i} O)  m f)
    isNull-Φ-Sphere = isNull-Φ-many m _ _ _ f g


  open bool-neutral
  
  module _ {  : Ptd i} (m : Nat)
           (g :  →̇ ) where

    c₀ = snd (⊙Ω^ m )


    {- Lemma 4.8 -}
    null-on-pspaces :
      ((f : (⊙Sphere' {i} m) →̇ )  isNull∙ (g ⊙∘ f))
      
      isNulld (ap^ m g)
      
    null-on-pspaces = -- {!equiv-Π-l!}

      ((f : (⊙Sphere' {i} m) →̇ )  isNull∙ (g ⊙∘ f))

        ≃⟨ equiv-Π-r  f  isNull-Φ-Sphere m f g) 

      ((f : (⊙Sphere' {i} m) →̇ )  isNull∙ ((ap^ m g) ⊙∘ Φ-iter (⊙Sphere' {i} O)  m f))

        ≃⟨ equiv-Π-l
             {A = (⊙Sphere' {i} m) →̇ }
             {B = (⊙Sphere' {i} O) →̇ (⊙Ω^ m )}
              f'  isNull∙ ((ap^ m g) ⊙∘ f'))
             {h = Φ-iter (⊙Sphere' {i} O)  m}
             (Φ-iter-equiv _ _ m)
              

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m ))  isNull∙ ((ap^ m g) ⊙∘ f'))

        ≃⟨ equiv-Π-r {A = ⊙Sphere' {i} O →̇ (⊙Ω^ m )}  _  isNull-equiv _) 

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m ))  isNull∙' ((ap^ m g) ⊙∘ f'))
      
        ≃⟨ ide _  

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m ))  Σ ((x : bool)  fst ((ap^ m g) ⊙∘ f') x == _) λ h  h tt₀ == _)
      
        ≃⟨ equiv-Π-r {A = ⊙Sphere' {i} O →̇ (⊙Ω^ m )}
                      fp  reduction  b  fst (ap^ m g ⊙∘ fp) b == null.b₀ (ap^ m g ⊙∘ fp)) _)  

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m ))  fst ((ap^ m g) ⊙∘ f') ff₀ == _)

        ≃⟨ ide _  

      ((f' : (⊙Sphere' {i} O) →̇ (⊙Ω^ m ))  fst (ap^ m g) (fst f' ff₀) == _)

        ≃⟨ equiv-Π-l {A = (⊙Sphere' {i} O) →̇ (⊙Ω^ m )}
                     {B = fst (⊙Ω^ m )}
                     _
                     (snd (reduction  _  fst (⊙Ω^ m )) _))  

      ((x : fst (⊙Ω^ m ))  fst (ap^ m g) x == c₀)

        ≃⟨ ide _  

      isNulld (ap^ m g)
        ≃∎