{-# OPTIONS --safe --lossy-unification #-}
module Cubical.ZCohomology.Properties where

{-
This module contains:
1. direct proofs of connectedness of Kn and ΩKn
2. Induction principles for cohomology groups of pointed types
3. Equivalence between cohomology of A and reduced cohomology of (A + 1)
4. Equivalence between cohomology and reduced cohomology for dimension ≥ 1
5. Encode-decode proof of Kₙ ≃ ΩKₙ₊₁ and proofs that this equivalence
   and its inverse are morphisms
6. A proof of coHomGr ≅ coHomGrΩ
7. A locked (non-reducing) version of Kₙ ≃ ΩKₙ₊₁
8. Some HLevel lemmas used for the cup product
-}


open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Transport
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.Pointed.Homogeneous
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.GroupoidLaws renaming (assoc to assoc∙)
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Equiv.HalfAdjoint

open import Cubical.Functions.Morphism

open import Cubical.Data.Nat
open import Cubical.Data.Int renaming (_+_ to _ℤ+_) hiding (-_)
open import Cubical.Data.Sigma
open import Cubical.Data.Sum.Base

open import Cubical.Algebra.Group
open import Cubical.Algebra.Group.Morphisms
open import Cubical.Algebra.Group.MorphismProperties
open import Cubical.Algebra.AbGroup

open import Cubical.HITs.Truncation as T
open import Cubical.HITs.SetTruncation as ST renaming (isSetSetTrunc to §)
open import Cubical.HITs.S1 hiding (encode ; decode ; _·_)
open import Cubical.HITs.Sn
open import Cubical.HITs.Susp

open import Cubical.Homotopy.Loopspace
open import Cubical.Homotopy.Connected

open import Cubical.ZCohomology.Base
open import Cubical.ZCohomology.GroupStructure

open Iso renaming (inv to inv')

private
  variable
     ℓ' : Level

------------------- Connectedness ---------------------
is2ConnectedKn : (n : )  isConnected 2 (coHomK (suc n))
is2ConnectedKn zero =   base  
                    , T.elim  _  isOfHLevelPath 2 (isOfHLevelTrunc 2) _ _)
                        (T.elim  _  isOfHLevelPath 3 (isOfHLevelSuc 2 (isOfHLevelTrunc 2)) _ _)
                          (toPropElim  _  isOfHLevelTrunc 2 _ _) refl))
is2ConnectedKn (suc n) =   north  
                       , T.elim  _  isOfHLevelPath 2 (isOfHLevelTrunc 2) _ _)
                           (T.elim  _  isProp→isOfHLevelSuc (3 + n) (isOfHLevelTrunc 2 _ _))
                             (suspToPropElim (ptSn (suc n))  _  isOfHLevelTrunc 2 _ _) refl))

isConnectedKn : (n : )  isConnected (2 + n) (coHomK (suc n))
isConnectedKn n = isOfHLevelRetractFromIso 0 (invIso (truncOfTruncIso (2 + n) 1)) (sphereConnected (suc n))

-- direct proof of connectedness of ΩKₙ₊₁ not relying on the equivalence ∥ a ≡ b ∥ₙ ≃ (∣ a ∣ₙ₊₁ ≡ ∣ b ∣ₙ₊₁)
isConnectedPathKn : (n : ) (x y : (coHomK (suc n)))  isConnected (suc n) (x  y)
isConnectedPathKn n =
  T.elim  _  isProp→isOfHLevelSuc (2 + n) (isPropΠ λ _  isPropIsContr))
    (sphereElim _  _  isProp→isOfHLevelSuc n (isPropΠ λ _  isPropIsContr))
      λ y  isContrRetractOfConstFun
               {B = (hLevelTrunc (suc n) (ptSn (suc n)  ptSn (suc n)))}  refl 
                 (fun⁻ n y
                , T.elim  _  isOfHLevelPath (suc n) (isOfHLevelTrunc (suc n)) _ _)
                         (J  y p  fun⁻ n y _  _) (funExt⁻ (fun⁻Id n)  refl ))))
  where
  fun⁻ : (n : )  (y : coHomK (suc n)) 
         hLevelTrunc (suc n) (ptSn (suc n)  ptSn (suc n))
       hLevelTrunc (suc n) ( ptSn (suc n)   y)
  fun⁻ n =
    T.elim  _  isOfHLevelΠ (3 + n) λ _  isOfHLevelSuc (2 + n) (isOfHLevelSuc (suc n) (isOfHLevelTrunc (suc n))))
      (sphereElim n  _  isOfHLevelΠ (suc n) λ _  isOfHLevelTrunc (suc n)) λ _   refl )

  fun⁻Id : (n : )  fun⁻ n  ptSn (suc n)   λ _   refl 
  fun⁻Id zero = refl
  fun⁻Id (suc n) = refl

-------------------
-- Induction principles for cohomology groups (n ≥ 1)
-- If we want to show a proposition about some x : Hⁿ(A), it suffices to show it under the
-- assumption that x = ∣ f ∣₂ for some f : A → Kₙ and that f is pointed

