{-# OPTIONS --safe --lossy-unification #-}
module Cubical.Homotopy.Whitehead where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.GroupoidLaws

open import Cubical.Data.Nat
open import Cubical.Data.Sigma
open import Cubical.Data.Unit

open import Cubical.HITs.Susp renaming (toSusp to σ)
open import Cubical.HITs.Pushout
open import Cubical.HITs.Sn
open import Cubical.HITs.Join
open import Cubical.HITs.Wedge
open import Cubical.HITs.SetTruncation

open import Cubical.Homotopy.Group.Base

open Iso
open 3x3-span

joinTo⋁ :  { ℓ'} {A : Pointed } {B : Pointed ℓ'}
  join (typ A) (typ B)
  (Susp (typ A) , north)  (Susp (typ B) , north)
joinTo⋁ (inl x) = inr north
joinTo⋁ (inr x) = inl north
joinTo⋁ {A = A} {B = B} (push a b i) =
     ((λ i  inr (σ B b i))
  ∙∙ sym (push tt)
  ∙∙ λ i  inl (σ A a i)) i

-- Whitehead product (main definition)
[_∣_] :  {} {X : Pointed } {n m : }
        (S₊∙ (suc n) →∙ X)
        (S₊∙ (suc m) →∙ X)
        S₊∙ (suc (n + m)) →∙ X
fst ([_∣_] {X = X} {n = n} {m = m} f g) x =
  _∨→_ (f ∘∙ (inv (IsoSucSphereSusp n) , IsoSucSphereSusp∙ n))
        (g ∘∙ (inv (IsoSucSphereSusp m) , IsoSucSphereSusp∙ m))
        (joinTo⋁ {A = S₊∙ n} {B = S₊∙ m}
          (inv (IsoSphereJoin n m) x))
snd ([_∣_] {n = n} {m = m} f g) =
  cong (_∨→_ (f ∘∙ (inv (IsoSucSphereSusp n) , IsoSucSphereSusp∙ n))
       (g ∘∙ (inv (IsoSucSphereSusp m) , IsoSucSphereSusp∙ m)))
       (cong (joinTo⋁ {A = S₊∙ n} {B = S₊∙ m}) (IsoSphereJoin⁻Pres∙ n m))
     cong (fst g) (IsoSucSphereSusp∙ m)
     snd g

-- For Sⁿ, Sᵐ with n, m ≥ 2, we can avoid some bureaucracy. We make
-- a separate definition and prove it equivalent.
[_∣_]-pre :  {} {X : Pointed } {n m : }
        (S₊∙ (suc (suc n)) →∙ X)
        (S₊∙ (suc (suc m)) →∙ X)
        join (typ (S₊∙ (suc n))) (typ (S₊∙ (suc m)))  fst X
[_∣_]-pre {n = n} {m = m} f g x =
  _∨→_ f g
        (joinTo⋁ {A = S₊∙ (suc n)} {B = S₊∙ (suc m)}
        x)

[_∣_]₂ :  {} {X : Pointed } {n m : }
        (S₊∙ (suc (suc n)) →∙ X)
        (S₊∙ (suc (suc m)) →∙ X)
        S₊∙ (suc ((suc n) + (suc m))) →∙ X
fst ([_∣_]₂ {n = n} {m = m} f g) x =
  [ f  g ]-pre (inv (IsoSphereJoin (suc n) (suc m)) x)
snd ([_∣_]₂ {n = n} {m = m} f g) =
    cong ([ f  g ]-pre) (IsoSphereJoin⁻Pres∙ (suc n) (suc m))
   snd g

[]≡[]₂ :  {} {X : Pointed } {n m : }
        (f : (S₊∙ (suc (suc n)) →∙ X))
        (g : (S₊∙ (suc (suc m)) →∙ X))
        [ f  g ]  [ f  g ]₂
[]≡[]₂ {n = n} {m = m} f g =
  ΣPathP (
     i x  _∨→_ (∘∙-idˡ f i)
                    (∘∙-idˡ g i)
                (joinTo⋁ {A = S₊∙ (suc n)} {B = S₊∙ (suc m)}
                 (inv (IsoSphereJoin (suc n) (suc m)) x)))
  , (cong (cong (_∨→_ (f ∘∙ idfun∙ _)
                       (g ∘∙ idfun∙ _))
       (cong (joinTo⋁ {A = S₊∙ (suc n)} {B = S₊∙ (suc m)})
             (IsoSphereJoin⁻Pres∙ (suc n) (suc m))) ∙_)
                (sym (lUnit (snd g)))
   λ j   i  _∨→_ (∘∙-idˡ f j)
                        (∘∙-idˡ g j)
           ( joinTo⋁ {A = S₊∙ (suc n)} {B = S₊∙ (suc m)}
          ((IsoSphereJoin⁻Pres∙ (suc n) (suc m)) i)))  snd g))

