{-
This code is adapted from examples/brunerie3.ctt on the pi4s3_nobug
branch of cubicaltt It also contains a computation of a simplified
version of the Brunerie number, motivated in
Cubical.Homotopy.Group.Pi4S3.DirectProof.
-}
{-# OPTIONS --safe #-}
module Cubical.Homotopy.Group.Pi4S3.BrunerieExperiments where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Univalence

open import Cubical.Data.Bool
open import Cubical.Data.Nat
open import Cubical.Data.Int

open import Cubical.HITs.S1 hiding (encode)
open import Cubical.HITs.S2
open import Cubical.HITs.S3
open import Cubical.HITs.Join hiding (join∙)
open import Cubical.HITs.SetTruncation as SetTrunc
open import Cubical.HITs.GroupoidTruncation as GroupoidTrunc
open import Cubical.HITs.2GroupoidTruncation as 2GroupoidTrunc
open import Cubical.HITs.Truncation as Trunc
open import Cubical.HITs.Susp renaming (toSusp to σ)

open import Cubical.Homotopy.Loopspace
open import Cubical.Homotopy.Hopf
open S¹Hopf

Bool∙ S¹∙ S³∙ : Pointed₀
Bool∙ = (Bool , true)
S¹∙ = ( , base)
S³∙ = ( , base)

∥_∥₃∙ ∥_∥₄∙ : Pointed₀  Pointed₀
 A , a ∥₃∙ =  A ∥₃ ,  a ∣₃
 A , a ∥₄∙ =  A ∥₄ ,  a ∣₄

join∙ : Pointed₀  Type₀  Pointed₀
join∙ (A , a) B = join A B , inl a

Ω² Ω³ : Pointed₀  Pointed₀
Ω² = Ω^ 2
Ω³ = Ω^ 3

mapΩrefl : {A : Pointed₀} {B : Type₀} (f : A .fst  B)  Ω A .fst  Ω (B , f (pt A)) .fst
mapΩrefl f p i = f (p i)

mapΩ²refl : {A : Pointed₀} {B : Type₀} (f : A .fst  B)  Ω² A .fst  Ω² (B , f (pt A)) .fst
mapΩ²refl f p i j = f (p i j)

mapΩ³refl : {A : Pointed₀} {B : Type₀} (f : A .fst  B)  Ω³ A .fst  Ω³ (B , f (pt A)) .fst
mapΩ³refl f p i j k = f (p i j k)

meridS² :   Path  base base
meridS² base _ = base
meridS² (loop i) j = surf i j

alpha : join    
alpha (inl x) = base
alpha (inr y) = base
alpha (push x y i) = (meridS² y  meridS² x) i

connectionBoth : {A : Type₀} {a : A} (p : Path A a a)  PathP  i  Path A (p i) (p i)) p p
connectionBoth {a = a} p i j =
  hcomp
     k  λ
      { (i = i0)  p (j  ~ k)
      ; (i = i1)  p (j  k)
      ; (j = i0)  p (i  ~ k)
      ; (j = i1)  p (i  k)
      })
    a

data PostTotalHopf : Type₀ where
  base :   PostTotalHopf
  loop : (x : )  PathP  i  Path PostTotalHopf (base x) (base (rotLoop x (~ i)))) refl refl

tee12 : (x : )  HopfS² x  PostTotalHopf
tee12 base y = base y
tee12 (surf i j) y =
  hcomp
     k  λ
      { (i = i0)  base y
      ; (i = i1)  base y
      ; (j = i0)  base y
      ; (j = i1)  base (rotLoopInv y (~ i) k)
      })
    (loop (unglue (i  ~ i  j  ~ j) y) i j)

tee34 : PostTotalHopf  join  
tee34 (base x) = inl x
tee34 (loop x i j) =
  hcomp
     k  λ
      { (i = i0)  push x x (j  ~ k)
      ; (i = i1)  push x x (j  ~ k)
      ; (j = i0)  inl x
      ; (j = i1)  push (rotLoop x (~ i)) x (~ k)
      })
    (push x x j)

tee : (x : )  HopfS² x  join  
tee x y = tee34 (tee12 x y)

fibΩ : {B : Pointed₀} (P : B .fst  Type₀)  P (pt B)  Ω B .fst  Type₀
fibΩ P f p = PathP  i  P (p i)) f f

fibΩ² : {B : Pointed₀} (P : B .fst  Type₀)  P (pt B)  Ω² B .fst  Type₀
fibΩ² P f = fibΩ (fibΩ P f) refl

fibΩ³ : {B : Pointed₀} (P : B .fst  Type₀)  P (pt B)  Ω³ B .fst  Type₀
fibΩ³ P f = fibΩ² (fibΩ P f) refl