coHomPointedElim : {A : Type } (n : ) (a : A) {B : coHom (suc n) A  Type ℓ'}
                  ((x : coHom (suc n) A)  isProp (B x))
                  ((f : A  coHomK (suc n))  f a  coHom-pt (suc n)  B  f ∣₂)
                  (x : coHom (suc n) A)  B x
coHomPointedElim {ℓ' = ℓ'} {A = A} n a isprop indp =
  ST.elim  _  isOfHLevelSuc 1 (isprop _))
         λ f  helper n isprop indp f (f a) refl
  where
  helper :  (n : ) {B : coHom (suc n) A  Type ℓ'}
          ((x : coHom (suc n) A)  isProp (B x))
          ((f : A  coHomK (suc n))  f a  coHom-pt (suc n)  B  f ∣₂)
          (f : A  coHomK (suc n))
          (x : coHomK (suc n))
          f a  x  B  f ∣₂
  -- pattern matching a bit extra to avoid isOfHLevelPlus'
  helper zero isprop ind f =
    T.elim  _  isOfHLevelPlus {n = 1} 2 (isPropΠ λ _  isprop _))
           (toPropElim  _  isPropΠ λ _  isprop _) (ind f))
  helper (suc zero) isprop ind f =
    T.elim  _  isOfHLevelPlus {n = 1} 3 (isPropΠ λ _  isprop _))
           (suspToPropElim base  _  isPropΠ λ _  isprop _) (ind f))
  helper (suc (suc zero)) isprop ind f =
    T.elim  _  isOfHLevelPlus {n = 1} 4 (isPropΠ λ _  isprop _))
           (suspToPropElim north  _  isPropΠ λ _  isprop _) (ind f))
  helper (suc (suc (suc n))) isprop ind f =
    T.elim  _  isOfHLevelPlus' {n = 5 + n} 1 (isPropΠ λ _  isprop _))
           (suspToPropElim north  _  isPropΠ λ _  isprop _) (ind f))


coHomPointedElim2 : {A : Type } (n : ) (a : A) {B : coHom (suc n) A  coHom (suc n) A  Type ℓ'}
                  ((x y : coHom (suc n) A)  isProp (B x y))
                  ((f g : A  coHomK (suc n))  f a  coHom-pt (suc n)  g a  coHom-pt (suc n)  B  f ∣₂  g ∣₂)
                  (x y : coHom (suc n) A)  B x y
coHomPointedElim2 {ℓ' = ℓ'} {A = A} n a isprop indp = ST.elim2  _ _  isOfHLevelSuc 1 (isprop _ _))
                                                   λ f g  helper n a isprop indp f g (f a) (g a) refl refl
  where
  helper : (n : ) (a : A) {B : coHom (suc n) A  coHom (suc n) A  Type ℓ'}
                  ((x y : coHom (suc n) A)  isProp (B x y))
                  ((f g : A  coHomK (suc n))  f a  coHom-pt (suc n)  g a  coHom-pt (suc n)  B  f ∣₂  g ∣₂)
                  (f g : A  coHomK (suc n))
                  (x y : coHomK (suc n))
                  f a  x  g a  y
                  B  f ∣₂  g ∣₂
  helper zero a isprop indp f g =
    T.elim2  _ _  isOfHLevelPlus {n = 1} 2 (isPropΠ2 λ _ _  isprop _ _))
          (toPropElim2  _ _  isPropΠ2 λ _ _  isprop _ _) (indp f g))
  helper (suc zero) a isprop indp f g =
    T.elim2  _ _  isOfHLevelPlus {n = 1} 3 (isPropΠ2 λ _ _  isprop _ _))
          (suspToPropElim2 base  _ _  isPropΠ2 λ _ _  isprop _ _) (indp f g))
  helper (suc (suc zero)) a isprop indp f g =
    T.elim2  _ _  isOfHLevelPlus {n = 1} 4 (isPropΠ2 λ _ _  isprop _ _))
          (suspToPropElim2 north  _ _  isPropΠ2 λ _ _  isprop _ _) (indp f g))
  helper (suc (suc (suc n))) a isprop indp f g =
    T.elim2  _ _  isOfHLevelPlus' {n = 5 + n} 1 (isPropΠ2 λ _ _  isprop _ _))
          (suspToPropElim2 north  _ _  isPropΠ2 λ _ _  isprop _ _) (indp f g))

coHomK-elim :  {} (n : ) {B : coHomK (suc n)  Type }
            ((x : _)  isOfHLevel (suc n) (B x))
            B (0ₖ (suc n))
            (x : _)  B x
coHomK-elim n {B = B } hlev b =
  T.elim  _  isOfHLevelPlus {n = (suc n)} 2 (hlev _))
    (sphereElim _ (hlev  ∣_∣) b)

{- Equivalence between cohomology of A and reduced cohomology of (A + 1) -}
coHomRed+1Equiv : (n : ) 
                  (A : Type ) 
                  (coHom n A)  (coHomRed n ((A  Unit , inr (tt))))
coHomRed+1Equiv zero A i =  helpLemma {C = ( , pos 0)} i ∥₂
  module coHomRed+1 where
  helpLemma : {C : Pointed }  ( (A  (typ C))   ((((A  Unit) , inr (tt)) →∙ C)))
  helpLemma {C = C} = isoToPath (iso map-helper1
                                     map-helper2
                                      b  linvPf b)
                                      _   refl))
    where
    map-helper1 : (A  typ C)  ((((A  Unit) , inr (tt)) →∙ C))
    map-helper1 f = map1' , refl
      module helpmap where
      map1' : A  Unit  fst C
      map1' (inl x) = f x
      map1' (inr x) = pt C

    map-helper2 : ((((A  Unit) , inr (tt)) →∙ C))  (A  typ C)
    map-helper2 (g , pf) x = g (inl x)

    linvPf : (b :((((A  Unit) , inr (tt)) →∙ C)))  map-helper1 (map-helper2 b)  b
    linvPf (f , snd) i =  x  helper x i)  , λ j  snd ((~ i)  j)
      where
      helper : (x : A  Unit)  ((helpmap.map1') (map-helper2 (f , snd)) x)  f x
      helper (inl x) = refl
      helper (inr tt) = sym snd
coHomRed+1Equiv (suc zero) A i =  coHomRed+1.helpLemma A i {C = (coHomK 1 ,  base )} i ∥₂
coHomRed+1Equiv (suc (suc n)) A i =  coHomRed+1.helpLemma A i {C = (coHomK (2 + n) ,  north )} i ∥₂

Iso-coHom-coHomRed :  {} {A : Pointed } (n : )  Iso (coHomRed (suc n) A) (coHom (suc n) (typ A))
fun (Iso-coHom-coHomRed {A = A , a} n) = ST.map fst
inv' (Iso-coHom-coHomRed {A = A , a} n) = ST.map λ f   x  f x -ₖ f a) , rCancelₖ _ _
rightInv (Iso-coHom-coHomRed {A = A , a} n) =
  ST.elim  _  isOfHLevelPath 2 § _ _)
         λ f  T.rec (isProp→isOfHLevelSuc _ (§ _ _))
                       p  cong ∣_∣₂ (funExt λ x  cong  y  f x +ₖ y) (cong -ₖ_ p  -0ₖ)  rUnitₖ _ (f x)))
                      (Iso.fun (PathIdTruncIso (suc n)) (isContr→isProp (isConnectedKn n)  f a   0ₖ _ ))