-- Homotopy group version
[_∣_]π' :  {} {X : Pointed } {n m : }
        π' (suc n) X  π' (suc m) X
        π' (suc (n + m)) X
[_∣_]π' = elim2  _ _  squash₂) λ f g   [ f  g ] ∣₂

-- We prove that the function joinTo⋁ used in the definition of the whitehead
-- product gives an equivalence between (Susp A × Susp B) and the
-- appropriate cofibre of joinTo⋁ (last two theorems in the following
-- module).

module _ (A B : Type) (a₀ : A) (b₀ : B) where
  private
    W = joinTo⋁ {A = (A , a₀)} {B = (B , b₀)}

  A∨B = (Susp A , north)  (Susp B , north)

  σB = σ (B , b₀)
  σA = σ (A , a₀)

  cofibW = Pushout W λ _  tt

  whitehead3x3 :  3x3-span
  A00 whitehead3x3 = Susp A
  A02 whitehead3x3 = B
  A04 whitehead3x3 = Unit
  A20 whitehead3x3 = B
  A22 whitehead3x3 = A × B
  A24 whitehead3x3 = A
  A40 whitehead3x3 = B
  A42 whitehead3x3 = B
  A44 whitehead3x3 = Unit
  f10 whitehead3x3 _ = south
  f12 whitehead3x3 = snd
  f14 whitehead3x3 _ = tt
  f30 whitehead3x3 = idfun B
  f32 whitehead3x3 = snd
  f34 whitehead3x3 _ = tt
  f01 whitehead3x3 _ = north
  f21 whitehead3x3 = snd
  f41 whitehead3x3 = idfun B
  f03 whitehead3x3 _ = tt
  f23 whitehead3x3 = fst
  f43 whitehead3x3 _ = tt
  H11 whitehead3x3 x = merid (fst x)
  H13 whitehead3x3 _ = refl
  H31 whitehead3x3 _ = refl
  H33 whitehead3x3 _ = refl

  A0□→A∨B : A0□ whitehead3x3  A∨B
  A0□→A∨B (inl x) = inl x
  A0□→A∨B (inr x) = inr north
  A0□→A∨B (push a i) = (push tt  λ i  inr (σB a (~ i))) i

  A∨B→A0□ : A∨B  A0□ whitehead3x3
  A∨B→A0□ (inl x) = inl x
  A∨B→A0□ (inr north) = inl north
  A∨B→A0□ (inr south) = inl north
  A∨B→A0□ (inr (merid b i)) = (push b₀  sym (push b)) i
  A∨B→A0□ (push a i) = inl north

  Iso-A0□-⋁ : Iso (A0□ whitehead3x3) A∨B
  fun Iso-A0□-⋁ = A0□→A∨B
  inv Iso-A0□-⋁ = A∨B→A0□
  rightInv Iso-A0□-⋁ (inl x) = refl
  rightInv Iso-A0□-⋁ (inr north) = push tt
  rightInv Iso-A0□-⋁ (inr south) = push tt  λ i  inr (merid b₀ i)
  rightInv Iso-A0□-⋁ (inr (merid a i)) j = lem j i
    where
    lem : PathP  i  push tt i  (push tt   i  inr (merid b₀ i))) i)
              (cong A0□→A∨B (cong A∨B→A0□ λ i  inr (merid a i)))
               i  inr (merid a i))
    lem = (cong-∙ A0□→A∨B (push b₀) (sym (push a))
       cong₂ _∙_ (cong (push tt ∙_)
                   j i  inr (rCancel (merid b₀) j (~ i)))
                  sym (rUnit (push tt)))
                  (symDistr (push tt)  i  inr (σB a (~ i)))))
       λ i j  hcomp  k 
                  λ { (i = i0)  compPath-filler' (push tt)
                                 (compPath-filler  i  inr (σB a i))
                                                  (sym (push tt)) k) k j
                    ; (i = i1)  inr (merid a j)
                    ; (j = i0)  push tt (i  ~ k)
                    ; (j = i1)  compPath-filler' (push tt)
                                                   i  inr (merid b₀ i)) k i})
                        (inr (compPath-filler (merid a)
                                              (sym (merid b₀)) (~ i) j))

  rightInv Iso-A0□-⋁ (push a i) j = push tt (i  j)
  leftInv Iso-A0□-⋁ (inl x) = refl
  leftInv Iso-A0□-⋁ (inr tt) = push b₀
  leftInv Iso-A0□-⋁ (push b i) j = help j i
    where
    help : PathP  i  inl north  push b₀ i)
                 (cong A∨B→A0□ (cong A0□→A∨B (push b)))
                 (push b)
    help = (cong-∙ A∨B→A0□ (push tt)  i  inr (σB b (~ i)))
           i  lUnit (sym (cong-∙ (A∨B→A0□  inr)
                               (merid b) (sym (merid b₀)) i)) (~ i))
          cong sym (cong ((push b₀  sym (push b)) ∙_)
                      (cong sym (rCancel (push b₀))))
          cong sym (sym (rUnit (push b₀  sym (push b)))))
          λ i j  compPath-filler' (push b₀) (sym (push b)) (~ i) (~ j)

  Iso-A2□-join : Iso (A2□ whitehead3x3) (join A B)
  fun Iso-A2□-join (inl x) = inr x
  fun Iso-A2□-join (inr x) = inl x
  fun Iso-A2□-join (push (a , b) i) = push a b (~ i)
  inv Iso-A2□-join (inl x) = inr x
  inv Iso-A2□-join (inr x) = inl x
  inv Iso-A2□-join (push a b i) = push (a , b) (~ i)
  rightInv Iso-A2□-join (inl x) = refl
  rightInv Iso-A2□-join (inr x) = refl
  rightInv Iso-A2□-join (push a b i) = refl
  leftInv Iso-A2□-join (inl x) = refl
  leftInv Iso-A2□-join (inr x) = refl
  leftInv Iso-A2□-join (push a i) = refl

  isContrA4□ : isContr (A4□ whitehead3x3)
  fst isContrA4□ = inr tt
  snd isContrA4□ (inl x) = sym (push x)
  snd isContrA4□ (inr x) = refl
  snd isContrA4□ (push a i) j = push a (i  ~ j)

  A4□≃Unit : A4□ whitehead3x3  Unit
  A4□≃Unit = isContr→≃Unit isContrA4□

  Iso-A□0-Susp : Iso (A□0 whitehead3x3) (Susp A)
  fun Iso-A□0-Susp (inl x) = x
  fun Iso-A□0-Susp (inr x) = north
  fun Iso-A□0-Susp (push a i) = merid a₀ (~ i)
  inv Iso-A□0-Susp x = inl x
  rightInv Iso-A□0-Susp x = refl
  leftInv Iso-A□0-Susp (inl x) = refl
  leftInv Iso-A□0-Susp (inr x) =  i  inl (merid a₀ i))  push x
  leftInv Iso-A□0-Susp (push a i) j =
    hcomp  k  λ { (i = i0)  inl (merid a₀ (k  j))
                    ; (i = i1)  compPath-filler
                                    i₁  inl (merid a₀ i₁))
                                   (push (idfun B a)) k j
                    ; (j = i0)  inl (merid a₀ (~ i  k))
                    ; (j = i1)  push a (i  k)})
          (inl (merid a₀ j))

  Iso-A□2-Susp× : Iso (A□2 whitehead3x3) (Susp A × B)
  fun Iso-A□2-Susp× (inl x) = north , x
  fun Iso-A□2-Susp× (inr x) = south , x
  fun Iso-A□2-Susp× (push a i) = merid (fst a) i , (snd a)
  inv Iso-A□2-Susp× (north , y) = inl y
  inv Iso-A□2-Susp× (south , y) = inr y
  inv Iso-A□2-Susp× (merid a i , y) = push (a , y) i
  rightInv Iso-A□2-Susp× (north , snd₁) = refl
  rightInv Iso-A□2-Susp× (south , snd₁) = refl
  rightInv Iso-A□2-Susp× (merid a i , snd₁) = refl
  leftInv Iso-A□2-Susp× (inl x) = refl
  leftInv Iso-A□2-Susp× (inr x) = refl
  leftInv Iso-A□2-Susp× (push a i) = refl

  Iso-A□4-Susp : Iso (A□4 whitehead3x3) (Susp A)
  fun Iso-A□4-Susp (inl x) = north
  fun Iso-A□4-Susp (inr x) = south
  fun Iso-A□4-Susp (push a i) = merid a i
  inv Iso-A□4-Susp north = inl tt
  inv Iso-A□4-Susp south = inr tt
  inv Iso-A□4-Susp (merid a i) = push a i
  rightInv Iso-A□4-Susp north = refl
  rightInv Iso-A□4-Susp south = refl
  rightInv Iso-A□4-Susp (merid a i) = refl
  leftInv Iso-A□4-Susp (inl x) = refl
  leftInv Iso-A□4-Susp (inr x) = refl
  leftInv Iso-A□4-Susp (push a i) = refl

  Iso-PushSusp×-Susp×Susp :
    Iso (Pushout {A = Susp A × B} fst fst) (Susp A × Susp B)
  Iso-PushSusp×-Susp×Susp = theIso
    where
    F : Pushout {A = Susp A × B} fst fst
      Susp A × Susp B
    F (inl x) = x , north
    F (inr x) = x , north
    F (push (x , b) i) = x , σB b i

    G : Susp A × Susp B  Pushout {A = Susp A × B} fst fst
    G (a , north) = inl a
    G (a , south) = inr a
    G (a , merid b i) = push (a , b) i

    retr : retract F G
    retr (inl x) = refl
    retr (inr x) = push (x , b₀)
    retr (push (a , b) i) j = help j i
      where
      help : PathP  i  Path (Pushout fst fst) (inl a) (push (a , b₀) i))
                   (cong G  i  a , σB b i))
                   (push (a , b))
      help = cong-∙  x  G (a , x)) (merid b) (sym (merid b₀))
                   λ i j  compPath-filler
                               (push (a , b))
                               (sym (push (a , b₀)))
                               (~ i) j

    theIso : Iso (Pushout fst fst) (Susp A × Susp B)
    fun theIso = F
    inv theIso = G
    rightInv theIso (a , north) = refl
    rightInv theIso (a , south) = ΣPathP (refl , merid b₀)
    rightInv theIso (a , merid b i) j =
      a , compPath-filler (merid b) (sym (merid b₀)) (~ j) i
    leftInv theIso = retr

  Iso-A□○-PushSusp× :
    Iso (A□○ whitehead3x3) (Pushout {A = Susp A × B} fst fst)
  Iso-A□○-PushSusp× =
    pushoutIso _ _ fst fst
      (isoToEquiv Iso-A□2-Susp×)
      (isoToEquiv Iso-A□0-Susp)
      (isoToEquiv Iso-A□4-Susp)
      (funExt  { (inl x)  refl
                 ; (inr x)  merid a₀
                 ; (push a i) j  help₁ a j i}))
      (funExt λ { (inl x)  refl
                ; (inr x)  refl
                ; (push a i) j
                   fun Iso-A□4-Susp (rUnit (push (fst a)) (~ j) i)})
    where
    help₁ : (a : A × B)
       PathP  i  north  merid a₀ i)
               (cong (fun Iso-A□0-Susp)
                 (cong (f□1 whitehead3x3) (push a)))
               (merid (fst a))
    help₁ a =
        (cong-∙∙ (fun Iso-A□0-Susp)
                  i  inl (merid (fst a) i))
                 (push (snd a))
                 refl)
        i j  hcomp  k  λ {(i = i1)  merid (fst a) (j  ~ k)
                                 ; (j = i0)  merid (fst a) (~ k)
                                 ; (j = i1)  merid a₀ i})
                        (merid a₀ (i  ~ j)))

  Iso-A□○-Susp×Susp : Iso (A□○ whitehead3x3) (Susp A × Susp B)
  Iso-A□○-Susp×Susp = compIso Iso-A□○-PushSusp× Iso-PushSusp×-Susp×Susp

  Iso-A○□-cofibW : Iso (A○□ whitehead3x3) cofibW
  Iso-A○□-cofibW =
    pushoutIso _ _
      W  _  tt)
      (isoToEquiv Iso-A2□-join) (isoToEquiv Iso-A0□-⋁)
      A4□≃Unit
      (funExt lem)
      λ _ _  tt
    where
    lem : (x : A2□ whitehead3x3)
       A0□→A∨B (f1□ whitehead3x3 x)  W (fun Iso-A2□-join x)
    lem (inl x) =  i  inl (merid a₀ (~ i)))
    lem (inr x) = refl
    lem (push (a , b) i) j = help j i
      where
      help : PathP  i  Path (Pushout  _  north)  _  north))
                                ((inl (merid a₀ (~ i))))
                                (inr north))
                   (cong A0□→A∨B (cong (f1□ whitehead3x3) (push (a , b))))
                   (cong W (cong (fun Iso-A2□-join) (push (a , b))))
      help = (cong-∙∙ A0□→A∨B  i  inl (merid a (~ i))) (push b) refl
             λ j   i₂  inl (merid a (~ i₂)))
                   ∙∙ compPath-filler (push tt)  i  inr (σB b (~ i))) (~ j)
                   ∙∙ λ i  inr (σB b (~ i  j)))
             j   i  inl (sym (compPath-filler
                                       (merid a) (sym (merid a₀)) j) i))
                   ∙∙ push tt
                   ∙∙ λ i  inr (σB b (~ i)))

  Iso₁-Susp×Susp-cofibW : Iso (Susp A × Susp B) cofibW
  Iso₁-Susp×Susp-cofibW =
    compIso (invIso Iso-A□○-Susp×Susp)
            (compIso (3x3-Iso whitehead3x3) Iso-A○□-cofibW)

  -- Main iso
  Iso-Susp×Susp-cofibJoinTo⋁ : Iso (Susp A × Susp B) cofibW
  Iso-Susp×Susp-cofibJoinTo⋁ =
    compIso (Σ-cong-iso-snd  _  invSuspIso))
            Iso₁-Susp×Susp-cofibW

  -- The induced function A ∨ B → Susp A × Susp B satisfies
  -- the following identity
  Susp×Susp→cofibW≡ : Path (A∨B  Susp A × Susp B)
                      (Iso.inv Iso-Susp×Susp-cofibJoinTo⋁  inl)
                      ⋁↪
  Susp×Susp→cofibW≡ =
    funExt λ { (inl x)  ΣPathP (refl , (sym (merid b₀)))
             ; (inr north)  ΣPathP (refl , (sym (merid b₀)))
             ; (inr south)  refl
             ; (inr (merid a i)) j  lem₂ a j i
             ; (push a i) j  north , (merid b₀ (~ j))}
    where
    f₁ = fun Iso-PushSusp×-Susp×Susp
    f₂ = fun Iso-A□○-PushSusp×
    f₃ = backward-l whitehead3x3
    f₄ = fun (Σ-cong-iso-snd  _  invSuspIso))

    lem : (b : B)
       cong (f₁  f₂  f₃) (push b)
        i  north , σB b i)
    lem b = cong (cong f₁) (sym (rUnit (push (north , b))))

    lem₂ : (a : B)
       PathP  i  (north , merid b₀ (~ i))  (north , south))
               (cong (f₄  f₁  f₂  f₃  A∨B→A0□  inr) (merid a))
               λ i  north , merid a i
    lem₂ a =
         cong (cong f₄) (cong-∙ (f₁  f₂  f₃) (push b₀) (sym (push a))
      ∙∙ cong₂ _∙_ (lem b₀   j i  north , rCancel (merid b₀) j i))
                   (cong sym (lem a))
      ∙∙ sym (lUnit  i₁  north , σB a (~ i₁))))
         i j  north , cong-∙ invSusp (merid a) (sym (merid b₀)) i (~ j) )
         λ i j  north , compPath-filler (sym (merid a)) (merid b₀) (~ i) (~ j)