Ω³Hopf : Ω³ S²∙ .fst  Type₀
Ω³Hopf = fibΩ³ HopfS² base

fibContrΩ³Hopf :  p  Ω³Hopf p
fibContrΩ³Hopf p i j k =
  hcomp
     m  λ
      { (i = i0)  base
      ; (i = i1)  base
      ; (j = i0)  base
      ; (j = i1)  base
      ; (k = i0)  base
      ; (k = i1) 
        isSetΩS¹ refl refl
           i j  transp  n  HopfS² (p i j n)) (i  ~ i  j  ~ j) base)
           _ _  base)
          m i j
      })
    (transp  n  HopfS² (p i j (k  n))) (i  ~ i  j  ~ j  ~ k) base)

h : Ω³ S²∙ .fst  Ω³ (join∙ S¹∙ ) .fst
h p i j k = tee (p i j k) (fibContrΩ³Hopf p i j k)

multTwoAux : (x : )  Path (Path   ∥₄  x ∣₄  x ∣₄) refl refl
multTwoAux base i j =  surf i j ∣₄
multTwoAux (surf k l) i j =
  hcomp
     m  λ
      { (i = i0)   surf k l ∣₄
      ; (i = i1)   surf k l ∣₄
      ; (j = i0)   surf k l ∣₄
      ; (j = i1)   surf k l ∣₄
      ; (k = i0)   surf i j ∣₄
      ; (k = i1)   surf i j ∣₄
      ; (l = i0)   surf i j ∣₄
      ; (l = i1)  squash₄ _ _ _ _ _ _  k i j  step₁ k i j) refl m k i j
      })
    (step₁ k i j)

  where
  step₁ : I  I  I    ∥₄
  step₁ k i j =
    hcomp {A =   ∥₄}
       m  λ
        { (i = i0)   surf k (l  m) ∣₄
        ; (i = i1)   surf k (l  m) ∣₄
        ; (j = i0)   surf k (l  m) ∣₄
        ; (j = i1)   surf k (l  m) ∣₄
        ; (k = i0)   surf i j ∣₄
        ; (k = i1)   surf i j ∣₄
        ; (l = i0)   surf i j ∣₄
        })
      surf i j ∣₄

multTwoTildeAux : (t :   ∥₄)  Path (Path   ∥₄ t t) refl refl
multTwoTildeAux  x ∣₄ = multTwoAux x
multTwoTildeAux (squash₄ _ _ _ _ _ _ t u k l m n) i j =
  squash₄ _ _ _ _ _ _
     k l m  multTwoTildeAux (t k l m) i j)
     k l m  multTwoTildeAux (u k l m) i j)
    k l m n

multTwoEquivAux : Path (Path (  ∥₄    ∥₄) (idEquiv _) (idEquiv _)) refl refl
multTwoEquivAux i j =
  ( f i j
  , hcomp
       l  λ
        { (i = i0)  isPropIsEquiv _ (idIsEquiv _) (idIsEquiv _) l
        ; (i = i1)  isPropIsEquiv _ (idIsEquiv _) (idIsEquiv _) l
        ; (j = i0)  isPropIsEquiv _ (idIsEquiv _) (idIsEquiv _) l
        ; (j = i1) 
          isPropIsEquiv _
            (transp  k  isEquiv (f i k)) (i  ~ i) (idIsEquiv _))
            (idIsEquiv _)
            l
        })
      (transp  k  isEquiv (f i (j  k))) (i  ~ i  ~ j) (idIsEquiv _))
  )
  where
  f : I  I    ∥₄    ∥₄
  f i j t = multTwoTildeAux t i j

tHopf³ :   Type₀
tHopf³ base =   ∥₄
tHopf³ (surf i j k) =
  Glue   ∥₄
     { (i = i0)  (  ∥₄ , idEquiv _)
       ; (i = i1)  (  ∥₄ , idEquiv _)
       ; (j = i0)  (  ∥₄ , idEquiv _)
       ; (j = i1)  (  ∥₄ , idEquiv _)
       ; (k = i0)  (  ∥₄ , multTwoEquivAux i j)
       ; (k = i1)  (  ∥₄ , idEquiv _)
       })

π₃S³ : Ω³ S³∙ .fst  Ω²  S²∙ ∥₄∙ .fst
π₃S³ p i j = transp  k  tHopf³ (p j k i)) i0  base ∣₄

codeS² :   hGroupoid _
codeS² s =  HopfS² s ∥₃ , squash₃

codeTruncS² :   ∥₄  hGroupoid _
codeTruncS² = 2GroupoidTrunc.rec (isOfHLevelTypeOfHLevel 3) codeS²