leftInv (Iso-coHom-coHomRed {A = A , a} n) =
  ST.elim  _  isOfHLevelPath 2 § _ _)
        λ {(f , p)  cong ∣_∣₂ (ΣPathP (((funExt λ x  (cong  y  f x -ₖ y) p
                                                    ∙∙ cong  y  f x +ₖ y) -0ₖ
                                                    ∙∙ rUnitₖ _ (f x))  refl))
                              , helper n (f a) (sym p)))}
    where
    path : (n : ) (x : coHomK (suc n)) (p : 0ₖ _  x)  _
    path n x p = (cong  y  x -ₖ y) (sym p) ∙∙ cong  y  x +ₖ y) -0ₖ ∙∙ rUnitₖ _ x)  refl

    helper :  (n : ) (x : coHomK (suc n)) (p : 0ₖ _  x)
             PathP  i  path n x p i  0ₖ _) (rCancelₖ _ x) (sym p)
    helper zero x =
      J  x p  PathP  i  path 0 x p i  0ₖ _)
                        (rCancelₖ _ x) (sym p))
        λ i j  rUnit (rUnit  _  0ₖ 1) (~ j)) (~ j) i
    helper (suc n) x =
      J  x p  PathP  i  path (suc n) x p i  0ₖ _) (rCancelₖ _ x) (sym p))
        λ i j  rCancelₖ (suc (suc n)) (0ₖ (suc (suc n))) (~ i  ~ j)

+∙≡+ : (n : ) {A : Pointed } (x y : coHomRed (suc n) A)
      Iso.fun (Iso-coHom-coHomRed n) (x +ₕ∙ y)
       Iso.fun (Iso-coHom-coHomRed n) x +ₕ Iso.fun (Iso-coHom-coHomRed n) y

+∙≡+ zero = ST.elim2  _ _  isOfHLevelPath 2 § _ _) λ _ _  refl
+∙≡+ (suc n) = ST.elim2  _ _  isOfHLevelPath 2 § _ _) λ _ _  refl

private
  homhelp :  {} (n : ) (A : Pointed ) (x y : coHom (suc n) (typ A))
           Iso.inv (Iso-coHom-coHomRed {A = A} n) (x +ₕ y)
            Iso.inv (Iso-coHom-coHomRed n) x +ₕ∙ Iso.inv (Iso-coHom-coHomRed n) y
  homhelp n A = morphLemmas.isMorphInv _+ₕ∙_ _+ₕ_
                (Iso.fun (Iso-coHom-coHomRed n)) (+∙≡+ n) _
                (Iso.rightInv (Iso-coHom-coHomRed n)) (Iso.leftInv (Iso-coHom-coHomRed n))

coHomGr≅coHomRedGr :  {} (n : ) (A : Pointed )
                   GroupEquiv (coHomRedGrDir (suc n) A) (coHomGr (suc n) (typ A))
fst (coHomGr≅coHomRedGr n A) = isoToEquiv (Iso-coHom-coHomRed n)
snd (coHomGr≅coHomRedGr n A) = makeIsGroupHom (+∙≡+ n)

coHomRedGroup :  {} (n : ) (A : Pointed )  AbGroup 
coHomRedGroup zero A = coHomRedGroupDir zero A
coHomRedGroup (suc n) A =
  InducedAbGroupFromPres·
                 (coHomGroup (suc n) (typ A))
                 _+ₕ∙_
                 (isoToEquiv (invIso (Iso-coHom-coHomRed n)))
                 (homhelp n A)

abstract
  coHomGroup≡coHomRedGroup :  {} (n : ) (A : Pointed )
                           coHomGroup (suc n) (typ A)  coHomRedGroup (suc n) A
  coHomGroup≡coHomRedGroup n A =
    InducedAbGroupPathFromPres·
              (coHomGroup (suc n) (typ A))
              _+ₕ∙_
              (isoToEquiv (invIso (Iso-coHom-coHomRed n)))
              (homhelp n A)

------------------- Kₙ ≃ ΩKₙ₊₁ ---------------------
-- This proof uses the encode-decode method rather than Freudenthal

