{-# OPTIONS --safe --lossy-unification #-}

module Cubical.Homotopy.EilenbergMacLane.WedgeConnectivity where

open import Cubical.Homotopy.EilenbergMacLane.Base
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels

open import Cubical.Algebra.Group.Base
open import Cubical.HITs.Truncation as Trunc renaming (rec to trRec; elim to trElim)
open import Cubical.HITs.EilenbergMacLane1
open import Cubical.Algebra.AbGroup.Base
open import Cubical.Data.Empty
  renaming (rec to ⊥-rec)
open import Cubical.Data.Nat
open import Cubical.HITs.Susp

{-
This file contains a direct construction of the wedge connectivity lemma for EM
spaces. This direct construction gives nicer reductions and computes better than
the more general theorem. The main results are in the module "wedgeConEM" at the
end of this file.
-}

private
  variable  ℓ' ℓ'' : Level

-- One of the base cases, stated separately to avoid termination issues
  wedgeConFun' : (G : AbGroup ) (H : AbGroup ℓ') (n : )
               {A : EM-raw G (suc n)  EM-raw H (suc zero)  Type ℓ''}
               ((x : _) (y : _)  isOfHLevel (suc n + suc zero) (A x y))
               (f : (x : _)  A ptEM-raw x)
               (g : (x : _)  A x ptEM-raw)
               f ptEM-raw  g ptEM-raw
               (x : _) (y : _)  A x y
  wedgeConFun'ᵣ : (G : AbGroup ) (H : AbGroup ℓ') (n : )
               {A : EM-raw G (suc n)  EM-raw H (suc zero)  Type ℓ''}
               (hLev : ((x : _) (y : _)  isOfHLevel (suc n + suc zero) (A x y)))
               (f : (x : _)  A ptEM-raw x)
               (g : (x : _)  A x ptEM-raw)
               (p : f ptEM-raw  g ptEM-raw)
               (x : _)  wedgeConFun' G H n hLev f g p x ptEM-raw  g x
  wedgeConFun' G H zero {A = A} hlev f g p =
    elimSet _  _  isSetΠ λ _  hlev _ _) f mainpath
    where
    I→A : (x : fst G)  (i : I)  A (emloop x i) embase
    I→A x i =
      hcomp  k  λ {(i = i0)  p (~ k) ; (i = i1)  p (~ k)})
            (g (emloop x i))

    SquareP2 : (x : _) (z : _)
       SquareP  i j  A (emloop x i) (emloop z j))
          (cong f (emloop z)) (cong f (emloop z))
           i  I→A x i) λ i  I→A x i
    SquareP2 x z =
      toPathP (isOfHLevelPathP' 1  _ _  hlev _ _ _ _) _ _ _ _)

    CubeP2 : (x : _) (g h : _)
       PathP  k  PathP  j  PathP  i  A (emloop x i) (emcomp g h j k))
              (f (emcomp g h j k)) (f (emcomp g h j k)))
               i  SquareP2 x g i k) λ i  SquareP2 x ((snd (AbGroup→Group H) GroupStr.· g) h) i k)
               _ i  I→A x i) λ j i  SquareP2 x h i j
    CubeP2 x g h = toPathP (isOfHLevelPathP' 1 (isOfHLevelPathP 2 (hlev _ _) _ _) _ _ _ _)

    mainpath : (x : _)  PathP  i  (y : EM₁ (AbGroup→Group H))  A (emloop x i) y) f f
    mainpath x i embase = I→A x i
    mainpath x i (emloop z j) = SquareP2 x z i j
    mainpath x i (emcomp g h j k) = CubeP2 x g h k j i
    mainpath x i (emsquash y z p q r s j k' l) = mega i j k' l
      where
      mega :
        PathP  i  PathP  j  PathP  k 
          PathP  l  A (emloop x i) (emsquash y z p q r s j k l))
                    (mainpath x i y)
                    (mainpath x i z))
                     l  mainpath x i (p l))
                     λ l  mainpath x i (q l))
                     k l  mainpath x i (r k l))
                     λ k l  mainpath x i (s k l))
                     j mainpath l  f (emsquash y z p q r s j mainpath l))
                     λ j mainpath l  f (emsquash y z p q r s j mainpath l)
      mega =
        toPathP (isOfHLevelPathP' 1
          (isOfHLevelPathP 2 (isOfHLevelPathP 2 (hlev _ _) _ _) _ _) _ _ _ _)
  wedgeConFun' G H (suc n) {A = A} hLev f g p north y = f y
  wedgeConFun' G H (suc n) {A = A} hLev f g p south y = subst  x  A x y) (merid ptEM-raw) (f y)
  wedgeConFun' G H (suc n) {A = A} hLev f g p (merid a i) y = mainₗ a y i
    module _ where
    llem₁ : g south  subst  x₁  A x₁ ptEM-raw) (merid ptEM-raw) (f ptEM-raw)
    llem₁ =  i  transp  j  A (merid ptEM-raw (j  ~ i)) ptEM-raw) (~ i) (g (merid ptEM-raw (~ i))))
           cong (subst  x₁  A x₁ ptEM-raw) (merid ptEM-raw)) (sym p)

    llem₁' :
      Square
        i  transp  j  A (merid ptEM-raw (i  j)) ptEM-raw) i (g (merid ptEM-raw i))) refl
       (cong (subst  x  A x ptEM-raw) (merid ptEM-raw)) (sym p)) llem₁
    llem₁' i j =
      hcomp  k  λ { (i = i0)  transp  z  A (merid ptEM-raw (j  z)) ptEM-raw) j
                                            (g (merid ptEM-raw j))
                      ; (i = i1)  subst  x₁  A x₁ ptEM-raw) (merid ptEM-raw) (p (~ k))
                      ; (j = i0)  (subst  x  A x ptEM-raw) (merid ptEM-raw)) (p (~ k  ~ i))})
            (transp  k  A (merid ptEM-raw (k  ~ i  j)) ptEM-raw) (~ i  j) (g (merid ptEM-raw (~ i  j))))

    llem₂ :  i₁  transp  j  A (merid ptEM-raw (i₁  j)) ptEM-raw) (~ i₁) (f ptEM-raw))
          i₁   hcomp  k  λ { (i₁ = i0)  p (~ k) ; (i₁ = i1)  llem₁ k })
                          (g (merid ptEM-raw i₁)))
    llem₂ i j =
      hcomp  k  λ { (i = i0)  transp  j₁  A (merid ptEM-raw (j  j₁)) ptEM-raw) (~ j) (p (~ k))
                      ; (j = i0)  p (~ k)
                      ; (j = i1)  llem₁' k i})
        (transp  k  A (merid ptEM-raw ((i  k)  j)) ptEM-raw) (i  ~ j) (g (merid ptEM-raw (i  j))))

    mainₗ : (a : _) (y : _)
       PathP  i  A (merid a i) y) (f y) (subst  x  A x y) (merid ptEM-raw) (f y))
    mainₗ =
      wedgeConFun' G H n
         _ _  isOfHLevelPathP' (suc (n + 1)) (hLev _ _) _ _)
         x i  transp  j  A (merid ptEM-raw (i  j)) x) (~ i) (f x))
         x i  hcomp  k  λ { (i = i0)  p (~ k)
                                 ; (i = i1)  llem₁ k})
                        (g (merid x i)))
        llem₂

    mainP : (y : _)
       mainₗ y ptEM-raw
       λ i  hcomp  k  λ { (i = i0)  p (~ k)
                               ; (i = i1)  llem₁ k})
                      (g (merid y i))
    mainP y =
      wedgeConFun'ᵣ G H n
         _ _  isOfHLevelPathP' (suc (n + 1)) (hLev _ _) _ _)
         x i  transp  j  A (merid ptEM-raw (i  j)) x) (~ i) (f x))
         x i  hcomp  k  λ { (i = i0)  p (~ k)
                                 ; (i = i1)  llem₁ k})
                        (g (merid x i)))
        llem₂ y
  wedgeConFun'ᵣ G H zero {A = A} hLev f g p =
    elimProp _  _  hLev _ _ _ _) p
  wedgeConFun'ᵣ G H (suc n) {A = A} hLev f g p north = p
  wedgeConFun'ᵣ G H (suc n) {A = A} hLev f g p south = sym (llem₁ G H n hLev f g p ptEM-raw i0 ptEM-raw)
  wedgeConFun'ᵣ G H (suc n) {A = A} hLev f g p (merid a i) k = P k i
    where
    llem : _
    llem i j =
      hcomp  k  λ { (i = i1)  g (merid a j)
                      ; (j = i0)  p (i  ~ k)
                      ; (j = i1)  llem₁ G H n hLev f g p ptEM-raw i0 ptEM-raw (~ i  k)})
        (g (merid a j))

    P : PathP  k  PathP  i  A (merid a i) ptEM-raw)
              (p k) (llem₁ G H n hLev f g p ptEM-raw i0 ptEM-raw (~ k)))
               i  mainₗ G H n hLev f g p a i ptEM-raw a ptEM-raw i) λ i  g (merid a i)
    P = mainP G H n hLev f g p a i0 ptEM-raw a  llem