encodeTruncS² : Ω  S²∙ ∥₄∙ .fst    ∥₃
encodeTruncS² p = transp  i  codeTruncS² (p i) .fst) i0  base ∣₃

codeS¹ :   hSet _
codeS¹ s =  helix s ∥₂ , squash₂

codeTruncS¹ :   ∥₃  hSet _
codeTruncS¹ = GroupoidTrunc.rec (isOfHLevelTypeOfHLevel 2) codeS¹

encodeTruncS¹ : Ω  S¹∙ ∥₃∙ .fst    ∥₂
encodeTruncS¹ p = transp  i  codeTruncS¹ (p i) .fst) i0  pos zero ∣₂


-- THE BIG GAME

f3 : Ω³ S³∙ .fst  Ω³ (join∙ S¹∙ ) .fst
f3 = mapΩ³refl S³→joinS¹S¹

f4 : Ω³ (join∙ S¹∙ ) .fst  Ω³ S²∙ .fst
f4 = mapΩ³refl alpha

f5 : Ω³ S²∙ .fst  Ω³ (join∙ S¹∙ ) .fst
f5 = h

f6 : Ω³ (join∙ S¹∙ ) .fst  Ω³ S³∙ .fst
f6 = mapΩ³refl joinS¹S¹→S³

f7 : Ω³ S³∙ .fst  Ω²  S²∙ ∥₄∙ .fst
f7 = π₃S³

g8 : Ω²  S²∙ ∥₄∙ .fst  Ω  S¹∙ ∥₃∙ .fst
g8 = mapΩrefl encodeTruncS²

g9 : Ω  S¹∙ ∥₃∙ .fst    ∥₂
g9 = encodeTruncS¹

g10 :   ∥₂  
g10 = SetTrunc.rec isSetℤ (idfun )

-- don't run me
brunerie : 
brunerie = g10 (g9 (g8 (f7 (f6 (f5 (f4 (f3  i j k  surf i j k))))))))



{-

Computation of an alternative definition of the Brunerie number based
on https://github.com/agda/cubical/pull/741. One should note that this
computation is quite different to the one of the term "brunerie"
defined above. This computation starts in π₃S³ rather than π₃S².

-}

-- The brunerie element can be shown to correspond to the following map
η₃ : (join   , inl base) →∙ (Susp  , north)
fst η₃ (inl x) = north
fst η₃ (inr x) = north
fst η₃ (push a b i) =
  (σ ( , base) (S¹×S¹→S² a b)  σ ( , base) (S¹×S¹→S² a b)) i
snd η₃ = refl

K₂ =   ∥₄
-- We will need a map Ω (Susp S²) → K₂. It turns out that the
-- following map is fast. It need a bit of work, however. It's
-- esentially the same map as you find in ZCohomology from ΩKₙ₊₁ to
-- Kₙ. This gives another definition of f7 which appears to work better.

module f7stuff where
  _+₂_ : K₂  K₂  K₂
  _+₂_ = 2GroupoidTrunc.elim  _  isOfHLevelΠ 4 λ _  squash₄)
          λ { base x  x
          ; (surf i j) x  surfc x i j}
    where
    surfc : (x : K₂)  typ ((Ω^ 2) (K₂ , x))
    surfc =
      2GroupoidTrunc.elim
         _  isOfHLevelPath 4 (isOfHLevelPath 4 squash₄ _ _) _ _)
        (S²ToSetElim  _  squash₄ _ _ _ _) λ i j   surf i j ∣₄)

  K₂≃K₂ : (x : )  K₂  K₂
  fst (K₂≃K₂ x) y =  x ∣₄ +₂ y
  snd (K₂≃K₂ x) = help x
    where
    help : (x : _)  isEquiv  y   x ∣₄ +₂ y)
    help = S²ToSetElim  _  isProp→isSet (isPropIsEquiv _))
                       (idEquiv _ .snd)

  Code : Susp   Type ℓ-zero
  Code north = K₂
  Code south = K₂
  Code (merid a i) = ua (K₂≃K₂ a) i

  encode : (x : Susp )   north  x  Code x
  encode x = J  x p  Code x)  base ∣₄

-- We now get an alternative definition of f7
f7' : typ (Ω (Susp∙ ))  K₂
f7' = f7stuff.encode north

-- We can define the Brunerie number by
brunerie' : 
brunerie' = g10 (g9 (g8 λ i j  f7' λ k  η₃ .fst (push (loop i) (loop j) k)))

-- Computing it takes ~1s
brunerie'≡-2 : brunerie'  -2
brunerie'≡-2 = refl

-- Proving that this indeed corresponds to the Brunerie number
-- requires us to phrase things slightly more carefully. For this, see
-- the second part of the Cubical.Homotopy.Group.Pi4S3.DirectProof.