-- We define the map σ : Kₙ → ΩKₙ₊₁ and prove that it is a morphism
private
  module _ (n : ) where
  σ : {n : }  coHomK (suc n)  Path (coHomK (2 + n))  north   north 
  σ {n = n} = T.rec (isOfHLevelTrunc (4 + n) _ _)
                    λ a  cong ∣_∣ (merid a  sym (merid (ptSn (suc n))))

  σ-hom-helper :  {} {A : Type } {a : A} (p : a  a) (r : refl  p)
                    lUnit p  cong (_∙ p) r  rUnit p  cong (p ∙_) r
  σ-hom-helper p = J  p r  lUnit p  cong (_∙ p) r  rUnit p  cong (p ∙_) r) refl

  σ-hom : {n : } (x y : coHomK (suc n))  σ (x +ₖ y)  σ x  σ y
  σ-hom {n = zero} =
    T.elim2  _ _  isOfHLevelPath 3 (isOfHLevelTrunc 4 _ _) _ _)
          (wedgeconFun _ _
             _ _  isOfHLevelTrunc 4 _ _ _ _)
             x  lUnit _
                   cong (_∙ σ  x ) (cong (cong ∣_∣) (sym (rCancel (merid base)))))
             y  cong σ (rUnitₖ 1  y )
                 ∙∙ rUnit _
                 ∙∙ cong (σ  y  ∙_) (cong (cong ∣_∣) (sym (rCancel (merid base)))))
            (sym (σ-hom-helper (σ  base ) (cong (cong ∣_∣) (sym (rCancel (merid base)))))))
  σ-hom {n = suc n} =
    T.elim2  _ _  isOfHLevelPath (4 + n) (isOfHLevelTrunc (5 + n) _ _) _ _)
          (wedgeconFun _ _  _ _  isOfHLevelPath ((2 + n) + (2 + n)) (wedgeConHLev' n) _ _)
            x  lUnit _
                  cong (_∙ σ  x ) (cong (cong ∣_∣) (sym (rCancel (merid north)))))
            y  cong σ (rUnitₖ (2 + n)  y )
                ∙∙ rUnit _
                ∙∙ cong (σ  y  ∙_) (cong (cong ∣_∣) (sym (rCancel (merid north)))))
           (sym (σ-hom-helper (σ  north ) (cong (cong ∣_∣) (sym (rCancel (merid north)))))))

  -- We will need to following lemma
  σ-minusDistr : {n : } (x y : coHomK (suc n))  σ (x -ₖ y)  σ x  sym (σ y)
  σ-minusDistr {n = n} =
    morphLemmas.distrMinus'
      _+ₖ_ _∙_
      σ σ-hom  (ptSn (suc n))  refl
      -ₖ_ sym
       x  sym (lUnit x))  x  sym (rUnit x))
      (rUnitₖ (suc n))
      (lCancelₖ (suc n)) rCancel
      (assocₖ (suc n)) assoc∙
       (cong (cong ∣_∣) (rCancel (merid (ptSn (suc n)))))

  -- we define the code using addIso
  Code : (n : )   coHomK (2 + n)  Type₀
  Code n x = (T.rec {B = TypeOfHLevel ℓ-zero (3 + n)} (isOfHLevelTypeOfHLevel (3 + n))
                     λ a  Code' a , hLevCode' a) x .fst
    where
    Code' : (S₊ (2 + n))  Type₀
    Code' north = coHomK (suc n)
    Code' south = coHomK (suc n)
    Code' (merid a i) = isoToPath (addIso (suc n)  a ) i

    hLevCode' : (x : S₊ (2 + n))  isOfHLevel (3 + n) (Code' x)
    hLevCode' = suspToPropElim (ptSn (suc n))  _  isPropIsOfHLevel (3 + n)) (isOfHLevelTrunc (3 + n))

  symMeridLem : (n : )  (x : S₊ (suc n)) (y : coHomK (suc n))
                         subst (Code n) (cong ∣_∣ (sym (merid x))) y  y -ₖ  x 
  symMeridLem n x = T.elim  _  isOfHLevelPath (3 + n) (isOfHLevelTrunc (3 + n)) _ _)
                             y  cong (_-ₖ  x ) (transportRefl  y ))

  decode : {n : } (x : coHomK (2 + n))  Code n x   north   x
  decode {n = n} = T.elim  _  isOfHLevelΠ (4 + n) λ _  isOfHLevelPath (4 + n) (isOfHLevelTrunc (4 + n)) _ _)
                          decode-elim
    where
    north≡merid : (a : S₊ (suc n))
                 Path (coHomK (2 + n))  north   north 
                 (Path (coHomK (2 + n))  north   south )
    north≡merid a i = Path (coHomK (2 + n))  north   merid a i 

    decode-elim : (a : S₊ (2 + n))  Code n  a   Path (coHomK (2 + n))  north   a 
    decode-elim north = σ
    decode-elim south = T.rec (isOfHLevelTrunc (4 + n) _ _)
                              λ a  cong ∣_∣ (merid a)
    decode-elim (merid a i) =
      hcomp  k  λ { (i = i0)  σ
                      ; (i = i1)  mainPath a k})
            (funTypeTransp (Code n)  x   north   x) (cong ∣_∣ (merid a)) σ i)
      where
      mainPath : (a : (S₊ (suc n))) 
             transport (north≡merid a)  σ  transport  i  Code n  merid a (~ i) )
            T.rec (isOfHLevelTrunc (4 + n) _ _) λ a  cong ∣_∣ (merid a)
      mainPath a = funExt (T.elim  _  isOfHLevelPath (3 + n) (isOfHLevelTrunc (4 + n) _ _) _ _)
                                   x   i  transport (north≡merid a) (σ (symMeridLem n a  x  i)))
                                       ∙∙ cong (transport (north≡merid a)) (-distrHelp x)
                                       ∙∙ (substAbove x)))
        where
        -distrHelp : (x : S₊ (suc n))  σ ( x  -ₖ  a )  cong ∣_∣ (merid x)  cong ∣_∣ (sym (merid a))
        -distrHelp x =
             σ-minusDistr  x   a 
             i  (cong ∣_∣ (compPath-filler (merid x)  j  merid (ptSn (suc n)) (~ j  i)) (~ i)))
                    (cong ∣_∣ (sym (compPath-filler (merid a)  j  merid (ptSn (suc n)) (~ j  i)) (~ i)))))

        substAbove : (x : S₊ (suc n))  transport (north≡merid a) (cong ∣_∣ (merid x)  cong ∣_∣ (sym (merid a)))
                    cong ∣_∣ (merid x)
        substAbove x i = transp  j  north≡merid a (i  j)) i
                                (compPath-filler (cong ∣_∣ (merid x))  j   merid a (~ j  i) ) (~ i))

  encode : {n : } {x : coHomK (2 + n)}  Path (coHomK (2 + n))  north  x  Code n x
  encode {n = n} p = transport (cong (Code n) p)  (ptSn (suc n)) 

  decode-encode : {n : } {x : coHomK (2 + n)} (p : Path (coHomK (2 + n))  north  x)
                decode _ (encode p)  p
  decode-encode {n = n} =
    J  y p  decode _ (encode p)  p)
        (cong (decode  north ) (transportRefl  ptSn (suc n) )
        cong (cong ∣_∣) (rCancel (merid (ptSn (suc n)))))