-- Here, the actual stuff gets proved. However an additional natural number is stuck into the context
-- to convince the termination checker
private
  wedgeConFun : (G : AbGroup ) (H : AbGroup ℓ')
                 (k n m : )  (n + m  k)  {A : EM-raw G (suc n)  EM-raw H (suc m)  Type ℓ''}
               ((x : _) (y : _)  isOfHLevel (suc n + suc m) (A x y))
               (f : (x : _)  A ptEM-raw x)
               (g : (x : _)  A x ptEM-raw)
               f ptEM-raw  g ptEM-raw
               (x : _) (y : _)  A x y
  wedgeconLeft : (G : AbGroup ) (H : AbGroup ℓ') (k n m : ) (P : n + m  k)
                 {A : EM-raw G (suc n)  EM-raw H (suc m)  Type ℓ''}
               (hLev : ((x : _) (y : _)  isOfHLevel (suc n + suc m) (A x y)))
               (f : (x : _)  A ptEM-raw x)
               (g : (x : _)  A x ptEM-raw)
               (p : f ptEM-raw  g ptEM-raw)
                (x : _)  wedgeConFun G H k n m P hLev f g p ptEM-raw x  f x
  wedgeconRight : (G : AbGroup ) (H : AbGroup ℓ')
                 (k n m : ) (P : n + m  k) {A : EM-raw G (suc n)  EM-raw H (suc m)  Type ℓ''}
               (hLev : ((x : _) (y : _)  isOfHLevel (suc n + suc m) (A x y)))
               (f : (x : _)  A ptEM-raw x)
               (g : (x : _)  A x ptEM-raw)
               (p : f ptEM-raw  g ptEM-raw)
                (x : _)  wedgeConFun G H k n m P hLev f g p x ptEM-raw  g x
  wedgeConFun G H k n zero P {A = A} hLev f g p = wedgeConFun' G H n hLev f g p
  wedgeConFun G H k zero (suc m) P {A = A} hLev f g p x y =
    wedgeConFun' H G (suc m) {A = λ x y  A y x}
       x y  subst  n  isOfHLevel (2 + n) (A y x)) (+-comm 1 m) (hLev y x))
      g f (sym p) y x
  wedgeConFun G H l (suc n) (suc m) P {A = A} hlev f g p north y = f y
  wedgeConFun G H l (suc n) (suc m) P {A = A} hlev f g p south y = subst  x  A x y) (merid ptEM-raw) (f y)
  wedgeConFun G H zero (suc n) (suc m) P {A = A} hlev f g p (merid a i) y = ⊥-path i
    where
    ⊥-path : PathP  i  A (merid a i) y) (f y) (subst  x  A x y) (merid ptEM-raw) (f y))
    ⊥-path = ⊥-rec (snotz P)
  wedgeConFun G H (suc l) (suc n) (suc m) P {A = A} hlev f g p (merid a i) y = mmain a y i
    module _ where
    llem₃ : g south  (subst  x  A x ptEM-raw) (merid ptEM-raw) (f ptEM-raw))
    llem₃ = ((λ i  transp  j  A (merid ptEM-raw (~ i  j)) ptEM-raw) (~ i) (g (merid ptEM-raw (~ i)))))
           cong (subst  x  A x ptEM-raw) (merid ptEM-raw)) (sym p)

    llem₃' :
      Square
         i  transp  j  A (merid ptEM-raw (~ i  j)) ptEM-raw) (~ i) (g (merid ptEM-raw (~ i))))
        (refl {x = subst  x  A x ptEM-raw) (merid ptEM-raw) (f ptEM-raw)})
        llem₃
        ((cong (transport  z  A (merid ptEM-raw z) ptEM-raw)) (sym p)))
    llem₃' i j =
      hcomp  k  λ { (i = i0)  transp  j₁  A (merid ptEM-raw (~ j  j₁)) ptEM-raw) (~ j) (g (merid ptEM-raw (~ j)))
                      ; (i = i1)  subst  x  A x ptEM-raw) (merid ptEM-raw) (p (~ k))
                      ; (j = i1)  cong (transport  z  A (merid ptEM-raw z) ptEM-raw)) (sym p) (i  k)})
             (transp  j₁  A (merid ptEM-raw ((~ j  ~ i)  j₁)) ptEM-raw) (~ j  ~ i) (g (merid ptEM-raw (~ j  ~ i))))


    llem₄ :  i₁  transp  j  A (merid ptEM-raw (j  i₁)) ptEM-raw) (~ i₁) (f ptEM-raw))
          i₁  hcomp  k  λ { (i₁ = i0)  p (~ k) ; (i₁ = i1)  llem₃ k })
           (g (merid ptEM-raw i₁)))
    llem₄ i j =
      hcomp  k  λ { (i = i0)  transp  z  A (merid ptEM-raw (z  j)) ptEM-raw) (~ j) (p (~ k))
                      ; (j = i0)  p (~ k)
                      ; (j = i1)  llem₃' k (~ i)})
            (transp  z  A (merid ptEM-raw ((i  z)  j)) ptEM-raw) (i  ~ j) (g (merid ptEM-raw (i  j))))

    mmain : (a : _) (y : _)
           PathP  i  A (merid a i) y) (f y)
                   (subst  x  A x y) (merid ptEM-raw) (f y))
    mmain =
      wedgeConFun G H l n (suc m) (cong predℕ P)
         _ _  isOfHLevelPathP' (suc (n + (suc (suc m)))) (hlev _ _) _ _)
         x i  transp  j  A (merid ptEM-raw (j  i)) x) (~ i) (f x))
         y i  hcomp  k  λ { (i = i0)  p (~ k)
                                  ; (i = i1)  llem₃ k})
                       (g (merid y i)))
        llem₄

    mainR : (x : _)  mmain x ptEM-raw
                     λ i  hcomp  k  λ { (i = i0)  p (~ k)
                                             ; (i = i1)  llem₃ k})
                                               (g (merid x i))
    mainR x =
      wedgeconRight G H l n (suc m) (cong predℕ P)
         _ _  isOfHLevelPathP' (suc (n + (suc (suc m)))) (hlev _ _) _ _)
         x i  transp  j  A (merid ptEM-raw (j  i)) x) (~ i) (f x))
         y i  hcomp  k  λ { (i = i0)  p (~ k)
                                  ; (i = i1)  llem₃ k})
                       (g (merid y i)))
        llem₄ x
  wedgeconLeft G H k zero zero P {A = A} hLev f g p _ = refl
  wedgeconLeft G H k (suc n) zero P {A = A} hLev f g p _ = refl
  wedgeconLeft G H k zero (suc m) P {A = A} hLev f g p x =
    wedgeConFun'ᵣ H G (suc m)
         x₁ y 
           subst  n  (x₂ y₁ : A y x₁)  isOfHLevel (suc n) (x₂  y₁))
           (+-comm 1 m) (hLev y x₁))
        g f  i  p (~ i)) x
  wedgeconLeft G H k (suc n) (suc m) P {A = A} hLev f g p _ = refl
  wedgeconRight G H k n zero P {A = A} hLev f g p = wedgeConFun'ᵣ G H n hLev f g p
  wedgeconRight G H k zero (suc m) P {A = A} hLev f g p _ = refl
  wedgeconRight G H zero (suc n) (suc m) P {A = A} hLev f g p x = ⊥-rec (snotz P)
  wedgeconRight G H l (suc n) (suc m) P {A = A} hLev f g p north = p
  wedgeconRight G H l (suc n) (suc m) P {A = A} hLev f g p south =
    sym (llem₃ G H _ n m refl hLev f g p ptEM-raw i0 ptEM-raw)
  wedgeconRight G H (suc l) (suc n) (suc m) P {A = A} hLev f g p (merid a i) k =
    help k i
    where
    llem : _
    llem i j =
      hcomp  k  λ { (i = i1)  g (merid a j)
                      ; (j = i0)  p (i  ~ k)
                      ; (j = i1)  llem₃ G H l n m P hLev f g p ptEM-raw i0 ptEM-raw (~ i  k)})
            (g (merid a j))

    help : PathP  k  PathP  i  A (merid a i) ptEM-raw)
                 (p k) (llem₃ G H l n m P hLev f g p ptEM-raw i0 ptEM-raw (~ k)))
                  i  mmain G H l n m P hLev f g p a i north a north i) (cong g (merid a))
    help = mainR G H l n m P hLev f g p a i0 ptEM-raw a  llem

module wedgeConEM (G : AbGroup ) (H : AbGroup ℓ') (n m : )
                  {A : EM-raw G (suc n)  EM-raw H (suc m)  Type ℓ''}
                  (hLev : ((x : _) (y : _)  isOfHLevel (suc n + suc m) (A x y)))
                  (f : (x : _)  A ptEM-raw x)
                  (g : (x : _)  A x ptEM-raw)
                  (p : f ptEM-raw  g ptEM-raw) where
  fun : (x : EM-raw G (suc n)) (y : EM-raw H (suc m))  A x y
  fun = wedgeConFun G H (n + m) n m refl hLev f g p

  left : (x : EM-raw H (suc m))  fun ptEM-raw x  f x
  left = wedgeconLeft G H (n + m) n m refl hLev f g p

  right : (x : EM-raw G (suc n))  fun x ptEM-raw  g x
  right = wedgeconRight G H (n + m) n m refl hLev f g p