-- We define an addition operation on Code which we can use in order to show that encode is a
-- morphism (in a very loose sense)
  hLevCode : {n : } (x : coHomK (2 + n))  isOfHLevel (3 + n) (Code n x)
  hLevCode {n = n} =
    T.elim  _  isProp→isOfHLevelSuc (3 + n) (isPropIsOfHLevel (3 + n)))
      (sphereToPropElim _
         _  (isPropIsOfHLevel (3 + n))) (isOfHLevelTrunc (3 + n)))

  Code-add' : {n : } (x : _)  Code n  north   Code n  x   Code n  x 
  Code-add' {n = n} north = _+ₖ_
  Code-add' {n = n} south = _+ₖ_
  Code-add' {n = n} (merid a i) = helper n a i
    where
    help : (n : )  (x y a : S₊ (suc n))
         transport  i  Code n  north   Code n  merid a i   Code n  merid a i )
                     (_+ₖ_)  x   y 
           x  +ₖ  y 
    help n x y a =
          i  transportRefl (( transportRefl x i   +ₖ ( transportRefl y i  -ₖ  a )) +ₖ  a ) i)
      ∙∙ cong (_+ₖ  a ) (assocₖ _  x   y  (-ₖ  a ))
      ∙∙ sym (assocₖ _ ( x  +ₖ  y ) (-ₖ  a )  a )
      ∙∙ cong (( x  +ₖ  y ) +ₖ_) (lCancelₖ _  a )
      ∙∙ rUnitₖ _ _

    helper : (n : ) (a : S₊ (suc n))
            PathP  i  Code n  north   Code n  merid a i   Code n  merid a i ) _+ₖ_ _+ₖ_
    helper n a =
      toPathP (funExt
                (T.elim  _  isOfHLevelPath (3 + n) (isOfHLevelΠ (3 + n)  _  isOfHLevelTrunc (3 + n))) _ _)
                  λ x  funExt
                           (T.elim  _  isOfHLevelPath (3 + n) (isOfHLevelTrunc (3 + n)) _ _)
                                   λ y  help n x y a)))

  Code-add : {n : } (x : _)  Code n  north   Code n x  Code n x
  Code-add {n = n} =
    T.elim  x  isOfHLevelΠ (4 + n) λ _  isOfHLevelΠ (4 + n) λ _  isOfHLevelSuc (3 + n) (hLevCode {n = n} x))
           Code-add'

  encode-hom : {n : } {x : _} (q : 0ₖ _  0ₖ _) (p : 0ₖ _  x)
              encode (q  p)  Code-add {n = n} x (encode q) (encode p)
  encode-hom {n = n} q = J  x p  encode (q  p)  Code-add {n = n} x (encode q) (encode p))
                           (cong encode (sym (rUnit q))
                         ∙∙ sym (rUnitₖ _ (encode q))
                         ∙∙ cong (encode q +ₖ_) (cong ∣_∣ (sym (transportRefl _))))

stabSpheres : (n : )  Iso (coHomK (suc n)) (typ (Ω (coHomK-ptd (2 + n))))
fun (stabSpheres n) = decode _
inv' (stabSpheres n) = encode
rightInv (stabSpheres n) p = decode-encode p
leftInv (stabSpheres n) =
  T.elim  _  isOfHLevelPath (3 + n) (isOfHLevelTrunc (3 + n)) _ _)
    λ a  cong encode (congFunct ∣_∣ (merid a) (sym (merid (ptSn (suc n)))))
        ∙∙  i  transport (congFunct (Code n) (cong ∣_∣ (merid a))
                             (cong ∣_∣ (sym (merid (ptSn (suc n))))) i)  ptSn (suc n) )
        ∙∙ (substComposite  x  x)
                           (cong (Code n) (cong ∣_∣ (merid a)))
                           (cong (Code n) (cong ∣_∣ (sym (merid (ptSn (suc n))))))  ptSn (suc n) 
        ∙∙ cong (transport  i  Code n  merid (ptSn (suc n)) (~ i) ))
                (transportRefl ( (ptSn (suc n))  +ₖ  a )  lUnitₖ (suc n)  a )
        ∙∙ symMeridLem n (ptSn (suc n))  a 
        ∙∙ cong ( a  +ₖ_) -0ₖ
        ∙∙ rUnitₖ (suc n)  a )

Iso-Kn-ΩKn+1 : (n : HLevel)  Iso (coHomK n) (typ (Ω (coHomK-ptd (suc n))))
Iso-Kn-ΩKn+1 zero = invIso (compIso (congIso (truncIdempotentIso _ isGroupoidS¹)) ΩS¹Isoℤ)
Iso-Kn-ΩKn+1 (suc n) = stabSpheres n

Kn≃ΩKn+1 : {n : }  coHomK n  typ (Ω (coHomK-ptd (suc n)))
Kn≃ΩKn+1 {n = n} = isoToEquiv (Iso-Kn-ΩKn+1 n)

-- Some properties of the Iso
Kn→ΩKn+1 : (n : )  coHomK n  typ (Ω (coHomK-ptd (suc n)))
Kn→ΩKn+1 n = Iso.fun (Iso-Kn-ΩKn+1 n)

ΩKn+1→Kn : (n : )  typ (Ω (coHomK-ptd (suc n)))  coHomK n
ΩKn+1→Kn n = Iso.inv (Iso-Kn-ΩKn+1 n)

Kn→ΩKn+10ₖ : (n : )  Kn→ΩKn+1 n (0ₖ n)  refl
Kn→ΩKn+10ₖ zero = sym (rUnit refl)
Kn→ΩKn+10ₖ (suc n) i j =  (rCancel (merid (ptSn (suc n))) i j) 

ΩKn+1→Kn-refl : (n : )  ΩKn+1→Kn n refl  0ₖ n
ΩKn+1→Kn-refl zero = refl
ΩKn+1→Kn-refl (suc zero) = refl
ΩKn+1→Kn-refl (suc (suc n)) = refl

Kn≃ΩKn+1∙ : {n : }  coHomK-ptd n  (Ω (coHomK-ptd (suc n)))
Kn≃ΩKn+1∙ {n = n} = ua∙ Kn≃ΩKn+1 (Kn→ΩKn+10ₖ n)

Kn→ΩKn+1-hom : (n : ) (x y : coHomK n)  Kn→ΩKn+1 n (x +[ n ]ₖ y)  Kn→ΩKn+1 n x  Kn→ΩKn+1 n y
Kn→ΩKn+1-hom zero x y =  j i  hfill (doubleComp-faces  i₁   base )  _   base ) i)
                                         (inS ( intLoop (x ℤ+ y) i )) (~ j))
                      ∙∙  j i   intLoop-hom x y (~ j) i )
                      ∙∙ (congFunct ∣_∣ (intLoop x) (intLoop y)
                         cong₂ _∙_  j i  hfill (doubleComp-faces  i₁   base )  _   base ) i)
                                                    (inS ( intLoop x i )) j)
                                     λ j i  hfill (doubleComp-faces  i₁   base )  _   base ) i)
                                                    (inS ( intLoop y i )) j)
Kn→ΩKn+1-hom (suc n) = σ-hom

ΩKn+1→Kn-hom : (n : ) (x y : Path (coHomK (suc n)) (0ₖ _) (0ₖ _))
              ΩKn+1→Kn n (x  y)  ΩKn+1→Kn n x +[ n ]ₖ ΩKn+1→Kn n y
ΩKn+1→Kn-hom zero p q =
     cong winding (congFunct (T.rec isGroupoidS¹  x  x)) p q)
    winding-hom (cong (T.rec isGroupoidS¹  x  x)) p) (cong (T.rec isGroupoidS¹  x  x)) q)
ΩKn+1→Kn-hom (suc n) = encode-hom

Kn→ΩKn+1-ₖ : (n : ) (x : coHomK n)  Kn→ΩKn+1 n (-ₖ x)  sym (Kn→ΩKn+1 n x)
Kn→ΩKn+1-ₖ n x =
     lUnit _
  ∙∙ cong (_∙ Kn→ΩKn+1 n (-ₖ x)) (sym (lCancel _))
  ∙∙ sym (assoc∙ _ _ _)
  ∙∙ cong (sym (Kn→ΩKn+1 n x) ∙_) help
  ∙∙ sym (rUnit _)
  where
  help : Kn→ΩKn+1 n x  Kn→ΩKn+1 n (-ₖ x)  refl
  help = sym (Kn→ΩKn+1-hom n x (-ₖ x)) ∙∙ cong (Kn→ΩKn+1 n) (rCancelₖ n x) ∙∙ Kn→ΩKn+10ₖ n

isHomogeneousKn : (n : HLevel)  isHomogeneous (coHomK-ptd n)
isHomogeneousKn n =
  subst isHomogeneous
    (sym (ΣPathP (ua Kn≃ΩKn+1 , ua-gluePath _ (Kn→ΩKn+10ₖ n))))
    (isHomogeneousPath _ _)

-- With the equivalence Kn≃ΩKn+1, we get that the two definitions of cohomology groups agree
open IsGroupHom

coHom≅coHomΩ :  {} (n : ) (A : Type )  GroupIso (coHomGr n A) (coHomGrΩ n A)
fun (fst (coHom≅coHomΩ n A)) = ST.map λ f a  Kn→ΩKn+1 n (f a)
inv' (fst (coHom≅coHomΩ n A)) = ST.map λ f a  ΩKn+1→Kn n (f a)
rightInv (fst (coHom≅coHomΩ n A)) =
  ST.elim  _  isOfHLevelPath 2 § _ _)
        λ f  cong ∣_∣₂ (funExt λ x  rightInv (Iso-Kn-ΩKn+1 n) (f x))
leftInv (fst (coHom≅coHomΩ n A)) =
  ST.elim  _  isOfHLevelPath 2 § _ _)
        λ f  cong ∣_∣₂ (funExt λ x  leftInv (Iso-Kn-ΩKn+1 n) (f x))
snd (coHom≅coHomΩ n A) =
  makeIsGroupHom
    (ST.elim2  _ _  isOfHLevelPath 2 § _ _)
            λ f g  cong ∣_∣₂ (funExt λ x  Kn→ΩKn+1-hom n (f x) (g x)))

module lockedKnIso (key : Unit') where
  Kn→ΩKn+1' : (n : )  coHomK n  typ (Ω (coHomK-ptd (suc n)))
  Kn→ΩKn+1' n = lock key (Iso.fun (Iso-Kn-ΩKn+1 n))

  ΩKn+1→Kn' : (n : )  typ (Ω (coHomK-ptd (suc n)))  coHomK n
  ΩKn+1→Kn' n = lock key (Iso.inv (Iso-Kn-ΩKn+1 n))

  ΩKn+1→Kn→ΩKn+1 : (n : )  (x : typ (Ω (coHomK-ptd (suc n))))  Kn→ΩKn+1' n (ΩKn+1→Kn' n x)  x
  ΩKn+1→Kn→ΩKn+1 n x = pm key
    where
    pm : (key : Unit')  lock key (Iso.fun (Iso-Kn-ΩKn+1 n)) (lock key (Iso.inv (Iso-Kn-ΩKn+1 n)) x)  x
    pm unlock = Iso.rightInv (Iso-Kn-ΩKn+1 n) x

  Kn→ΩKn+1→Kn : (n : )  (x : coHomK n)  ΩKn+1→Kn' n (Kn→ΩKn+1' n x)  x
  Kn→ΩKn+1→Kn n x = pm key
    where
    pm : (key : Unit')  lock key (Iso.inv (Iso-Kn-ΩKn+1 n)) (lock key (Iso.fun (Iso-Kn-ΩKn+1 n)) x)  x
    pm unlock = Iso.leftInv (Iso-Kn-ΩKn+1 n) x

-distrLemma :  { ℓ'} {A : Type } {B : Type ℓ'} (n m : ) (f : GroupHom (coHomGr n A) (coHomGr m B))
              (x y : coHom n A)
             fst f (x -[ n ]ₕ y)  fst f x -[ m ]ₕ fst f y
-distrLemma n m f' x y = sym (-cancelRₕ m (f y) (f (x -[ n ]ₕ y)))
                     ∙∙ cong  x  x -[ m ]ₕ f y) (sym (f' .snd .pres· (x -[ n ]ₕ y) y))
                     ∙∙ cong  x  x -[ m ]ₕ f y) ( cong f (-+cancelₕ n _ _))
  where
  f = fst f'

-- HLevel stuff for cup product
isContr-↓∙ : (n : )  isContr (coHomK-ptd (suc n) →∙ coHomK-ptd n)
fst (isContr-↓∙ zero) =  _  0) , refl
snd (isContr-↓∙ zero) (f , p) =
  Σ≡Prop  f  isSetℤ _ _)
         (funExt (T.elim  _  isOfHLevelPath 3 (isOfHLevelSuc 2 isSetℤ) _ _)
                 (toPropElim  _  isSetℤ _ _) (sym p))))
fst (isContr-↓∙ (suc n)) =  _  0ₖ _) , refl
fst (snd (isContr-↓∙ (suc n)) f i) x =
  T.elim {B = λ x  0ₖ (suc n)  fst f x}
     _  isOfHLevelPath (4 + n) (isOfHLevelSuc (3 + n) (isOfHLevelTrunc (3 + n))) _ _)
    (sphereElim _  _  isOfHLevelTrunc (3 + n) _ _)
    (sym (snd f))) x i
snd (snd (isContr-↓∙ (suc n)) f i) j = snd f (~ i  j)

isContr-↓∙' : (n : )  isContr (S₊∙ (suc n) →∙ coHomK-ptd n)
fst (isContr-↓∙' zero) =  _  0) , refl
snd (isContr-↓∙' zero) (f , p) =
  Σ≡Prop  f  isSetℤ _ _)
         (funExt (toPropElim  _  isSetℤ _ _) (sym p)))
fst (isContr-↓∙' (suc n)) =  _  0ₖ _) , refl
fst (snd (isContr-↓∙' (suc n)) f i) x =
  sphereElim _ {A = λ x  0ₖ (suc n)  fst f x}
     _  isOfHLevelTrunc (3 + n) _ _)
    (sym (snd f)) x i
snd (snd (isContr-↓∙' (suc n)) f i) j = snd f (~ i  j)

isOfHLevel→∙Kn : {A : Pointed₀} (n m : )
   isOfHLevel (suc m) (A →∙ coHomK-ptd n)  isOfHLevel (suc (suc m)) (A →∙ coHomK-ptd (suc n))
isOfHLevel→∙Kn {A = A} n m hlev = step₃
  where
  step₁ : isOfHLevel (suc m) (A →∙ Ω (coHomK-ptd (suc n)))
  step₁ =
    subst (isOfHLevel (suc m))
       i  A →∙ ua∙ {A = Ω (coHomK-ptd (suc n))} {B = coHomK-ptd n}
      (invEquiv Kn≃ΩKn+1)
      (ΩKn+1→Kn-refl n) (~ i)) hlev

  step₂ : isOfHLevel (suc m) (typ (Ω (A →∙ coHomK-ptd (suc n) )))
  step₂ = isOfHLevelRetractFromIso (suc m) (invIso (invIso (ΩfunExtIso _ _))) step₁

  step₃ : isOfHLevel (suc (suc m)) (A →∙ coHomK-ptd (suc n))
  step₃ =
    isOfHLevelΩ→isOfHLevel m
      λ f  subst  x  isOfHLevel (suc m) (typ (Ω x)))
               (isHomogeneous→∙ (isHomogeneousKn (suc n)) f)
                step₂

isOfHLevel↑∙ :  n m  isOfHLevel (2 + n) (coHomK-ptd (suc m) →∙ coHomK-ptd (suc (n + m)))
isOfHLevel↑∙ zero m = isOfHLevel→∙Kn m 0 (isContr→isProp (isContr-↓∙ m))
isOfHLevel↑∙ (suc n) m = isOfHLevel→∙Kn (suc (n + m)) (suc n) (isOfHLevel↑∙ n m)

isOfHLevel↑∙' :  n m  isOfHLevel (2 + n) (S₊∙ (suc m) →∙ coHomK-ptd (suc (n + m)))
isOfHLevel↑∙' zero m = isOfHLevel→∙Kn m 0 (isContr→isProp (isContr-↓∙' m))
isOfHLevel↑∙' (suc n) m = isOfHLevel→∙Kn (suc (n + m)) (suc n) (isOfHLevel↑∙' n m)

→∙KnPath : (A : Pointed₀) (n : )  Ω (A →∙ coHomK-ptd (suc n) )  (A →∙ coHomK-ptd n )
→∙KnPath A n =
    ua∙ (isoToEquiv (ΩfunExtIso A (coHomK-ptd (suc n)))) refl
    i  (A →∙ Kn≃ΩKn+1∙ {n = n} (~ i) ))

contr∙-lem : (n m : )
   isContr (coHomK-ptd (suc n)
           →∙ (coHomK-ptd (suc m)
             →∙ coHomK-ptd (suc (n + m)) ))
fst (contr∙-lem n m) =  _   _  0ₖ _) , refl) , refl
snd (contr∙-lem n m) (f , p) = →∙Homogeneous≡ (isHomogeneous→∙ (isHomogeneousKn _)) (sym (funExt help))
  where
  help : (x : _)  (f x)  ((λ _  0ₖ _) , refl)
  help =
    T.elim  _  isOfHLevelPath (3 + n)
      (subst  x  isOfHLevel x (coHomK-ptd (suc m) →∙ coHomK-ptd (suc (n + m))))  i  suc (suc (+-comm n 1 i)))
             (isOfHLevelPlus' {n = 1} (2 + n) (isOfHLevel↑∙ n m))) _ _)
    (sphereElim _  _  isOfHLevel↑∙ n m _ _) p)

isOfHLevel↑∙∙ :  n m l
   isOfHLevel (2 + l) (coHomK-ptd (suc n)
                      →∙ (coHomK-ptd (suc m)
                       →∙ coHomK-ptd (suc (suc (l + n + m))) ))
isOfHLevel↑∙∙ n m zero =
  isOfHLevelΩ→isOfHLevel 0 λ f
     subst
        isProp (cong  x  typ (Ω x))
        (isHomogeneous→∙ (isHomogeneous→∙ (isHomogeneousKn _)) f))
        (isOfHLevelRetractFromIso 1 (ΩfunExtIso _ _) h)
  where
  h : isProp (coHomK-ptd (suc n)
           →∙ (Ω (coHomK-ptd (suc m)
            →∙ coHomK-ptd (suc (suc (n + m))) )))
  h =
    subst isProp  i  coHomK-ptd (suc n) →∙ (→∙KnPath (coHomK-ptd (suc m)) (suc (n + m)) (~ i)))
      (isContr→isProp (contr∙-lem n m))
isOfHLevel↑∙∙ n m (suc l) =
  isOfHLevelΩ→isOfHLevel (suc l)
    λ f 
    subst
        (isOfHLevel (2 + l)) (cong  x  typ (Ω x))
        (isHomogeneous→∙ (isHomogeneous→∙ (isHomogeneousKn _)) f))
        (isOfHLevelRetractFromIso (2 + l) (ΩfunExtIso _ _) h)
  where
  h : isOfHLevel (2 + l)
       (coHomK-ptd (suc n)
         →∙ (Ω (coHomK-ptd (suc m)
           →∙ coHomK-ptd (suc (suc (suc (l + n + m)))) )))
  h =
    subst (isOfHLevel (2 + l))
       i  coHomK-ptd (suc n) →∙ →∙KnPath (coHomK-ptd (suc m)) (suc (suc (l + n + m))) (~ i))
      (isOfHLevel↑∙∙ n m l)

----------- Misc. ------------
isoType→isoCohom : {A : Type } {B : Type ℓ'} (n : )
   Iso A B
   GroupIso (coHomGr n A) (coHomGr n B)
fst (isoType→isoCohom n is) = setTruncIso (domIso is)
snd (isoType→isoCohom n is) =
  makeIsGroupHom (ST.elim2  _ _  isOfHLevelPath 2 squash₂ _ _)
     _ _  refl))

-- Explicit index swapping for cohomology groups
transportCohomIso :  {A : Type } {n m : }
                   (p : n  m)
                   GroupIso (coHomGr n A) (coHomGr m A)
Iso.fun (fst (transportCohomIso {A = A} p)) = subst  n  coHom n A) p
Iso.inv (fst (transportCohomIso {A = A} p)) = subst  n  coHom n A) (sym p)
Iso.rightInv (fst (transportCohomIso p)) =
  transportTransport⁻ (cong (\ n  coHomGr n _ .fst) p)
Iso.leftInv (fst (transportCohomIso p)) =
  transportTransport⁻ (cong (\ n  coHomGr n _ .fst) (sym p))
snd (transportCohomIso {A = A} {n = n} {m = m} p) =
  makeIsGroupHom  x y  help x y p)
  where
  help : (x y : coHom n A) (p : n  m)
     subst  n  coHom n A) p (x +ₕ y)
      subst  n  coHom n A) p x +ₕ subst  n  coHom n A) p y
  help x y =
    J  m p  subst  n  coHom n A) p (x +ₕ y)
               subst  n  coHom n A) p x +ₕ subst  n  coHom n A) p y)
      (transportRefl (x +ₕ y)
       cong₂ _+ₕ_ (sym (transportRefl x)) (sym (transportRefl y)))