{-# OPTIONS --warning=error --safe --without-K #-}

-- This file contains everything that can be compiled in --safe mode.

open import Numbers.Naturals.Naturals
open import Numbers.Naturals.Division
open import Numbers.BinaryNaturals.Definition
open import Numbers.BinaryNaturals.Multiplication
open import Numbers.BinaryNaturals.Order
open import Numbers.BinaryNaturals.Subtraction
open import Numbers.Primes.PrimeNumbers
open import Numbers.Primes.IntegerFactorisation
open import Numbers.Rationals.Lemmas

open import Numbers.Modulo.Group

open import Numbers.Integers.Integers
open import Numbers.Integers.RingStructure.EuclideanDomain
open import Numbers.Integers.RingStructure.Archimedean

open import Numbers.ClassicalReals.Examples
open import Numbers.ClassicalReals.RealField.Lemmas

open import Lists.Lists
open import Lists.Filter.AllTrue

open import Groups.Groups
open import Groups.Abelian.Lemmas
open import Groups.DirectSum.Definition
open import Groups.QuotientGroup.Definition
open import Groups.QuotientGroup.Lemmas
open import Groups.FiniteGroups.Definition
open import Groups.Homomorphisms.Lemmas
open import Groups.Homomorphisms.Lemmas2
open import Groups.Homomorphisms.Examples
open import Groups.Isomorphisms.Lemmas
open import Groups.FinitePermutations
open import Groups.Lemmas
open import Groups.FirstIsomorphismTheorem
open import Groups.SymmetricGroups.Definition
open import Groups.Actions.Stabiliser
open import Groups.Actions.Orbit
open import Groups.SymmetricGroups.Lemmas
open import Groups.ActionIsSymmetry
open import Groups.Cyclic.Definition
open import Groups.Cyclic.DefinitionLemmas
open import Groups.Polynomials.Examples
open import Groups.Cosets
open import Groups.Subgroups.Normal.Examples
open import Groups.Examples.ExampleSheet1
open import Groups.LectureNotes.Lecture1

open import Fields.Fields
open import Fields.Orders.Partial.Definition
open import Fields.Orders.Total.Definition
open import Fields.Orders.Partial.Archimedean
open import Fields.Orders.LeastUpperBounds.Examples
open import Fields.Orders.Lemmas
open import Fields.FieldOfFractions.Field
open import Fields.FieldOfFractions.Lemmas
open import Fields.FieldOfFractions.Order
--open import Fields.FieldOfFractions.Archimedean

open import Rings.Definition
open import Rings.Lemmas
open import Rings.Orders.Partial.Definition
open import Rings.Orders.Total.Lemmas
open import Rings.Orders.Partial.Lemmas
open import Rings.IntegralDomains.Definition
open import Rings.DirectSum
open import Rings.Polynomial.Ring
open import Rings.Polynomial.Evaluation
open import Rings.Ideals.Definition
open import Rings.Isomorphisms.Definition
open import Rings.Quotients.Definition
open import Rings.Cosets
open import Rings.EuclideanDomains.Definition
open import Rings.EuclideanDomains.Examples
open import Rings.Homomorphisms.Image
open import Rings.Homomorphisms.Kernel
open import Rings.Ideals.FirstIsomorphismTheorem
open import Rings.Ideals.Lemmas
open import Rings.Ideals.Prime.Definition
open import Rings.Ideals.Prime.Lemmas
open import Rings.Ideals.Principal.Definition
open import Rings.IntegralDomains.Examples
open import Rings.PrincipalIdealDomains.Lemmas
open import Rings.Subrings.Definition
open import Rings.Ideals.Maximal.Lemmas
open import Rings.Primes.Lemmas
open import Rings.Irreducibles.Definition
open import Rings.Divisible.Lemmas
open import Rings.Associates.Lemmas
open import Rings.InitialRing
open import Rings.Homomorphisms.Lemmas
open import Rings.UniqueFactorisationDomains.Definition
open import Rings.Examples.Examples

open import Setoids.Setoids
open import Setoids.DirectSum
open import Setoids.Lists
open import Setoids.Orders.Total.Lemmas
open import Setoids.Functions.Definition
open import Setoids.Functions.Extension
open import Setoids.Algebra.Lemmas
open import Setoids.Intersection.Lemmas
open import Setoids.Union.Lemmas
open import Setoids.Cardinality.Infinite.Lemmas
open import Setoids.Cardinality.Finite.Definition

open import Sets.Cardinality.Infinite.Lemmas
open import Sets.Cardinality.Countable.Lemmas
open import Sets.Cardinality.Finite.Lemmas
open import Sets.Cardinality
open import Sets.FinSet.Lemmas

open import Decidable.Sets
open import Decidable.Reduction
open import Decidable.Relations

open import Vectors
open import Vectors.VectorSpace

open import KeyValue.KeyValue
open import KeyValue.LinearStore.Definition

open import Maybe
open import Orders.Total.Lemmas
open import Orders.WellFounded.Induction

open import ClassicalLogic.ClassicalFive

open import Monoids.Definition

open import Semirings.Definition
open import Semirings.Solver

open import Categories.Definition
open import Categories.Functor.Definition
open import Categories.Functor.Lemmas
open import Categories.Dual.Definition

open import Graphs.PathGraph
open import Graphs.CycleGraph
open import Graphs.UnionGraph
open import Graphs.CompleteGraph
open import Graphs.Colouring
open import Graphs.Bipartite
open import Graphs.Complement
open import Graphs.InducedSubgraph

open import LectureNotes.MetAndTop.Chapter1
open import LectureNotes.NumbersAndSets.Lecture1

open import Modules.Examples
open import Modules.PolynomialModule
open import Modules.Lemmas
open import Modules.DirectSum

open import Computability.LambdaCalculus.ChurchNumeral

open import ProjectEuler.Problem1

module Everything.Safe where


{-# OPTIONS --rewriting --prop --confluence-check --cumulativity  #-}

open import Agda.Primitive
open import Agda.Builtin.Bool
open import Agda.Builtin.Nat
open import Agda.Builtin.List
open import Agda.Builtin.Equality
open import Agda.Builtin.Equality.Rewrite
open import Agda.Builtin.Sigma
open import Agda.Builtin.Unit
open import Data.Vec.Base
open import Data.Bool
open import Data.Sum
open import ett-rr

{- Axiomatisation of the Cast calculus -}

{- diagonal cases -}

postulate cast : (A : Set ℓ) (B : Set ℓ₁) → A → B

postulate cast-set : (A : Set ℓ) → cast (Set ℓ) (Set ℓ) A ≡ A

{-# REWRITE cast-set #-}

postulate cast-prop : (A : Prop ℓ) → cast (Prop ℓ) (Prop ℓ) A ≡ A

{-# REWRITE cast-prop #-}

postulate cast-Pi : (A : Set ℓ) (B : A → Set ℓ₁) (A' : Set ℓ₂) (B' : A' → Set ℓ₃) (f : (a : A) → B a)  →
                    cast ((a : A) → B a) ((a' : A') → B' a') f ≡
                    λ (a' : A') → cast _ _ (f (cast A' A a'))

{-# REWRITE cast-Pi #-}

postulate cast-Sigma : (A : Set ℓ) (B : A → Set ℓ₁) (A' : Set ℓ₂) (B' : A' → Set ℓ₃) (x : A) (y : B x) →
                       cast (Σ A B) (Σ A' B') (x , y) ≡ (cast {ℓ = ℓ} {ℓ₁ = ℓ₂} A A' x , cast (B x) (B' (cast A A' x)) y)

{-# REWRITE cast-Sigma #-}

postulate cast-Sum-inj₁ : (A A' : Set ℓ) (B B' : Set ℓ₁) (a : A) →
                    cast (A ⊎ B) (A' ⊎ B') (inj₁ a) ≡ inj₁ (cast A A' a)

postulate cast-Sum-inj₂ : (A A' : Set ℓ) (B B' : Set ℓ₁) (b : B) →
                    cast (A ⊎ B) (A' ⊎ B') (inj₂ b) ≡ inj₂ (cast B B' b)

{-# REWRITE cast-Sum-inj₁ #-}
{-# REWRITE cast-Sum-inj₂ #-}

postulate cast-List-nil : (A A' : Set ℓ) →
                          cast (List A) (List A') [] ≡ []

postulate cast-List-cons : (A A' : Set ℓ) (a : A) (l : List {a = ℓ} A) →
                           cast (List A) (List {a = ℓ} A') (a ∷ l) ≡
                           cast A A' a ∷ cast (List A) (List A') l

{-# REWRITE cast-List-nil #-}

{-# REWRITE cast-List-cons #-}

postulate cast-Nat-zero : cast Nat Nat 0 ≡ 0

postulate cast-Nat-suc : (n : Nat ) → cast Nat Nat (suc n) ≡ suc (cast _ _ n)

{-# REWRITE cast-Nat-zero #-}

{-# REWRITE cast-Nat-suc #-}

postulate cast-Bool-true : cast Bool Bool true ≡ true

{-# REWRITE cast-Bool-true #-}

postulate cast-Bool-false : cast Bool Bool false ≡ false

{-# REWRITE cast-Bool-false #-}

postulate cast-Unit : cast ⊤ ⊤ tt ≡ tt

{-# REWRITE cast-Unit #-}

{- non-diagonal cases -}

postulate cast-Set-bad : (A : Set (lsuc ℓ)) → cast (Set (lsuc ℓ)) (Set ℓ) A ≡ raise _

{-# REWRITE cast-Set-bad #-}

postulate cast-raise :  ∀ ℓ ℓ₁ → (A : Set ℓ₁) → cast (raise {ℓ = lsuc ℓ} (Set ℓ)) A (raise _) ≡ raise _

{-# REWRITE cast-raise #-}

postulate cast-Pi-Sigma : (A A' : Set ℓ) (B : A → Set ℓ₁) (B' : A' → Set ℓ₁) (f : (a : A) → B a)  →
                    cast ((a : A) → B a) (Σ {a = ℓ} {b = ℓ₁} A' B') f ≡ raise (Σ A' B')

{-# REWRITE cast-Pi-Sigma #-}

postulate cast-Pi-Nat : (A : Set ℓ) (B : A → Set ℓ₁) (f : (a : A) → B a) →
                    cast ((a : A) → B a) Nat f ≡ raise Nat

{-# REWRITE cast-Pi-Nat #-}

{- Rules specific to Unk -}

postulate cast-Pi-Unk : (A : Set ℓ) (B : A → Set ℓ₁) (f : ((a : A) → B a)) →
                        cast ((a : A) → B a) (Unk (ℓ ⊔ ℓ₁)) f ≡ box ((a : A) → B a) f

postulate cast-Unk : (A : Set ℓ) (B : Set ℓ₁) (f : A) →
                        cast (Unk ℓ) B (box A f) ≡ cast A B f

postulate cast-Pi-Unk-bad : (f : Unk ℓ → Unk ℓ₁) →
                            cast (Unk ℓ → Unk ℓ₁) (Unk (ℓ ⊔ ℓ₁)) f ≡ raise _

{-# REWRITE cast-Pi-Unk cast-Unk cast-Pi-Unk-bad #-}

postulate cast-Sigma-Unk : (A : Set ℓ) (B : A → Set ℓ₁) (x : Σ {a = ℓ} {b = ℓ₁} A B) →
                        cast (Σ A B) (Unk _) x ≡ box (Σ A B) x

{-# REWRITE cast-Sigma-Unk #-}

proj : {A : Set ℓ₁} → Unk ℓ → A
proj {ℓ₁} {ℓ} {A} (box A' a') = cast {ℓ = ℓ} {ℓ₁ = ℓ₁} A' A a'

delta : Unk ℓ → Unk ℓ
delta {ℓ} x = proj {A = Unk ℓ → Unk ℓ} x x

omega : Unk (lsuc ℓ)
omega {ℓ} = delta {ℓ = lsuc ℓ} (box (Unk ℓ → Unk ℓ) (delta {ℓ = ℓ}))


------------------------------------------------------------------------
-- Equivalence relations
------------------------------------------------------------------------

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

open import Equality

module Equivalence-relation
  {e⁺} (eq : ∀ {a p} → Equality-with-J a p e⁺) where

open Derived-definitions-and-properties eq

open import Prelude

open import H-level eq
open import H-level.Closure eq

private
  variable
    a r r₁ r₂         : Level
    A A₁ A₂ B B₁ B₂ C : Type a
    R R₁ R₂           : A → B → Type r

------------------------------------------------------------------------
-- The definition

-- The definition of "equivalence relation".

record Is-equivalence-relation
         {A : Type a} (R : A → A → Type r) : Type (a ⊔ r) where
  field
    reflexive  : ∀ {x} → R x x
    symmetric  : ∀ {x y} → R x y → R y x
    transitive : ∀ {x y z} → R x y → R y z → R x z

------------------------------------------------------------------------
-- Some examples of equivalence relations/equivalence relation
-- transformers

-- A trivial binary relation.

Trivial : A → B → Type r
Trivial _ _ = ↑ _ ⊤

-- Homogeneous instances of Trivial are equivalence relations.

Trivial-is-equivalence-relation :
  Is-equivalence-relation (Trivial {A = A} {r = r})
Trivial-is-equivalence-relation = _

-- Trivial is propositional.

Trivial-is-propositional :
  {x y : A} → Is-proposition (Trivial {r = r} x y)
Trivial-is-propositional = ↑-closure 1 (mono₁ 0 ⊤-contractible)

-- The superscript P used in the names of the definitions in this
-- section stands for "pointwise".

-- Lifts binary relations from B to A → B.

infix 0 _→ᴾ_

_→ᴾ_ :
  (A : Type a) →
  (B → C → Type r) →
  ((A → B) → (A → C) → Type (a ⊔ r))
(_ →ᴾ R) f g = ∀ x → R (f x) (g x)

-- _→ᴾ_ preserves equivalence relations.

→ᴾ-preserves-Is-equivalence-relation :
  Is-equivalence-relation R →
  Is-equivalence-relation (A →ᴾ R)
→ᴾ-preserves-Is-equivalence-relation R-equiv = record
  { reflexive  = λ _ → reflexive
  ; symmetric  = λ f∼g x → symmetric (f∼g x)
  ; transitive = λ f∼g g∼h x → transitive (f∼g x) (g∼h x)
  }
  where
  open Is-equivalence-relation R-equiv

-- _→ᴾ_ preserves Is-proposition (assuming extensionality).

→ᴾ-preserves-Is-proposition :
  {A : Type a} (R : B → C → Type r) →
  Extensionality a r →
  (∀ {x y} → Is-proposition (R x y)) →
  ∀ {f g} → Is-proposition ((A →ᴾ R) f g)
→ᴾ-preserves-Is-proposition _ ext R-prop =
  Π-closure ext 1 λ _ →
  R-prop

-- Lifts binary relations from A and B to A ⊎ B.

infixr 1 _⊎ᴾ_

_⊎ᴾ_ :
  (A₁ → A₂ → Type r) →
  (B₁ → B₂ → Type r) →
  (A₁ ⊎ B₁ → A₂ ⊎ B₂ → Type r)
(P ⊎ᴾ Q) (inj₁ x) (inj₁ y) = P x y
(P ⊎ᴾ Q) (inj₂ x) (inj₂ y) = Q x y
(P ⊎ᴾ Q) _        _        = ⊥

-- _⊎ᴾ_ preserves Is-equivalence-relation.

⊎ᴾ-preserves-Is-equivalence-relation :
  Is-equivalence-relation R₁ →
  Is-equivalence-relation R₂ →
  Is-equivalence-relation (R₁ ⊎ᴾ R₂)
⊎ᴾ-preserves-Is-equivalence-relation R₁-equiv R₂-equiv = record
  { reflexive  = λ { {x = inj₁ _} → reflexive R₁-equiv
                   ; {x = inj₂ _} → reflexive R₂-equiv
                   }
  ; symmetric  = λ { {x = inj₁ _} {y = inj₁ _} → symmetric R₁-equiv
                   ; {x = inj₂ _} {y = inj₂ _} → symmetric R₂-equiv
                   ; {x = inj₁ _} {y = inj₂ _} ()
                   ; {x = inj₂ _} {y = inj₁ _} ()
                   }
  ; transitive = λ
     { {x = inj₁ _} {y = inj₁ _} {z = inj₁ _} → transitive R₁-equiv
     ; {x = inj₂ _} {y = inj₂ _} {z = inj₂ _} → transitive R₂-equiv

     ; {x = inj₁ _} {y = inj₂ _} ()
     ; {x = inj₂ _} {y = inj₁ _} ()
     ; {y = inj₁ _} {z = inj₂ _} _ ()
     ; {y = inj₂ _} {z = inj₁ _} _ ()
     }
  }
  where
  open Is-equivalence-relation

-- _⊎ᴾ_ preserves Is-proposition.

⊎ᴾ-preserves-Is-proposition :
  (∀ {x y} → Is-proposition (R₁ x y)) →
  (∀ {x y} → Is-proposition (R₂ x y)) →
  ∀ {x y} → Is-proposition ((R₁ ⊎ᴾ R₂) x y)
⊎ᴾ-preserves-Is-proposition = λ where
  R₁-prop R₂-prop {inj₁ _} {inj₁ _} → R₁-prop
  R₁-prop R₂-prop {inj₁ _} {inj₂ _} → ⊥-propositional
  R₁-prop R₂-prop {inj₂ _} {inj₁ _} → ⊥-propositional
  R₁-prop R₂-prop {inj₂ _} {inj₂ _} → R₂-prop

-- Lifts a binary relation from A to Maybe A.

Maybeᴾ :
  (A → B → Type r) →
  (Maybe A → Maybe B → Type r)
Maybeᴾ R = Trivial ⊎ᴾ R

-- Maybeᴾ preserves Is-equivalence-relation.

Maybeᴾ-preserves-Is-equivalence-relation :
  Is-equivalence-relation R →
  Is-equivalence-relation (Maybeᴾ R)
Maybeᴾ-preserves-Is-equivalence-relation =
  ⊎ᴾ-preserves-Is-equivalence-relation Trivial-is-equivalence-relation

-- Maybeᴾ preserves Is-proposition.

Maybeᴾ-preserves-Is-proposition :
  (∀ {x y} → Is-proposition (R x y)) →
  ∀ {x y} → Is-proposition (Maybeᴾ R x y)
Maybeᴾ-preserves-Is-proposition =
  ⊎ᴾ-preserves-Is-proposition λ {x} →
  Trivial-is-propositional {x = x}

-- Lifts binary relations from A and B to A × B.

infixr 2 _×ᴾ_

_×ᴾ_ :
  (A₁ → B₁ → Type r₁) →
  (A₂ → B₂ → Type r₂) →
  (A₁ × A₂ → B₁ × B₂ → Type (r₁ ⊔ r₂))
(P ×ᴾ Q) (x₁ , x₂) (y₁ , y₂) = P x₁ y₁ × Q x₂ y₂

-- _×ᴾ_ preserves Is-equivalence-relation.

×ᴾ-preserves-Is-equivalence-relation :
  Is-equivalence-relation R₁ →
  Is-equivalence-relation R₂ →
  Is-equivalence-relation (R₁ ×ᴾ R₂)
×ᴾ-preserves-Is-equivalence-relation R₁-equiv R₂-equiv = λ where
    .Is-equivalence-relation.reflexive →
      E₁.reflexive , E₂.reflexive
    .Is-equivalence-relation.symmetric →
      Σ-map E₁.symmetric E₂.symmetric
    .Is-equivalence-relation.transitive →
      Σ-zip E₁.transitive E₂.transitive
  where
  module E₁ = Is-equivalence-relation R₁-equiv
  module E₂ = Is-equivalence-relation R₂-equiv

-- _×ᴾ_ preserves Is-proposition.

×ᴾ-preserves-Is-proposition :
  (∀ {x y} → Is-proposition (R₁ x y)) →
  (∀ {x y} → Is-proposition (R₂ x y)) →
  ∀ {x y} → Is-proposition ((R₁ ×ᴾ R₂) x y)
×ᴾ-preserves-Is-proposition R₁-prop R₂-prop =
  ×-closure 1 R₁-prop R₂-prop


------------------------------------------------------------------------
-- A solver for certain natural number equalities
------------------------------------------------------------------------

-- The standard library's solver is used.

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

open import Equality

module Nat.Solver
  {reflexive} (eq : ∀ {a p} → Equality-with-J a p reflexive) where

open Derived-definitions-and-properties eq

import Equality.Propositional as EP
open import Equality.Instances-related
open import Prelude

open import Bijection eq using (_↔_)
open import Function-universe eq

open import Data.Nat.Solver
open import Data.Vec as Vec
open import Data.Vec.N-ary
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence using (Equivalence)
import Relation.Binary.Reflection
import Relation.Binary.PropositionalEquality as P

open +-*-Solver public using (_:=_; _:+_; _:*_; _:^_)
open +-*-Solver.Polynomial public using (con)

open +-*-Solver hiding (solve)
open Relation.Binary.Reflection (P.setoid _) var ⟦_⟧ ⟦_⟧↓ correct
  using (close)

private

  -- Some simple lemmas.

  ≡↔≡ : ∀ {a} {A : Type a} {x y : A} → x ≡ y ↔ x P.≡ y
  ≡↔≡ = proj₁ (all-equality-types-isomorphic eq EP.equality-with-J)

  ≡→≡ : ∀ {a} {A : Type a} {x y : A} → x ≡ y → x P.≡ y
  ≡→≡ = _↔_.to ≡↔≡

  ≡←≡ : ∀ {a} {A : Type a} {x y : A} → x P.≡ y → x ≡ y
  ≡←≡ = _↔_.from ≡↔≡

-- A variant of +-*-Solver.solve.

solve :
  ∀ n (f : N-ary n (Polynomial n)
                   (Polynomial n × Polynomial n)) →
  Eqʰ n _≡_ (curryⁿ ⟦ proj₁ (close n f) ⟧↓)
            (curryⁿ ⟦ proj₂ (close n f) ⟧↓) →
  Eq  n _≡_ (curryⁿ ⟦ proj₁ (close n f) ⟧)
            (curryⁿ ⟦ proj₂ (close n f) ⟧)
solve n f =
  Eqʰ n _≡_ (curryⁿ ⟦ proj₁ (close n f) ⟧↓)
            (curryⁿ ⟦ proj₂ (close n f) ⟧↓)                        ↝⟨ Eqʰ-to-Eq n _≡_ ⟩

  Eq n _≡_ (curryⁿ ⟦ proj₁ (close n f) ⟧↓)
           (curryⁿ ⟦ proj₂ (close n f) ⟧↓)                         ↔⟨⟩

  ∀ⁿ n (curryⁿ λ xs → (curryⁿ ⟦ proj₁ (close n f) ⟧↓ $ⁿ xs) ≡
                      (curryⁿ ⟦ proj₂ (close n f) ⟧↓ $ⁿ xs))       ↝⟨ Equivalence.to (uncurry-∀ⁿ n) ⟨$⟩_ ⟩

  (∀ (xs : Vec _ n) →
     (curryⁿ λ xs → (curryⁿ ⟦ proj₁ (close n f) ⟧↓ $ⁿ xs) ≡
                    (curryⁿ ⟦ proj₂ (close n f) ⟧↓ $ⁿ xs)) $ⁿ xs)  ↝⟨ (∀-cong _ λ xs → ≡⇒↝ _ $ ≡←≡ $ left-inverse _ xs) ⟩

  (∀ (xs : Vec _ n) →
     (curryⁿ ⟦ proj₁ (close n f) ⟧↓ $ⁿ xs) ≡
     (curryⁿ ⟦ proj₂ (close n f) ⟧↓ $ⁿ xs))                        ↝⟨ (∀-cong _ λ _ → ≡→≡) ⟩

  (∀ (xs : Vec _ n) →
     (curryⁿ ⟦ proj₁ (close n f) ⟧↓ $ⁿ xs) P.≡
     (curryⁿ ⟦ proj₂ (close n f) ⟧↓ $ⁿ xs))                        ↝⟨ (∀-cong _ λ xs → ≡⇒↝ _ $ sym $ ≡←≡ $ left-inverse _ xs) ⟩

  (∀ (xs : Vec _ n) →
     (curryⁿ λ xs → (curryⁿ ⟦ proj₁ (close n f) ⟧↓ $ⁿ xs) P.≡
                    (curryⁿ ⟦ proj₂ (close n f) ⟧↓ $ⁿ xs)) $ⁿ xs)  ↝⟨ Equivalence.from (uncurry-∀ⁿ n) ⟨$⟩_ ⟩

  ∀ⁿ n (curryⁿ λ xs → (curryⁿ ⟦ proj₁ (close n f) ⟧↓ $ⁿ xs) P.≡
                      (curryⁿ ⟦ proj₂ (close n f) ⟧↓ $ⁿ xs))       ↔⟨⟩

  Eq n P._≡_ (curryⁿ ⟦ proj₁ (close n f) ⟧↓)
             (curryⁿ ⟦ proj₂ (close n f) ⟧↓)                       ↝⟨ Eq-to-Eqʰ n P._≡_ ⟩

  Eqʰ n P._≡_ (curryⁿ ⟦ proj₁ (close n f) ⟧↓)
              (curryⁿ ⟦ proj₂ (close n f) ⟧↓)                      ↝⟨ +-*-Solver.solve n f ⟩

  Eq n P._≡_ (curryⁿ ⟦ proj₁ (close n f) ⟧)
             (curryⁿ ⟦ proj₂ (close n f) ⟧)                        ↔⟨⟩

  ∀ⁿ n (curryⁿ λ xs → (curryⁿ ⟦ proj₁ (close n f) ⟧ $ⁿ xs) P.≡
                      (curryⁿ ⟦ proj₂ (close n f) ⟧ $ⁿ xs))        ↝⟨ Equivalence.to (uncurry-∀ⁿ n) ⟨$⟩_ ⟩

  (∀ (xs : Vec _ n) →
     (curryⁿ λ xs → (curryⁿ ⟦ proj₁ (close n f) ⟧ $ⁿ xs) P.≡
                    (curryⁿ ⟦ proj₂ (close n f) ⟧ $ⁿ xs)) $ⁿ xs)   ↝⟨ (∀-cong _ λ xs → ≡⇒↝ _ $ ≡←≡ $ left-inverse _ xs) ⟩

  (∀ (xs : Vec _ n) →
     (curryⁿ ⟦ proj₁ (close n f) ⟧ $ⁿ xs) P.≡
     (curryⁿ ⟦ proj₂ (close n f) ⟧ $ⁿ xs))                         ↝⟨ (∀-cong _ λ _ → ≡←≡) ⟩

  (∀ (xs : Vec _ n) →
     (curryⁿ ⟦ proj₁ (close n f) ⟧ $ⁿ xs) ≡
     (curryⁿ ⟦ proj₂ (close n f) ⟧ $ⁿ xs))                         ↝⟨ (∀-cong _ λ xs → ≡⇒↝ _ $ sym $ ≡←≡ $ left-inverse _ xs) ⟩

  (∀ (xs : Vec _ n) →
     (curryⁿ λ xs → (curryⁿ ⟦ proj₁ (close n f) ⟧ $ⁿ xs) ≡
                    (curryⁿ ⟦ proj₂ (close n f) ⟧ $ⁿ xs)) $ⁿ xs)   ↝⟨ Equivalence.from (uncurry-∀ⁿ n) ⟨$⟩_ ⟩

  ∀ⁿ n (curryⁿ λ xs → (curryⁿ ⟦ proj₁ (close n f) ⟧ $ⁿ xs) ≡
                      (curryⁿ ⟦ proj₂ (close n f) ⟧ $ⁿ xs))        ↔⟨⟩

  Eq n _≡_ (curryⁿ ⟦ proj₁ (close n f) ⟧)
           (curryⁿ ⟦ proj₂ (close n f) ⟧)                          □


{-# OPTIONS --rewriting --confluence-check #-}

open import Agda.Builtin.Bool
open import Agda.Builtin.Equality
open import Agda.Builtin.Equality.Rewrite

module _ where

data Unit : Set where
  unit : Unit

sym : {A : Set} {x y : A} → x ≡ y → y ≡ x
sym refl = refl

subst : {A : Set} (P : A → Set) {x y : A} → x ≡ y → P x → P y
subst P refl p = p

module _ (p : false ≡ true) where

  block : {A : Set} → Unit → A → A
  block unit x = x

  r : ∀ u → block u false ≡ true
  r unit = p

  {-# REWRITE r #-}

  r′ : ∀ u → block u false ≡ true
  r′ u = refl

  lazy : false ≡ true
  lazy = r′ unit

T : Bool → Set
T true  = Bool
T false = Bool → Bool

module _ (p : false ≡ true) where

  bool : (Bool → Bool) → Bool
  bool = subst T (lazy p)

  fun : Bool → (Bool → Bool)
  fun = subst T (sym (lazy p))

  omega : Bool → Bool
  omega = λ x → fun x x

  loop : Bool
  loop = omega (bool omega)

-- omega = λ p x → x x
-- loop = λ p → <BLACKHOLE>


------------------------------------------------------------------------
-- Functional semantics for an untyped λ-calculus with constants
------------------------------------------------------------------------

module Lambda.Closure.Functional where

open import Category.Monad
open import Category.Monad.Partiality as Partiality
  using (_⊥; never; OtherKind; other; steps)
open import Codata.Musical.Notation
open import Data.Empty using (⊥-elim)
open import Data.List hiding (lookup)
open import Data.Maybe hiding (_>>=_)
import Data.Maybe.Categorical as Maybe
open import Data.Nat
open import Data.Product
open import Data.Sum
open import Data.Vec using (Vec; []; _∷_; lookup)
open import Function
import Level
open import Relation.Binary using (module Preorder)
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Relation.Nullary
open import Relation.Nullary.Negation

open Partiality._⊥
private
  open module E {A : Set} = Partiality.Equality (_≡_ {A = A})
  open module R {A : Set} =
    Partiality.Reasoning (P.isEquivalence {A = A})

open import Lambda.Syntax
open Closure Tm
open import Lambda.VirtualMachine
open Functional
private
  module VM = Closure Code

------------------------------------------------------------------------
-- A monad with partiality and failure

PF : RawMonad {f = Level.zero} (_⊥ ∘ Maybe)
PF = Maybe.monadT Partiality.monad

module PF where
  open RawMonad PF public

  fail : {A : Set} → Maybe A ⊥
  fail = now nothing

  _>>=-cong_ :
    ∀ {k} {A B : Set} {x₁ x₂ : Maybe A ⊥} {f₁ f₂ : A → Maybe B ⊥} →
    Rel k x₁ x₂ → (∀ x → Rel k (f₁ x) (f₂ x)) →
    Rel k (x₁ >>= f₁) (x₂ >>= f₂)
  _>>=-cong_ {k} {f₁ = f₁} {f₂} x₁≈x₂ f₁≈f₂ =
    Partiality._>>=-cong_ x₁≈x₂ helper
    where
    helper : ∀ {x y} → x ≡ y →
             Rel k (maybe f₁ fail x) (maybe f₂ fail y)
    helper {x = nothing} P.refl = fail ∎
    helper {x = just x}  P.refl = f₁≈f₂ x

  associative :
    {A B C : Set}
    (x : Maybe A ⊥) (f : A → Maybe B ⊥) (g : B → Maybe C ⊥) →
    (x >>= f >>= g) ≅ (x >>= λ y → f y >>= g)
  associative x f g =
    (x >>= f >>= g)                      ≅⟨ Partiality.associative P.refl x _ _ ⟩
    (x >>=′ λ y → maybe f fail y >>= g)  ≅⟨ Partiality._>>=-cong_ (x ∎) helper ⟩
    (x >>= λ y → f y >>= g)              ∎
    where
    open RawMonad Partiality.monad renaming (_>>=_ to _>>=′_)

    helper : ∀ {y₁ y₂} → y₁ ≡ y₂ →
             (maybe f fail y₁ >>= g) ≅ maybe (λ z → f z >>= g) fail y₂
    helper {y₁ = nothing} P.refl = fail ∎
    helper {y₁ = just y}  P.refl = (f y >>= g) ∎

  >>=-inversion-⇓ :
    ∀ {k} {A B : Set} x {f : A → Maybe B ⊥} {y} →
    (x>>=f⇓ : (x >>= f) ⇓[ k ] just y) →
    ∃ λ z → ∃₂ λ (x⇓ : x ⇓[ k ] just z)
                 (fz⇓ : f z ⇓[ k ] just y) →
                 steps x⇓ + steps fz⇓ ≡ steps x>>=f⇓
  >>=-inversion-⇓ x {f} x>>=f⇓
    with Partiality.>>=-inversion-⇓ {_∼A_ = _≡_} P.refl x x>>=f⇓
  ... | (nothing , x↯ , now ()  , _)
  ... | (just z  , x⇓ , fz⇓     , eq) = (z , x⇓ , fz⇓ , eq)

  >>=-inversion-⇑ :
    ∀ {k} {A B : Set} x {f : A → Maybe B ⊥} →
    (x >>= f) ⇑[ other k ] →
    ¬ ¬ (x ⇑[ other k ] ⊎
         ∃ λ y → x ⇓[ other k ] just y × f y ⇑[ other k ])
  >>=-inversion-⇑ {k} x {f} x>>=f⇑ =
    helper ⟨$⟩ Partiality.>>=-inversion-⇑ P.isEquivalence x x>>=f⇑
    where
    open RawMonad ¬¬-Monad renaming (_<$>_ to _⟨$⟩_)

    helper : (_ ⊎ ∃ λ (y : Maybe _) → _) → _
    helper (inj₁ x⇑                      ) = inj₁ x⇑
    helper (inj₂ (just y  , x⇓,fy⇑)      ) = inj₂ (y , x⇓,fy⇑)
    helper (inj₂ (nothing , x↯,now∼never)) =
      ⊥-elim (Partiality.now≉never (proj₂ x↯,now∼never))

------------------------------------------------------------------------
-- A workaround for the limitations of guardedness

module Workaround where

  data Maybe_⊥P : Set → Set₁ where
    fail   : ∀ {A} → Maybe A ⊥P
    return : ∀ {A} (x : A) → Maybe A ⊥P
    later  : ∀ {A} (x : ∞ (Maybe A ⊥P)) → Maybe A ⊥P
    _>>=_  : ∀ {A B} (x : Maybe A ⊥P) (f : A → Maybe B ⊥P) → Maybe B ⊥P

  private

    data Maybe_⊥W : Set → Set₁ where
      fail   : ∀ {A} → Maybe A ⊥W
      return : ∀ {A} (x : A) → Maybe A ⊥W
      later  : ∀ {A} (x : Maybe A ⊥P) → Maybe A ⊥W

    mutual

      _>>=W_ : ∀ {A B} → Maybe A ⊥W → (A → Maybe B ⊥P) → Maybe B ⊥W
      fail     >>=W f = fail
      return x >>=W f = whnf (f x)
      later x  >>=W f = later (x >>= f)

      whnf : ∀ {A} → Maybe A ⊥P → Maybe A ⊥W
      whnf fail       = fail
      whnf (return x) = return x
      whnf (later x)  = later (♭ x)
      whnf (x >>= f)  = whnf x >>=W f

  mutual

    private

      ⟪_⟫W : ∀ {A} → Maybe A ⊥W → Maybe A ⊥
      ⟪ fail     ⟫W = PF.fail
      ⟪ return x ⟫W = PF.return x
      ⟪ later x  ⟫W = later (♯ ⟪ x ⟫P)

    ⟪_⟫P : ∀ {A} → Maybe A ⊥P → Maybe A ⊥
    ⟪ p ⟫P = ⟪ whnf p ⟫W

  -- The definitions above make sense. ⟪_⟫P is homomorphic with respect
  -- to fail, return, later and _>>=_.

  fail-hom : ∀ {A} → ⟪ fail {A = A} ⟫P ≅ PF.fail
  fail-hom = PF.fail ∎

  return-hom : ∀ {A} (x : A) → ⟪ return x ⟫P ≅ PF.return x
  return-hom x = PF.return x ∎

  later-hom : ∀ {A} (x : ∞ Maybe A ⊥P) →
              ⟪ later x ⟫P ≅ later (♯ ⟪ ♭ x ⟫P)
  later-hom x = later (♯ (⟪ ♭ x ⟫P ∎))

  mutual

    private

      >>=-homW : ∀ {A B} (x : Maybe A ⊥W) (f : A → Maybe B ⊥P) →
                 ⟪ x >>=W f ⟫W ≅ PF._>>=_ ⟪ x ⟫W (λ y → ⟪ f y ⟫P)
      >>=-homW fail       f = PF.fail ∎
      >>=-homW (return x) f = ⟪ f x ⟫P ∎
      >>=-homW (later x)  f = later (♯ >>=-hom x f)

    >>=-hom : ∀ {A B} (x : Maybe A ⊥P) (f : A → Maybe B ⊥P) →
              ⟪ x >>= f ⟫P ≅ PF._>>=_ ⟪ x ⟫P (λ y → ⟪ f y ⟫P)
    >>=-hom x f = >>=-homW (whnf x) f

open Workaround hiding (_>>=_)

------------------------------------------------------------------------
-- Semantics

infix 5 _∙_

-- Note that this definition gives us determinism "for free".

mutual

  ⟦_⟧′ : ∀ {n} → Tm n → Env n → Maybe Value ⊥P
  ⟦ con i ⟧′   ρ = return (con i)
  ⟦ var x ⟧′   ρ = return (lookup ρ x)
  ⟦ ƛ t ⟧′     ρ = return (ƛ t ρ)
  ⟦ t₁ · t₂ ⟧′ ρ = ⟦ t₁ ⟧′ ρ >>= λ v₁ →
                   ⟦ t₂ ⟧′ ρ >>= λ v₂ →
                   v₁ ∙ v₂
    where open Workaround

  _∙_ : Value → Value → Maybe Value ⊥P
  con i  ∙ v₂ = fail
  ƛ t₁ ρ ∙ v₂ = later (♯ (⟦ t₁ ⟧′ (v₂ ∷ ρ)))

⟦_⟧ : ∀ {n} → Tm n → Env n → Maybe Value ⊥
⟦ t ⟧ ρ = ⟪ ⟦ t ⟧′ ρ ⟫P

------------------------------------------------------------------------
-- Example

Ω-loops : ⟦ Ω ⟧ [] ≈ never
Ω-loops = later (♯ Ω-loops)

------------------------------------------------------------------------
-- Some lemmas

open PF hiding (_>>=_)

-- An abbreviation.

infix 5 _⟦·⟧_

_⟦·⟧_ : Maybe Value ⊥ → Maybe Value ⊥ → Maybe Value ⊥
v₁ ⟦·⟧ v₂ = v₁ >>= λ v₁ → v₂ >>= λ v₂ → ⟪ v₁ ∙ v₂ ⟫P
  where open PF

-- _⟦·⟧_ preserves equality.

_⟦·⟧-cong_ : ∀ {k v₁₁ v₁₂ v₂₁ v₂₂} →
             Rel k v₁₁ v₂₁ → Rel k v₁₂ v₂₂ →
             Rel k (v₁₁ ⟦·⟧ v₁₂) (v₂₁ ⟦·⟧ v₂₂)
v₁₁≈v₂₁ ⟦·⟧-cong v₁₂≈v₂₂ =
  v₁₁≈v₂₁ >>=-cong λ v₁ →
  v₁₂≈v₂₂ >>=-cong λ v₂ →
  ⟪ v₁ ∙ v₂ ⟫P ∎

-- The semantics of application is compositional (with respect to the
-- syntactic equality which is used).

·-comp : ∀ {n} (t₁ t₂ : Tm n) {ρ} →
         ⟦ t₁ · t₂ ⟧ ρ ≅ ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ
·-comp t₁ t₂ {ρ} =
  ⟦ t₁ · t₂ ⟧ ρ                                    ≅⟨ >>=-hom (⟦ t₁ ⟧′ ρ) _ ⟩

  PF._>>=_ (⟦ t₁ ⟧ ρ) (λ v₁ →
           ⟪ Workaround._>>=_ (⟦ t₂ ⟧′ ρ) (λ v₂ →
             v₁ ∙ v₂) ⟫P)                          ≅⟨ ((⟦ t₁ ⟧ ρ ∎) >>=-cong λ _ →
                                                       >>=-hom (⟦ t₂ ⟧′ ρ) _) ⟩
  ⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ                            ∎

open PF

------------------------------------------------------------------------
-- Compiler correctness

module Correctness {k : OtherKind} where

  infix  4 _≈P_ _≈W_
  infixr 2 _≡⟨_⟩W_ _≈⟨_⟩P_ _≈⟨_⟩W_

  mutual

    data _≈P_ : Maybe VM.Value ⊥ → Maybe VM.Value ⊥ → Set where
      _≈⟨_⟩P_ : ∀ x {y z} (x≈y : x ≈P y) (y≅z : y ≅ z) → x ≈P z
      correct :
        ∀ {n} t {ρ : Env n} {c s} {k : Value → Maybe VM.Value ⊥} →
        (hyp : ∀ v → exec ⟨ c , val (comp-val v) ∷ s , comp-env ρ ⟩
                       ≈W k v) →
        exec ⟨ comp t c , s , comp-env ρ ⟩ ≈P (⟦ t ⟧ ρ >>= k)

    data _≈W_ : Maybe VM.Value ⊥ → Maybe VM.Value ⊥ → Set where
      ⌈_⌉    : ∀ {x y} (x≈y : Rel (other k) x y) → x ≈W y
      later  : ∀ {x y} (x≈y : ♭ x ≈P ♭ y) → later x ≈W later y
      laterˡ : ∀ {x y} (x≈y : ♭ x ≈W   y) → later x ≈W       y

  _≡⟨_⟩W_ : ∀ x {y z} → x ≡ y → y ≈W z → x ≈W z
  _ ≡⟨ P.refl ⟩W y≈z = y≈z

  _≈⟨_⟩W_ : ∀ x {y z} → x ≈W y → y ≅ z → x ≈W z
  ._ ≈⟨ later  x≈y ⟩W later y≅z = later  (_ ≈⟨ x≈y ⟩P ♭ y≅z)
  ._ ≈⟨ laterˡ x≈y ⟩W       y≅z = laterˡ (_ ≈⟨ x≈y ⟩W   y≅z)
  _  ≈⟨ ⌈ x≈y ⌉    ⟩W       y≅z = ⌈ trans x≈y (Partiality.≅⇒ y≅z) ⌉
    where trans = Preorder.trans (Partiality.preorder P.isPreorder _)

  -- The relation _≈_ does not admit unrestricted use of transitivity
  -- in corecursive proofs, so I have formulated the correctness proof
  -- using a continuation. Note that the proof would perhaps be easier
  -- if the semantics was also formulated in continuation-passing
  -- style.

  mutual

    correctW :
      ∀ {n} t {ρ : Env n} {c s} {k : Value → Maybe VM.Value ⊥} →
      (∀ v → exec ⟨ c , val (comp-val v) ∷ s , comp-env ρ ⟩ ≈W k v) →
      exec ⟨ comp t c , s , comp-env ρ ⟩ ≈W (⟦ t ⟧ ρ >>= k)
    correctW (con i) {ρ} {c} {s} {k} hyp = laterˡ (
      exec ⟨ c , val (Lambda.Syntax.Closure.con i) ∷ s , comp-env ρ ⟩  ≈⟨ hyp (con i) ⟩W
      k (con i)                                                        ∎)
    correctW (var x) {ρ} {c} {s} {k} hyp = laterˡ (
      exec ⟨ c , val (lookup (comp-env ρ) x) ∷ s , comp-env ρ ⟩  ≡⟨ P.cong (λ v → exec ⟨ c , val v ∷ s , comp-env ρ ⟩) $ lookup-hom x ρ ⟩W
      exec ⟨ c , val (comp-val (lookup ρ x)) ∷ s , comp-env ρ ⟩  ≈⟨ hyp (lookup ρ x) ⟩W
      k (lookup ρ x)                                             ∎)
    correctW (ƛ t) {ρ} {c} {s} {k} hyp = laterˡ (
      exec ⟨ c , val (comp-val (ƛ t ρ)) ∷ s , comp-env ρ ⟩  ≈⟨ hyp (ƛ t ρ) ⟩W
      k (ƛ t ρ)                                             ∎)
    correctW (t₁ · t₂) {ρ} {c} {s} {k} hyp =
      exec ⟨ comp t₁ (comp t₂ (app ∷ c)) , s , comp-env ρ ⟩           ≈⟨ correctW t₁ (λ v₁ → correctW t₂ (λ v₂ → ∙-correctW v₁ v₂ hyp)) ⟩W
      (⟦ t₁ ⟧ ρ >>= λ v₁ →  ⟦ t₂ ⟧ ρ >>= λ v₂ → ⟪ v₁ ∙ v₂ ⟫P  >>= k)  ≅⟨ ((⟦ t₁ ⟧ ρ ∎) >>=-cong λ _ → sym $ associative (⟦ t₂ ⟧ ρ) _ _) ⟩
      (⟦ t₁ ⟧ ρ >>= λ v₁ → (⟦ t₂ ⟧ ρ >>= λ v₂ → ⟪ v₁ ∙ v₂ ⟫P) >>= k)  ≅⟨ sym $ associative (⟦ t₁ ⟧ ρ) _ _ ⟩
      (⟦ t₁ ⟧ ρ ⟦·⟧ ⟦ t₂ ⟧ ρ >>= k)                                   ≅⟨ sym (·-comp t₁ t₂ >>=-cong λ v → k v ∎) ⟩
      (⟦ t₁ · t₂ ⟧ ρ >>= k)                                           ∎

    ∙-correctW :
      ∀ {n} v₁ v₂ {ρ : Env n} {c s} {k : Value → Maybe VM.Value ⊥} →
      (∀ v → exec ⟨ c , val (comp-val v) ∷ s , comp-env ρ ⟩ ≈W k v) →
      exec ⟨ app ∷ c , val (comp-val v₂) ∷ val (comp-val v₁) ∷ s , comp-env ρ ⟩ ≈W
      (⟪ v₁ ∙ v₂ ⟫P >>= k)
    ∙-correctW (con i)   v₂                 _   = ⌈ PF.fail ∎ ⌉
    ∙-correctW (ƛ t₁ ρ₁) v₂ {ρ} {c} {s} {k} hyp =
      exec ⟨ app ∷ c , val (comp-val v₂) ∷ val (comp-val (ƛ t₁ ρ₁)) ∷ s , comp-env ρ ⟩  ≈⟨ later (

        exec ⟨ comp t₁ [ ret ] , ret c (comp-env ρ) ∷ s , comp-env (v₂ ∷ ρ₁) ⟩               ≈⟨ correct t₁ (λ v → laterˡ (hyp v)) ⟩P
        (⟦ t₁ ⟧ (v₂ ∷ ρ₁) >>= k)                                                             ∎) ⟩W

      (⟪ ƛ t₁ ρ₁ ∙ v₂ ⟫P >>= k)                                                         ∎

  whnf : ∀ {x y} → x ≈P y → x ≈W y
  whnf (x ≈⟨ x≈y ⟩P y≅z) = x ≈⟨ whnf x≈y ⟩W y≅z
  whnf (correct t hyp)   = correctW t hyp

  mutual

    soundW : ∀ {x y} → x ≈W y → Rel (other k) x y
    soundW ⌈ x≈y ⌉      = x≈y
    soundW (later  x≈y) = later (♯ soundP x≈y)
    soundW (laterˡ x≈y) = laterˡ (soundW x≈y)

    soundP : ∀ {x y} → x ≈P y → Rel (other k) x y
    soundP x≈y = soundW (whnf x≈y)

-- Note that the equality that is used here is syntactic.

correct : ∀ t →
          exec ⟨ comp t [] , [] , [] ⟩ ≈
          (⟦ t ⟧ [] >>= λ v → PF.return (comp-val v))
correct t =
  soundP $ Correctness.correct t (λ v → ⌈ PF.return (comp-val v) ∎ ⌉)
  where open Correctness


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Homotopy.HopfInvariant.Homomorphism where

open import Cubical.Homotopy.HopfInvariant.Base
open import Cubical.Homotopy.Group.Base
open import Cubical.Homotopy.Group.SuspensionMap

open import Cubical.ZCohomology.Base
open import Cubical.ZCohomology.GroupStructure
open import Cubical.ZCohomology.Properties
open import Cubical.ZCohomology.MayerVietorisUnreduced
open import Cubical.ZCohomology.Groups.Unit
open import Cubical.ZCohomology.Groups.Wedge
open import Cubical.ZCohomology.Groups.Sn
open import Cubical.ZCohomology.RingStructure.CupProduct
open import Cubical.ZCohomology.RingStructure.GradedCommutativity
open import Cubical.ZCohomology.Gysin

open import Cubical.Foundations.Path
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Transport
open import Cubical.Foundations.Function
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Pointed
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Foundations.Equiv

open import Cubical.Data.Sigma
open import Cubical.Data.Int hiding (_+'_)
open import Cubical.Data.Nat renaming (_+_ to _+ℕ_ ; _·_ to _·ℕ_)
open import Cubical.Data.Unit

open import Cubical.Algebra.Group
open import Cubical.Algebra.Group.DirProd
open import Cubical.Algebra.Group.ZAction
open import Cubical.Algebra.Group.Exact
open import Cubical.Algebra.Group.Morphisms
open import Cubical.Algebra.Group.MorphismProperties
open import Cubical.Algebra.Group.Instances.Int
open import Cubical.Algebra.Group.GroupPath

open import Cubical.HITs.Pushout
open import Cubical.HITs.Sn
open import Cubical.HITs.Susp
open import Cubical.HITs.Wedge
open import Cubical.HITs.Truncation
open import Cubical.HITs.SetTruncation
  renaming (elim to sElim ; elim2 to sElim2 ; map to sMap)
open import Cubical.HITs.PropositionalTruncation

open PlusBis

-- The pushout describing the hopf invariant of the multiplication (∙Π) of
-- two maps (S³⁺²ⁿ →∙ S²⁺ⁿ)
C·Π : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n)) → Type _
C·Π n f g = Pushout (λ _ → tt) (∙Π f g .fst)

-- Another pushout, essentially the same, but starting with
-- S³⁺²ⁿ ∨ S³⁺²ⁿ. This will be used to break up the hopf invariant
-- of ∙Π f g.
C* : ∀ n → (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n)) → Type _
C* n f g = Pushout (λ _ → tt) (fst (∨→∙ f g))

-- The coequaliser of ∙Π and ∨→∙
θ : ∀ {ℓ} {A : Pointed ℓ} → Susp (fst A)
  → (Susp (fst A) , north) ⋁ (Susp (fst A) , north)
θ north = inl north
θ south = inr north
θ {A = A} (merid a i₁) =
     ((λ i → inl ((merid a ∙ sym (merid (pt A))) i))
  ∙∙ push tt
  ∙∙ λ i → inr ((merid a ∙ sym (merid (pt A))) i)) i₁

∙Π≡∨→∘θ : ∀ n → (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
     → (x : _) → ∙Π f g .fst x ≡ (f ∨→ g) (θ {A = S₊∙ _} x)
∙Π≡∨→∘θ n f g north = sym (snd f)
∙Π≡∨→∘θ n f g south = sym (snd g)
∙Π≡∨→∘θ n f g (merid a i₁) j = main j i₁
  where
  help : cong (f ∨→ g) (cong (θ {A = S₊∙ _}) (merid a))
       ≡ (refl ∙∙ cong (fst f) (merid a ∙ sym (merid north)) ∙∙ snd f)
       ∙ (sym (snd g) ∙∙ cong (fst g) (merid a ∙ sym (merid north)) ∙∙ refl)
  help = cong-∙∙ (f ∨→ g) ((λ i → inl ((merid a ∙ sym (merid north)) i)))
                           (push tt)
                           (λ i → inr ((merid a ∙ sym (merid north)) i))
      ∙∙ doubleCompPath≡compPath _ _ _
      ∙∙ cong (cong (f ∨→ g)
              (λ i₂ → inl ((merid a ∙ (λ i₃ → merid north (~ i₃))) i₂)) ∙_)
              (sym (assoc _ _ _))
      ∙∙ assoc _ _ _
      ∙∙ cong₂ _∙_ refl (compPath≡compPath' _ _)

  main : PathP (λ i → snd f (~ i)
                    ≡ snd g (~ i)) (λ i₁ → ∙Π f g .fst (merid a i₁))
               λ i₁ → (f ∨→ g) (θ {A = S₊∙ _} (merid a i₁))
  main = (λ i → ((λ j → snd f (~ i ∧ ~ j))
               ∙∙ cong (fst f) (merid a ∙ sym (merid north))
               ∙∙ snd f)
       ∙ (sym (snd g)
         ∙∙ cong (fst g) (merid a ∙ sym (merid north))
         ∙∙ λ j → snd g (~ i ∧ j)))
       ▷ sym help

private
  WedgeElim : ∀ {ℓ} (n : ℕ)
              {P : S₊∙ (3 +ℕ (n +ℕ n)) ⋁ S₊∙ (3 +ℕ (n +ℕ n)) → Type ℓ}
            → ((x : _) → isOfHLevel (3 +ℕ n) (P x))
            → P (inl north)
            → (x : _) → P x
  WedgeElim n {P = P} x s (inl x₁) =
    sphereElim _ {A = P ∘ inl}
      (λ _ → isOfHLevelPlus' {n = n} (3 +ℕ n) (x _)) s x₁
  WedgeElim n {P = P} x s (inr x₁) =
    sphereElim _ {A = P ∘ inr}
      (sphereElim _ (λ _ → isProp→isOfHLevelSuc ((suc (suc (n +ℕ n))))
        (isPropIsOfHLevel (suc (suc (suc (n +ℕ n))))))
          (subst (isOfHLevel ((3 +ℕ n) +ℕ n)) (cong P (push tt))
            (isOfHLevelPlus' {n = n} (3 +ℕ n) (x _))))
          (subst P (push tt) s) x₁
  WedgeElim n {P = P} x s (push a j) =
    transp (λ i → P (push tt (i ∧ j))) (~ j) s

H²C*≅ℤ : ∀ n → (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
     → GroupIso (coHomGr (2 +ℕ n) (C* n f g)) ℤGroup
H²C*≅ℤ n f g = compGroupIso is (Hⁿ-Sⁿ≅ℤ (suc n))
  where
  ∘inr : (coHom (2 +ℕ n) (C* n f g)) → coHom (2 +ℕ n) (S₊ (2 +ℕ n))
  ∘inr = sMap λ f → f ∘ inr

  invMapPrim : (S₊ (2 +ℕ n) → coHomK (2 +ℕ n))
             → C* n f g → coHomK (2 +ℕ n)
  invMapPrim h (inl x) = h (ptSn _)
  invMapPrim h (inr x) = h x
  invMapPrim h (push a i₁) =
    WedgeElim n {P = λ a → h north ≡ h (fst (∨→∙ f g) a)}
                (λ _ → isOfHLevelTrunc (4 +ℕ n) _ _)
                (cong h (sym (snd f))) a i₁

  invMap : coHom (2 +ℕ n) (S₊ (2 +ℕ n)) → (coHom (2 +ℕ n) (C* n f g))
  invMap = sMap invMapPrim

  ∘inrSect : (x : coHom (2 +ℕ n) (S₊ (2 +ℕ n))) → ∘inr (invMap x) ≡ x
  ∘inrSect = sElim (λ _ → isOfHLevelPath 2 squash₂ _ _)
               λ a → refl

  ∘inrRetr : (x : (coHom (2 +ℕ n) (C* n f g))) → invMap (∘inr x) ≡ x
  ∘inrRetr =
    sElim (λ _ → isOfHLevelPath 2 squash₂ _ _)
      λ h → cong ∣_∣₂ (funExt λ { (inl x) → cong h ((λ i →  inr ((snd f) (~ i)))
                                                  ∙ sym (push (inl north)))
                                ; (inr x) → refl
                                ; (push a i) → lem₁ h a i})
    where
    lem₁ : (h : C* n f g → coHomK (2 +ℕ n)) (a : _)
         → PathP (λ i → invMapPrim (h ∘ inr) (push a i) ≡ h (push a i))
                  (cong h ((λ i →  inr ((snd f) (~ i)))
                         ∙ sym (push (inl north))))
                  refl
    lem₁ h =
      WedgeElim n (λ _ → isOfHLevelPathP (3 +ℕ n)
                           (isOfHLevelTrunc (4 +ℕ n) _ _) _ _)
         lem₂

      where
      lem₂ : PathP (λ i → invMapPrim (h ∘ inr) (push (inl north) i)
                         ≡ h (push (inl north) i))
                  (cong h ((λ i →  inr ((snd f) (~ i)))
                          ∙ sym (push (inl north))))
                  refl
      lem₂ i j = h (hcomp (λ k → λ { (i = i1) → inr (fst f north)
                                     ; (j = i0) → inr (snd f (~ i))
                                     ; (j = i1) → push (inl north) (i ∨ ~ k)})
                           (inr (snd f (~ i ∧ ~ j))))

  is : GroupIso (coHomGr (2 +ℕ n) (C* n f g)) (coHomGr (2 +ℕ n) (S₊ (2 +ℕ n)))
  Iso.fun (fst is) = ∘inr
  Iso.inv (fst is) = invMap
  Iso.rightInv (fst is) = ∘inrSect
  Iso.leftInv (fst is) = ∘inrRetr
  snd is = makeIsGroupHom
    (sElim2 (λ _ _ → isOfHLevelPath 2 squash₂ _ _)
           λ f g → refl)

H⁴-C·Π : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
        → GroupIso (coHomGr ((2 +ℕ n) +' (2 +ℕ n)) (C·Π n f g))
                    ℤGroup
H⁴-C·Π n f g = compGroupIso
  (transportCohomIso (cong (3 +ℕ_) (+-suc n n))) (Hopfβ-Iso n (∙Π f g))

H²-C·Π : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
      → GroupIso (coHomGr (2 +ℕ n) (C·Π n f g))
                  ℤGroup
H²-C·Π n f g = Hopfα-Iso n (∙Π f g)

H⁴-C* : ∀ n → (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
     → GroupIso (coHomGr ((2 +ℕ n) +' (2 +ℕ n)) (C* n f g))
                 (DirProd ℤGroup ℤGroup)
H⁴-C* n f g =
  compGroupIso (GroupEquiv→GroupIso (invGroupEquiv fstIso))
                  (compGroupIso (transportCohomIso (cong (2 +ℕ_) (+-suc n n)))
                    (compGroupIso (Hⁿ-⋁ (S₊∙ (suc (suc (suc (n +ℕ n)))))
                                        (S₊∙ (suc (suc (suc (n +ℕ n))))) _)
                      (GroupIsoDirProd (Hⁿ-Sⁿ≅ℤ _) (Hⁿ-Sⁿ≅ℤ _))))
  where
  module Ms = MV _ _ _ (λ _ → tt) (fst (∨→∙ f g))
  fstIso : GroupEquiv (coHomGr ((suc (suc (n +ℕ suc n))))
                               (S₊∙ (3 +ℕ (n +ℕ n)) ⋁ S₊∙ (3 +ℕ (n +ℕ n))))
                      (coHomGr ((2 +ℕ n) +' (2 +ℕ n)) (C* n f g))
  fst (fst fstIso) = Ms.d (suc (suc (n +ℕ suc n))) .fst
  snd (fst fstIso) =
    SES→isEquiv
      (sym (fst (GroupPath _ _)
        (isContr→≃Unit
          (isContrΣ (subst isContr (isoToPath (invIso (fst (Hⁿ-Unit≅0 _))))
                           isContrUnit)
             (λ _ → subst isContr (isoToPath (invIso
                           (fst (Hⁿ-Sᵐ≅0 _ _ λ p →
                                   ¬lemHopf 0 ((λ _ → north) , refl)
                                   n n (cong suc (sym (+-suc n n)) ∙ p)))))
                                   isContrUnit))
       , makeIsGroupHom λ _ _ → refl)))
      (GroupPath _ _ .fst
        (compGroupEquiv (invEquiv (isContr→≃Unit ((tt , tt) , (λ _ → refl)))
                                   , makeIsGroupHom λ _ _ → refl)
            (GroupEquivDirProd
             (GroupIso→GroupEquiv (invGroupIso (Hⁿ-Unit≅0 _)))
             (GroupIso→GroupEquiv
               (invGroupIso (Hⁿ-Sᵐ≅0 _ _ λ p →
                              ¬lemHopf 0 ((λ _ → north) , refl)
                              n (suc n) (cong (2 +ℕ_) (sym (+-suc n n))
                                                      ∙ p)))))))
      (Ms.Δ (suc (suc (n +ℕ suc n))))
      (Ms.d (suc (suc (n +ℕ suc n))))
      (Ms.i (suc (suc (suc (n +ℕ suc n)))))
      (Ms.Ker-d⊂Im-Δ _)
      (Ms.Ker-i⊂Im-d _)
  snd fstIso = Ms.d (suc (suc (n +ℕ suc n))) .snd

module _ (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n)) where

  C·Π' = C·Π n f g

  -- The generators of the cohomology groups of C* and C·Π

  βₗ : coHom ((2 +ℕ n) +' (2 +ℕ n)) (C* n f g)
  βₗ = Iso.inv (fst (H⁴-C* n f g)) (1 , 0)

  βᵣ : coHom ((2 +ℕ n) +' (2 +ℕ n)) (C* n f g)
  βᵣ = Iso.inv (fst (H⁴-C* n f g)) (0 , 1)

  β·Π : coHom ((2 +ℕ n) +' (2 +ℕ n)) C·Π'
  β·Π = Iso.inv (fst (H⁴-C·Π n f g)) 1

  α* : coHom (2 +ℕ n) (C* n f g)
  α* = Iso.inv (fst (H²C*≅ℤ n f g)) 1

  -- They can be difficult to work with in this form.
  -- We give them simpler forms and prove that these are equivalent
  βᵣ'-fun : C* n f g → coHomK ((4 +ℕ (n +ℕ n)))
  βᵣ'-fun (inl x) = 0ₖ _
  βᵣ'-fun (inr x) = 0ₖ _
  βᵣ'-fun (push (inl x) i₁) = 0ₖ _
  βᵣ'-fun (push (inr x) i₁) = Kn→ΩKn+1 _ ∣ x ∣ i₁
  βᵣ'-fun (push (push a i₂) i₁) = Kn→ΩKn+10ₖ _ (~ i₂) i₁

  βₗ'-fun : C* n f g → coHomK (4 +ℕ (n +ℕ n))
  βₗ'-fun (inl x) = 0ₖ _
  βₗ'-fun (inr x) = 0ₖ _
  βₗ'-fun (push (inl x) i₁) = Kn→ΩKn+1 _ ∣ x ∣ i₁
  βₗ'-fun (push (inr x) i₁) = 0ₖ _
  βₗ'-fun (push (push a i₂) i₁) = Kn→ΩKn+10ₖ _ i₂ i₁

  βₗ''-fun : coHom ((2 +ℕ n) +' (2 +ℕ n)) (C* n f g)
  βₗ''-fun = subst (λ m → coHom m (C* n f g))
                   (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
                   ∣ βₗ'-fun ∣₂

  βᵣ''-fun : coHom ((2 +ℕ n) +' (2 +ℕ n)) (C* n f g)
  βᵣ''-fun = subst (λ m → coHom m (C* n f g))
                   (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
                   ∣ βᵣ'-fun ∣₂

  private
    0ₖ≡∨→ : (a : _) → 0ₖ (suc (suc n)) ≡ ∣ (f ∨→ g) a ∣
    0ₖ≡∨→ = WedgeElim _ (λ _ → isOfHLevelTrunc (4 +ℕ n) _ _)
                   (cong ∣_∣ₕ (sym (snd f)))

  0ₖ≡∨→-inr : 0ₖ≡∨→ (inr north) ≡ cong ∣_∣ₕ (sym (snd g))
  0ₖ≡∨→-inr =
    (λ j → transport (λ i →  0ₖ (2 +ℕ n)
                          ≡ ∣ compPath-filler' (snd f) (sym (snd g)) (~ j) i ∣ₕ)
                      λ i → ∣ snd f (~ i ∨ j) ∣ₕ)
         ∙ λ j → transp (λ i → 0ₖ (2 +ℕ n) ≡ ∣ snd g (~ i ∧ ~ j) ∣ₕ) j
                         λ i → ∣ snd g (~ i ∨ ~ j) ∣ₕ

  α*'-fun : C* n f g → coHomK (2 +ℕ n)
  α*'-fun (inl x) = 0ₖ _
  α*'-fun (inr x) = ∣ x ∣
  α*'-fun (push a i₁) = 0ₖ≡∨→ a i₁

  α*' : coHom (2 +ℕ n) (C* n f g)
  α*' = ∣ α*'-fun ∣₂

  -- We also need the following three maps
  jₗ : HopfInvariantPush n (fst f) → C* n f g
  jₗ (inl x) = inl x
  jₗ (inr x) = inr x
  jₗ (push a i₁) = push (inl a) i₁

  jᵣ : HopfInvariantPush n (fst g) → C* n f g
  jᵣ (inl x) = inl x
  jᵣ (inr x) = inr x
  jᵣ (push a i₁) = push (inr a) i₁

  q : C·Π' → C* n f g
  q (inl x) = inl x
  q (inr x) = inr x
  q (push a i₁) = (push (θ a) ∙ λ i → inr (∙Π≡∨→∘θ n f g a (~ i))) i₁

α↦1 : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
        → Iso.fun (fst (H²C*≅ℤ n f g)) (α*' n f g) ≡ 1
α↦1 n f g =
     sym (cong (Iso.fun (fst (Hⁿ-Sⁿ≅ℤ (suc n)))) (Hⁿ-Sⁿ≅ℤ-nice-generator n))
   ∙ Iso.rightInv (fst (Hⁿ-Sⁿ≅ℤ (suc n))) (pos 1)

α≡ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
        → α* n f g ≡ α*' n f g
α≡ n f g = sym (Iso.leftInv (fst (H²C*≅ℤ n f g)) _)
             ∙∙ cong (Iso.inv (fst (H²C*≅ℤ n f g))) lem
             ∙∙ Iso.leftInv (fst (H²C*≅ℤ n f g)) _
  where
  lem : Iso.fun (fst (H²C*≅ℤ n f g)) (α* n f g)
       ≡ Iso.fun (fst (H²C*≅ℤ n f g)) (α*' n f g)
  lem = (Iso.rightInv (fst (H²C*≅ℤ n f g)) (pos 1)) ∙ sym (α↦1 n f g)

q-α : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → coHomFun _ (q n f g) (α* n f g) ≡ Hopfα n (∙Π f g)
q-α n f g = (λ i → coHomFun _ (q n f g) (α≡ n f g i))
          ∙ sym (Iso.leftInv is _)
          ∙∙ cong (Iso.inv is) lem
          ∙∙ Iso.leftInv is _
  where
  is = fst (Hopfα-Iso n (∙Π f g))

  lem : Iso.fun is (coHomFun _ (q n f g) (α*' n f g))
       ≡ Iso.fun is (Hopfα n (∙Π f g))
  lem = sym (cong (Iso.fun (fst (Hⁿ-Sⁿ≅ℤ (suc n)))) (Hⁿ-Sⁿ≅ℤ-nice-generator n))
      ∙∙ Iso.rightInv (fst (Hⁿ-Sⁿ≅ℤ (suc n))) (pos 1)
      ∙∙ sym (Hopfα-Iso-α n (∙Π f g))

βₗ≡ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → βₗ n f g ≡ βₗ''-fun n f g
βₗ≡ n f g = cong (∨-d ∘ subber₂)
                 (cong (Iso.inv (fst ((Hⁿ-⋁ (S₊∙ (suc (suc (suc (n +ℕ n)))))
                                            (S₊∙ (suc (suc (suc (n +ℕ n)))))
                                (((suc (suc (n +ℕ n))))))))) help
               ∙ lem)
          ∙ funExt⁻ ∨-d∘subber ∣ wedgeGen ∣₂
          ∙ cong subber₃ (sym βₗ'-fun≡)
  where
  ∨-d = MV.d _ _ _ (λ _ → tt) (fst (∨→∙ f g)) (suc (suc (n +ℕ suc n))) .fst
  ∨-d' = MV.d _ _ _ (λ _ → tt) (fst (∨→∙ f g)) (suc (suc (suc (n +ℕ n)))) .fst

  help : Iso.inv (fst (GroupIsoDirProd
                        (Hⁿ-Sⁿ≅ℤ (suc (suc (n +ℕ n))))
                        (Hⁿ-Sⁿ≅ℤ (suc (suc (n +ℕ n)))))) (1 , 0)
       ≡ (∣ ∣_∣ ∣₂ , 0ₕ _)
  help = ΣPathP ((Hⁿ-Sⁿ≅ℤ-nice-generator _)
       , IsGroupHom.pres1 (snd (invGroupIso (Hⁿ-Sⁿ≅ℤ (suc (suc (n +ℕ n)))))))

  subber₃ = subst (λ m → coHom m (C* n f g))
                  (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))

  subber₂ = (subst (λ m → coHom m (S₊∙ (suc (suc (suc (n +ℕ n))))
                                  ⋁ S₊∙ (suc (suc (suc (n +ℕ n))))))
                   (sym (cong (2 +ℕ_) (+-suc n n))))

  ∨-d∘subber : ∨-d ∘ subber₂ ≡ subber₃ ∘ ∨-d'
  ∨-d∘subber =
    funExt (λ a →
      (λ j → transp (λ i → coHom ((suc ∘ suc ∘ suc) (+-suc n n (~ i ∧ j)))
                                  (C* n f g)) (~ j)
               (MV.d _ _ _ (λ _ → tt) (fst (∨→∙ f g))
                           (suc (suc (+-suc n n j))) .fst
                  (transp (λ i → coHom (2 +ℕ (+-suc n n (j ∨ ~ i)))
                                          (S₊∙ (suc (suc (suc (n +ℕ n))))
                                         ⋁ S₊∙ (suc (suc (suc (n +ℕ n)))))) j
                          a))))

  wedgeGen : (S₊∙ (suc (suc (suc (n +ℕ n)))) ⋁ S₊∙ (suc (suc (suc (n +ℕ n))))
           → coHomK (suc (suc (suc (n +ℕ n)))))
  wedgeGen (inl x) = ∣ x ∣
  wedgeGen (inr x) = 0ₖ _
  wedgeGen (push a i₁) = 0ₖ _

  βₗ'-fun≡ : ∣ βₗ'-fun n f g ∣₂ ≡ ∨-d' ∣ wedgeGen ∣₂
  βₗ'-fun≡ =
    cong ∣_∣₂ (funExt λ { (inl x) → refl
                       ; (inr x) → refl
                       ; (push (inl x) i₁) → refl
                       ; (push (inr x) i₁) j → Kn→ΩKn+10ₖ _ (~ j) i₁
                       ; (push (push a i₂) i₁) j → Kn→ΩKn+10ₖ _ (~ j ∧ i₂) i₁})

  lem : Iso.inv (fst (Hⁿ-⋁ (S₊∙ (suc (suc (suc (n +ℕ n)))))
                           (S₊∙ (suc (suc (suc (n +ℕ n)))))
                           (((suc (suc (n +ℕ n)))))))
                   (∣ ∣_∣ ∣₂ , 0ₕ _)
                   ≡ ∣ wedgeGen ∣₂
  lem = cong ∣_∣₂ (funExt λ { (inl x) → rUnitₖ (suc (suc (suc (n +ℕ n)))) ∣ x ∣
                           ; (inr x) → refl
                           ; (push a i₁) → refl})

βᵣ≡ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → βᵣ n f g ≡ βᵣ''-fun n f g
βᵣ≡ n f g =
    cong (∨-d ∘ subber₂)
      (cong (Iso.inv (fst (Hⁿ-⋁ (S₊∙ (suc (suc (suc (n +ℕ n)))))
                                 (S₊∙ (suc (suc (suc (n +ℕ n)))))
                                (suc (suc (n +ℕ n))))))
            help
          ∙ lem)
  ∙ funExt⁻ ∨-d∘subber ∣ wedgeGen ∣₂
  ∙ cong (subber₃) (sym βᵣ'-fun≡)
  where
  ∨-d = MV.d _ _ _ (λ _ → tt) (fst (∨→∙ f g)) (suc (suc (n +ℕ suc n))) .fst
  ∨-d' = MV.d _ _ _ (λ _ → tt) (fst (∨→∙ f g)) (suc (suc (suc (n +ℕ n)))) .fst

  help : Iso.inv (fst (GroupIsoDirProd
                       (Hⁿ-Sⁿ≅ℤ (suc (suc (n +ℕ n))))
                       (Hⁿ-Sⁿ≅ℤ (suc (suc (n +ℕ n)))))) (0 , 1)
       ≡ (0ₕ _ , ∣ ∣_∣ ∣₂)
  help =
    ΣPathP (IsGroupHom.pres1 (snd (invGroupIso (Hⁿ-Sⁿ≅ℤ (suc (suc (n +ℕ n))))))
         , (Hⁿ-Sⁿ≅ℤ-nice-generator _))

  subber₃ = subst (λ m → coHom m (C* n f g))
                  (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))

  subber₂ = (subst (λ m → coHom m (S₊∙ (suc (suc (suc (n +ℕ n))))
                                 ⋁ S₊∙ (suc (suc (suc (n +ℕ n))))))
                   (sym (cong (2 +ℕ_) (+-suc n n))))

  ∨-d∘subber : ∨-d ∘ subber₂ ≡ subber₃ ∘ ∨-d'
  ∨-d∘subber =
    funExt (λ a →
      (λ j → transp (λ i → coHom ((suc ∘ suc ∘ suc) (+-suc n n (~ i ∧ j)))
                     (C* n f g)) (~ j)
               (MV.d _ _ _ (λ _ → tt)
                 (fst (∨→∙ f g)) (suc (suc (+-suc n n j))) .fst
                  (transp (λ i → coHom (2 +ℕ (+-suc n n (j ∨ ~ i)))
                                        (S₊∙ (suc (suc (suc (n +ℕ n))))
                                       ⋁ S₊∙ (suc (suc (suc (n +ℕ n)))))) j
                          a))))

  wedgeGen : (S₊∙ (suc (suc (suc (n +ℕ n)))) ⋁ S₊∙ (suc (suc (suc (n +ℕ n))))
      → coHomK (suc (suc (suc (n +ℕ n)))))
  wedgeGen (inl x) = 0ₖ _
  wedgeGen (inr x) = ∣ x ∣
  wedgeGen (push a i₁) = 0ₖ _

  lem : Iso.inv (fst ((Hⁿ-⋁ (S₊∙ (suc (suc (suc (n +ℕ n)))))
                            (S₊∙ (suc (suc (suc (n +ℕ n)))))
                            (suc (suc (n +ℕ n))))))
                   (0ₕ _ , ∣ ∣_∣ ∣₂)
                   ≡ ∣ wedgeGen ∣₂
  lem = cong ∣_∣₂ (funExt λ { (inl x) → refl
                          ; (inr x) → lUnitₖ (suc (suc (suc (n +ℕ n)))) ∣ x ∣
                          ; (push a i₁) → refl})

  βᵣ'-fun≡ : ∣ βᵣ'-fun n f g ∣₂ ≡ ∨-d' ∣ wedgeGen ∣₂
  βᵣ'-fun≡ =
    cong ∣_∣₂ (funExt
      λ { (inl x) → refl
         ; (inr x) → refl
         ; (push (inl x) i₁) j → Kn→ΩKn+10ₖ _ (~ j) i₁
         ; (push (inr x) i₁) → refl
         ; (push (push a i₂) i₁) j → Kn→ΩKn+10ₖ _ (~ j ∧ ~ i₂) i₁})

q-βₗ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → coHomFun _ (q n f g) (βₗ n f g)
    ≡ β·Π n f g
q-βₗ n f g = cong (coHomFun _ (q n f g)) (βₗ≡ n f g)
           ∙ transportLem
           ∙ cong (subst (λ m → coHom m (C·Π' n f g))
                  (cong (suc ∘ suc ∘ suc) (sym (+-suc n n))))
                        (sym (Iso.leftInv (fst (Hopfβ-Iso n (∙Π f g)))
                                               (Hopfβ n (∙Π f g)))
                        ∙ cong (Iso.inv ((fst (Hopfβ-Iso n (∙Π f g)))))
                               (Hopfβ↦1 n (∙Π f g)))
  where
  transportLem : coHomFun (suc (suc (suc (n +ℕ suc n)))) (q n f g)
      (βₗ''-fun n f g)
     ≡ subst (λ m → coHom m (C·Π' n f g))
             (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
             (Hopfβ n (∙Π f g))
  transportLem =
      natTranspLem ∣ βₗ'-fun n f g ∣₂ (λ m → coHomFun m (q n f g))
           (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
    ∙ cong (subst (λ m → coHom m (C·Π' n f g))
      (cong (suc ∘ suc ∘ suc) (sym (+-suc n n))))
        (cong ∣_∣₂ (funExt λ { (inl x) → refl
                            ; (inr x) → refl
                            ; (push a i₁) → push-lem a i₁}))
    where
    push-lem : (x : fst (S₊∙ (3 +ℕ (n +ℕ n)))) →
      PathP (λ i₁ → βₗ'-fun n f g ((push (θ x)
                   ∙ λ i → inr (∙Π≡∨→∘θ n f g x (~ i))) i₁)
                 ≡ MV.d-pre Unit (fst (S₊∙ (2 +ℕ n))) (fst (S₊∙ (3 +ℕ n +ℕ n)))
                     (λ _ → tt) (fst (∙Π f g)) (3 +ℕ n +ℕ n) ∣_∣ (push x i₁))
      (λ _ → βₗ'-fun n f g (q n f g (inl tt)))
      (λ _ → βₗ'-fun n f g (q n f g (inr (∙Π f g .fst x))))
    push-lem x =
      flipSquare (cong-∙ (βₗ'-fun n f g)
                         (push (θ x)) (λ i → inr (∙Π≡∨→∘θ n f g x (~ i)))
              ∙∙ sym (rUnit _)
              ∙∙ lem₁ x)

      where
      lem₁ : (x : _) → cong (βₗ'-fun n f g) (push (θ {A = S₊∙ _} x))
                      ≡ Kn→ΩKn+1 _ ∣ x ∣
      lem₁ north = refl
      lem₁ south =
          sym (Kn→ΩKn+10ₖ _)
        ∙ cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n))))) (cong ∣_∣ₕ (merid north))
      lem₁ (merid a j) k = merid-lem k j
        where
        p = pt (S₊∙ (suc (suc (n +ℕ n))))

        merid-lem :
          PathP (λ k → Kn→ΩKn+1 _ ∣ north ∣ₕ
                      ≡ (sym (Kn→ΩKn+10ₖ _)
                       ∙ cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))))
                              (cong ∣_∣ₕ (merid north))) k)
             (λ j → cong (βₗ'-fun n f g) (push (θ {A = S₊∙ _} (merid a j))))
             (cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))) ∘ ∣_∣ₕ) (merid a))
        merid-lem =
           (cong-∙∙ (cong (βₗ'-fun n f g) ∘ push)
                          (λ i₁ → inl ((merid a ∙ (sym (merid p))) i₁))
                    (push tt)
                         ((λ i₁ → inr ((merid a ∙ (sym (merid p))) i₁)))
         ∙ sym (compPath≡compPath' _ _)
         ∙ cong (_∙ Kn→ΩKn+10ₖ _)
                (cong-∙ ((Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))) ∘ ∣_∣ₕ))
                        (merid a) (sym (merid north)))
         ∙ sym (assoc _ _ _)
         ∙ cong (cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))) ∘ ∣_∣ₕ) (merid a) ∙_)
                (sym (symDistr (sym ((Kn→ΩKn+10ₖ _)))
                       (cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))))
                             (cong ∣_∣ₕ (merid north))))))
         ◁ λ i j → compPath-filler
           (cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))) ∘ ∣_∣ₕ) (merid a))
                 (sym (sym (Kn→ΩKn+10ₖ _)
                 ∙ cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))))
                        (cong ∣_∣ₕ (merid north))))
                 (~ i) j

q-βᵣ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → coHomFun _ (q n f g) (βᵣ n f g)
    ≡ β·Π n f g
q-βᵣ n f g = cong (coHomFun _ (q n f g)) (βᵣ≡ n f g)
           ∙ transportLem
           ∙ cong (subst (λ m → coHom m (C·Π' n f g))
                  (cong (suc ∘ suc ∘ suc) (sym (+-suc n n))))
                        (sym (Iso.leftInv (fst (Hopfβ-Iso n (∙Π f g)))
                                               (Hopfβ n (∙Π f g)))
                        ∙ cong (Iso.inv ((fst (Hopfβ-Iso n (∙Π f g)))))
                               (Hopfβ↦1 n (∙Π f g)))
  where
  transportLem : coHomFun (suc (suc (suc (n +ℕ suc n)))) (q n f g)
      (βᵣ''-fun n f g)
     ≡ subst (λ m → coHom m (C·Π' n f g))
             (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
             (Hopfβ n (∙Π f g))
  transportLem =
      natTranspLem ∣ βᵣ'-fun n f g ∣₂ (λ m → coHomFun m (q n f g))
            (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
    ∙ cong (subst (λ m → coHom m (C·Π' n f g))
      (cong (suc ∘ suc ∘ suc) (sym (+-suc n n))))
        (cong ∣_∣₂ (funExt λ { (inl x) → refl
                            ; (inr x) → refl
                            ; (push a i₁) → push-lem a i₁}))
    where
    push-lem : (x : fst (S₊∙ (3 +ℕ (n +ℕ n)))) →
      PathP
      (λ i₁ →
         βᵣ'-fun n f g ((push (θ x) ∙ λ i → inr (∙Π≡∨→∘θ n f g x (~ i))) i₁) ≡
         MV.d-pre Unit (fst (S₊∙ (2 +ℕ n))) (fst (S₊∙ (3 +ℕ n +ℕ n)))
         (λ _ → tt) (fst (∙Π f g)) (3 +ℕ n +ℕ n) ∣_∣ (push x i₁))
      (λ _ → βᵣ'-fun n f g (q n f g (inl tt)))
      (λ _ → βᵣ'-fun n f g (q n f g (inr (∙Π f g .fst x))))
    push-lem x = flipSquare (cong-∙ (βᵣ'-fun n f g) (push (θ x))
                                    (λ i → inr (∙Π≡∨→∘θ n f g x (~ i)))
              ∙∙ sym (rUnit _)
              ∙∙ lem₁ x)
      where
      lem₁ : (x : _) → cong (βᵣ'-fun n f g) (push (θ {A = S₊∙ _} x))
                      ≡ Kn→ΩKn+1 _ ∣ x ∣
      lem₁ north = sym (Kn→ΩKn+10ₖ _)
      lem₁ south = cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))))
                        (cong ∣_∣ₕ (merid north))
      lem₁ (merid a j) k = merid-lem k j
        where
        p = pt (S₊∙ (suc (suc (n +ℕ n))))

        merid-lem :
          PathP (λ k → (Kn→ΩKn+10ₖ _) (~ k)
                      ≡ (cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))))
                              (cong ∣_∣ₕ (merid north))) k)
                (λ j → cong (βᵣ'-fun n f g) (push (θ {A = S₊∙ _} (merid a j))))
                (cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))) ∘ ∣_∣ₕ) (merid a))
        merid-lem =
          (cong-∙∙ (cong (βᵣ'-fun n f g) ∘ push)
                        (λ i₁ → inl ((merid a
                                    ∙ (sym (merid p))) i₁))
                        (push tt)
                        (λ i₁ → inr ((merid a
                                    ∙ (sym (merid p))) i₁))
             ∙∙ (cong (sym (Kn→ΩKn+10ₖ _) ∙_)
                      (cong-∙ (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))) ∘ ∣_∣ₕ)
                              (merid a) (sym (merid (ptSn _)))))
             ∙∙ sym (doubleCompPath≡compPath _ _ _))
             ◁ symP (doubleCompPath-filler
                      (sym (Kn→ΩKn+10ₖ _))
                      (cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))) ∘ ∣_∣ₕ)
                            (merid a))
                      (cong (Kn→ΩKn+1 (suc (suc (suc (n +ℕ n)))) ∘ ∣_∣ₕ)
                            (sym (merid north))))

jₗ-α : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → coHomFun _ (jₗ n f g) (α* n f g)
    ≡ Hopfα n f
jₗ-α n f g =
    cong (coHomFun _ (jₗ n f g)) (α≡ n f g)
  ∙ cong ∣_∣₂ (funExt (HopfInvariantPushElim n (fst f)
                      (isOfHLevelPath ((suc (suc (suc (suc (n +ℕ n))))))
                        (isOfHLevelPlus' {n = n} (4 +ℕ n)
                          (isOfHLevelTrunc (4 +ℕ n))) _ _)
                      refl
                      (λ _ → refl)
                      λ j → refl))

jₗ-βₗ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → coHomFun _ (jₗ n f g) (βₗ n f g)
    ≡ subst (λ m → coHom m (HopfInvariantPush n (fst f)))
            (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
            (Hopfβ n f)
jₗ-βₗ n f g =
     cong (coHomFun _ (jₗ n f g)) (βₗ≡ n f g)
  ∙∙ natTranspLem ∣ βₗ'-fun n f g ∣₂ (λ m → coHomFun m (jₗ n f g))
          (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
  ∙∙ cong (subst (λ m → coHom m (HopfInvariantPush n (fst f)))
      (cong (suc ∘ suc ∘ suc) (sym (+-suc n n))))
        (cong ∣_∣₂
          (funExt λ { (inl x) → refl
                    ; (inr x) → refl
                    ; (push a i₁) → refl}))

jₗ-βᵣ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → coHomFun _ (jₗ n f g) (βᵣ n f g)
    ≡ 0ₕ _
jₗ-βᵣ n f g =
  cong (coHomFun _ (jₗ n f g)) (βᵣ≡ n f g)
  ∙∙ natTranspLem ∣ βᵣ'-fun n f g ∣₂ (λ m → coHomFun m (jₗ n f g))
          (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
  ∙∙ cong (subst (λ m → coHom m (HopfInvariantPush n (fst f)))
      (cong (suc ∘ suc ∘ suc) (sym (+-suc n n))))
      cool
  where
  cool : coHomFun (suc (suc (suc (suc (n +ℕ n))))) (jₗ n f g)
      ∣ βᵣ'-fun n f g ∣₂ ≡ 0ₕ _
  cool = cong ∣_∣₂ (funExt λ { (inl x) → refl
                            ; (inr x) → refl
                            ; (push a i₁) → refl})

jᵣ-α : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → coHomFun _ (jᵣ n f g) (α* n f g)
    ≡ Hopfα n g
jᵣ-α n f g = cong (coHomFun _ (jᵣ n f g)) (α≡ n f g)
  ∙ cong ∣_∣₂ (funExt (HopfInvariantPushElim n (fst g)
                      (isOfHLevelPath ((suc (suc (suc (suc (n +ℕ n))))))
                        (isOfHLevelPlus' {n = n} (4 +ℕ n)
                          (isOfHLevelTrunc (4 +ℕ n))) _ _)
                      refl
                      (λ _ → refl)
                      (flipSquare (0ₖ≡∨→-inr n f g))))

jᵣ-βₗ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → coHomFun _ (jᵣ n f g) (βₗ n f g) ≡ 0ₕ _
jᵣ-βₗ n f g =
  cong (coHomFun _ (jᵣ n f g)) (βₗ≡ n f g)
  ∙∙ natTranspLem ∣ βₗ'-fun n f g ∣₂ (λ m → coHomFun m (jᵣ n f g))
            (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
  ∙∙ cong (subst (λ m → coHom m (HopfInvariantPush n (fst f)))
      (cong (suc ∘ suc ∘ suc) (sym (+-suc n n))))
      cool
  where
  cool : coHomFun (suc (suc (suc (suc (n +ℕ n))))) (jᵣ n f g)
      ∣ βₗ'-fun n f g ∣₂ ≡ 0ₕ _
  cool = cong ∣_∣₂ (funExt λ { (inl x) → refl
                            ; (inr x) → refl
                            ; (push a i₁) → refl})

jᵣ-βᵣ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
    → coHomFun _ (jᵣ n f g) (βᵣ n f g)
    ≡ subst (λ m → coHom m (HopfInvariantPush n (fst g)))
            (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
            (Hopfβ n g)
jᵣ-βᵣ n f g =
  cong (coHomFun _ (jᵣ n f g)) (βᵣ≡ n f g)
  ∙∙ natTranspLem ∣ βᵣ'-fun n f g ∣₂ (λ m → coHomFun m (jᵣ n f g))
            (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
  ∙∙  cong (subst (λ m → coHom m (HopfInvariantPush n (fst g)))
      (cong (suc ∘ suc ∘ suc) (sym (+-suc n n))))
        (cong ∣_∣₂
          (funExt λ { (inl x) → refl
                    ; (inr x) → refl
                    ; (push a i₁) → refl}))

genH²ⁿC* : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
         → gen₂-by (coHomGr ((2 +ℕ n) +' (2 +ℕ n)) (C* n f g))
                    (βₗ n f g)
                    (βᵣ n f g)
genH²ⁿC* n f g =
  Iso-pres-gen₂ (DirProd ℤGroup ℤGroup)
    (coHomGr ((2 +ℕ n) +' (2 +ℕ n)) (C* n f g))
    (1 , 0)
    (0 , 1)
    gen₂ℤ×ℤ
    (invGroupIso (H⁴-C* n f g))

private
  H⁴C* : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n)) → Group _
  H⁴C* n f g = coHomGr ((2 +ℕ n) +' (2 +ℕ n)) (C* n f g)

  X : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
      → ℤ
  X n f g = (genH²ⁿC* n f g) (α* n f g ⌣ α* n f g) .fst .fst

  Y : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
      → ℤ
  Y n  f g = (genH²ⁿC* n f g) (α* n f g ⌣ α* n f g) .fst .snd

  α*≡⌣ : (n : ℕ) (f g : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
      → α* n f g ⌣ α* n f g ≡ ((X n f g) ℤ[ H⁴C* n f g ]· βₗ n f g)
                            +ₕ ((Y n f g) ℤ[ H⁴C* n f g ]· βᵣ n f g)
  α*≡⌣ n f g = (genH²ⁿC* n f g) (α* n f g ⌣ α* n f g) .snd

coHomFun⌣ : ∀ {ℓ ℓ'} {A : Type ℓ} {B : Type ℓ'} (f : A → B)
          → (n : ℕ) (x y : coHom n B)
          → coHomFun _ f (x ⌣ y) ≡ coHomFun _ f x ⌣ coHomFun _ f y
coHomFun⌣ f n = sElim2 (λ _ _ → isOfHLevelPath 2 squash₂ _ _) λ _ _ → refl

isHom-HopfInvariant :
          (n : ℕ) (f g : S₊∙ (suc (suc (suc (n +ℕ n)))) →∙ S₊∙ (suc (suc n)))
        → HopfInvariant n (∙Π f g) ≡ HopfInvariant n f + HopfInvariant n g
isHom-HopfInvariant n f g =
      mainL
   ∙ sym (cong₂ _+_ f-id g-id)
  where
  eq₁ : (Hopfα n (∙Π f g)) ⌣ (Hopfα n (∙Π f g))
      ≡ ((X n f g + Y n f g) ℤ[ coHomGr _ _ ]· (β·Π n f g))
  eq₁ = cong (λ x → x ⌣ x) (sym (q-α n f g)
                           ∙ cong (coHomFun (suc (suc n)) (q n f g)) (α≡ n f g))
      ∙ cong ((coHomFun _) (q _ f g)) (cong (λ x → x ⌣ x) (sym (α≡ n f g))
                           ∙ α*≡⌣ n f g)
      ∙ IsGroupHom.pres· (coHomMorph _ (q n f g) .snd) _ _
      ∙ cong₂ _+ₕ_ (homPresℤ· (coHomMorph _ (q n f g)) (βₗ n f g) (X n f g)
                           ∙ cong (λ z → (X n f g) ℤ[ coHomGr _ _ ]· z)
                                  (q-βₗ n f g))
                   (homPresℤ· (coHomMorph _ (q n f g)) (βᵣ n f g) (Y n f g)
                           ∙ cong (λ z → (Y n f g) ℤ[ coHomGr _ _ ]· z)
                                  (q-βᵣ n f g))
      ∙ sym (distrℤ· (coHomGr _ _) (β·Π n f g) (X n f g) (Y n f g))

  eq₂ : Hopfα n f ⌣ Hopfα n f
      ≡ (X n f g ℤ[ coHomGr _ _ ]·
                   subst (λ m → coHom m (HopfInvariantPush n (fst f)))
                   (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
                   (Hopfβ n f))
  eq₂ = cong (λ x → x ⌣ x) (sym (jₗ-α n f g))
     ∙∙ sym (coHomFun⌣ (jₗ n f g) _ _ _)
     ∙∙ cong (coHomFun _ (jₗ n f g)) (α*≡⌣ n f g)
     ∙∙ IsGroupHom.pres· (snd (coHomMorph _ (jₗ n f g))) _ _
     ∙∙ cong₂ _+ₕ_ (homPresℤ· (coHomMorph _ (jₗ n f g)) (βₗ n f g) (X n f g))
                   (homPresℤ· (coHomMorph _ (jₗ n f g)) (βᵣ n f g) (Y n f g)
                             ∙ cong (λ z → (Y n f g) ℤ[ coHomGr _ _ ]· z)
                                    (jₗ-βᵣ n f g))
     ∙∙ cong₂ _+ₕ_ refl (rUnitℤ· (coHomGr _ _) (Y n f g))
     ∙∙ (rUnitₕ _ _
      ∙ cong (X n f g ℤ[ coHomGr _ _ ]·_) (jₗ-βₗ n f g))

  eq₃ : Hopfα n g ⌣ Hopfα n g
      ≡ (Y n f g ℤ[ coHomGr _ _ ]·
                   subst (λ m → coHom m (HopfInvariantPush n (fst f)))
                   (cong (suc ∘ suc ∘ suc) (sym (+-suc n n)))
                   (Hopfβ n g))
  eq₃ = cong (λ x → x ⌣ x) (sym (jᵣ-α n f g))
     ∙∙ sym (coHomFun⌣ (jᵣ n f g) _ _ _)
     ∙∙ cong (coHomFun _ (jᵣ n f g)) (α*≡⌣ n f g)
     ∙∙ IsGroupHom.pres· (snd (coHomMorph _ (jᵣ n f g))) _ _
     ∙∙ cong₂ _+ₕ_ (homPresℤ· (coHomMorph _ (jᵣ n f g)) (βₗ n f g) (X n f g)
                             ∙ cong (λ z → (X n f g) ℤ[ coHomGr _ _ ]· z)
                                    (jᵣ-βₗ n f g))
                   (homPresℤ· (coHomMorph _ (jᵣ n f g)) (βᵣ n f g) (Y n f g))
     ∙∙ cong₂ _+ₕ_ (rUnitℤ· (coHomGr _ _) (X n f g)) refl
     ∙∙ ((lUnitₕ _ _) ∙ cong (Y n f g ℤ[ coHomGr _ _ ]·_) (jᵣ-βᵣ n f g))

  transpLem : ∀ {ℓ} {G : ℕ → Group ℓ} (n m : ℕ) (x : ℤ) (p : m ≡ n)
              (g : fst (G n))
             → subst (fst ∘ G) p (x ℤ[ G m ]· subst (fst ∘ G) (sym p) g)
              ≡ (x ℤ[ G n ]· g)
  transpLem {G = G} n m x =
    J (λ n p → (g : fst (G n))
             → subst (fst ∘ G) p (x ℤ[ G m ]· subst (fst ∘ G) (sym p) g)
              ≡ (x ℤ[ G n ]· g))
      λ g → transportRefl _ ∙ cong (x ℤ[ G m ]·_) (transportRefl _)

  mainL : HopfInvariant n (∙Π f g) ≡ X n f g + Y n f g
  mainL =
       cong (Iso.fun (fst (Hopfβ-Iso n (∙Π f g))))
            (cong (subst (λ x → coHom x (HopfInvariantPush n (fst (∙Π f g))))
                         λ i₁ → suc (suc (suc (+-suc n n i₁))))
                  eq₁)
    ∙∙ cong (Iso.fun (fst (Hopfβ-Iso n (∙Π f g))))
        (transpLem {G = λ x → coHomGr x
                                (HopfInvariantPush n (fst (∙Π f g)))} _ _
                   (X n f g + Y n f g)
                   (λ i₁ → suc (suc (suc (+-suc n n i₁))))
                   (Iso.inv (fst (Hopfβ-Iso n (∙Π f g))) 1))
    ∙∙ homPresℤ· (_ , snd (Hopfβ-Iso n (∙Π f g)))
                 (Iso.inv (fst (Hopfβ-Iso n (∙Π f g))) 1)
                 (X n f g + Y n f g)
    ∙∙ cong ((X n f g + Y n f g) ℤ[ ℤ , ℤGroup .snd ]·_)
            (Iso.rightInv ((fst (Hopfβ-Iso n (∙Π f g)))) 1)
    ∙∙ rUnitℤ·ℤ (X n f g + Y n f g)

  f-id :  HopfInvariant n f ≡ X n f g
  f-id =
      cong (Iso.fun (fst (Hopfβ-Iso n f)))
      (cong (subst (λ x → coHom x (HopfInvariantPush n (fst f)))
            (λ i₁ → suc (suc (suc (+-suc n n i₁))))) eq₂)
    ∙ cong (Iso.fun (fst (Hopfβ-Iso n f)))
           (transpLem {G = λ x → coHomGr x
                           (HopfInvariantPush n (fst f))} _ _
                      (X n f g)
                      ((cong (suc ∘ suc ∘ suc) (+-suc n n)))
                      (Hopfβ n f))
    ∙ homPresℤ· (_ , snd (Hopfβ-Iso n f)) (Hopfβ n f) (X n f g)
    ∙ cong (X n f g ℤ[ ℤ , ℤGroup .snd ]·_) (Hopfβ↦1 n f)
    ∙ rUnitℤ·ℤ (X n f g)

  g-id : HopfInvariant n g ≡ Y n f g
  g-id =
       cong (Iso.fun (fst (Hopfβ-Iso n g)))
            (cong (subst (λ x → coHom x (HopfInvariantPush n (fst g)))
                      (λ i₁ → suc (suc (suc (+-suc n n i₁)))))
                      eq₃)
    ∙∙ cong (Iso.fun (fst (Hopfβ-Iso n g)))
                   (transpLem {G = λ x → coHomGr x
                                           (HopfInvariantPush n (fst g))} _ _
                              (Y n f g)
                              ((cong (suc ∘ suc ∘ suc) (+-suc n n)))
                              (Hopfβ n g))
    ∙∙ homPresℤ· (_ , snd (Hopfβ-Iso n g)) (Hopfβ n g) (Y n f g)
    ∙∙ cong (Y n f g ℤ[ ℤ , ℤGroup .snd ]·_) (Hopfβ↦1 n g)
    ∙∙ rUnitℤ·ℤ (Y n f g)

GroupHom-HopfInvariant-π' : (n : ℕ)
  → GroupHom (π'Gr (suc (suc (n +ℕ n))) (S₊∙ (suc (suc n)))) ℤGroup
fst (GroupHom-HopfInvariant-π' n) = HopfInvariant-π' n
snd (GroupHom-HopfInvariant-π' n) =
  makeIsGroupHom (sElim2 (λ _ _ → isOfHLevelPath 2 isSetℤ _ _)
                 (isHom-HopfInvariant n))

GroupHom-HopfInvariant-π : (n : ℕ)
  → GroupHom (πGr (suc (suc (n +ℕ n))) (S₊∙ (suc (suc n)))) ℤGroup
fst (GroupHom-HopfInvariant-π n) = HopfInvariant-π n
snd (GroupHom-HopfInvariant-π n) =
  makeIsGroupHom (sElim2 (λ _ _ → isOfHLevelPath 2 isSetℤ _ _)
    λ p q → cong (HopfInvariant-π' n)
                  (IsGroupHom.pres·
                    (snd (invGroupIso (π'Gr≅πGr (suc (suc (n +ℕ n)))
                                                (S₊∙ (suc (suc n))))))
                                    ∣ p ∣₂ ∣ q ∣₂)
          ∙ IsGroupHom.pres· (GroupHom-HopfInvariant-π' n .snd)
                             ∣ Ω→SphereMap _ p ∣₂ ∣ Ω→SphereMap _ q ∣₂)


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

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Weakening {{eqrel : EqRelSet}} where
open EqRelSet {{...}}

open import Definition.Untyped as U hiding (wk)
open import Definition.Untyped.Properties
open import Definition.Typed
open import Definition.Typed.Weakening as T hiding (wk; wkEq; wkTerm; wkEqTerm)
open import Definition.LogicalRelation
open import Definition.LogicalRelation.Irrelevance

open import Tools.Embedding
open import Tools.Product
import Tools.PropositionalEquality as PE

-- Weakening of neutrals in WHNF

wkTermNe : ∀ {ρ Γ Δ k A} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
         → Γ ⊩neNf k ∷ A → Δ ⊩neNf U.wk ρ k ∷ U.wk ρ A
wkTermNe {ρ} [ρ] ⊢Δ (neNfₜ neK ⊢k k≡k) =
  neNfₜ (wkNeutral ρ neK) (T.wkTerm [ρ] ⊢Δ ⊢k) (~-wk [ρ] ⊢Δ k≡k)

wkEqTermNe : ∀ {ρ Γ Δ k k′ A} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
           → Γ ⊩neNf k ≡ k′ ∷ A → Δ ⊩neNf U.wk ρ k ≡ U.wk ρ k′ ∷ U.wk ρ A
wkEqTermNe {ρ} [ρ] ⊢Δ (neNfₜ₌ neK neM k≡m) =
  neNfₜ₌ (wkNeutral ρ neK) (wkNeutral ρ neM) (~-wk [ρ] ⊢Δ k≡m)

-- Weakening of reducible natural numbers

mutual
  wkTermℕ : ∀ {ρ Γ Δ n} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
          → _⊩ℕ_∷ℕ Γ n → _⊩ℕ_∷ℕ Δ (U.wk ρ n)
  wkTermℕ {ρ} [ρ] ⊢Δ (ℕₜ n d n≡n prop) =
    ℕₜ (U.wk ρ n) (wkRed:*:Term [ρ] ⊢Δ d)
       (≅ₜ-wk [ρ] ⊢Δ n≡n)
       (wkNatural-prop [ρ] ⊢Δ prop)

  wkNatural-prop : ∀ {ρ Γ Δ n} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
                 → Natural-prop Γ n
                 → Natural-prop Δ (U.wk ρ n)
  wkNatural-prop ρ ⊢Δ (sucᵣ n) = sucᵣ (wkTermℕ ρ ⊢Δ n)
  wkNatural-prop ρ ⊢Δ zeroᵣ = zeroᵣ
  wkNatural-prop ρ ⊢Δ (ne nf) = ne (wkTermNe ρ ⊢Δ nf)

mutual
  wkEqTermℕ : ∀ {ρ Γ Δ t u} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
            → _⊩ℕ_≡_∷ℕ Γ t u
            → _⊩ℕ_≡_∷ℕ Δ (U.wk ρ t) (U.wk ρ u)
  wkEqTermℕ {ρ} [ρ] ⊢Δ (ℕₜ₌ k k′ d d′ t≡u prop) =
    ℕₜ₌ (U.wk ρ k) (U.wk ρ k′) (wkRed:*:Term [ρ] ⊢Δ d)
        (wkRed:*:Term [ρ] ⊢Δ d′) (≅ₜ-wk [ρ] ⊢Δ t≡u)
        (wk[Natural]-prop [ρ] ⊢Δ prop)

  wk[Natural]-prop : ∀ {ρ Γ Δ n n′} → ρ ∷ Δ ⊆ Γ → (⊢Δ : ⊢ Δ)
                   → [Natural]-prop Γ n n′
                   → [Natural]-prop Δ (U.wk ρ n) (U.wk ρ n′)
  wk[Natural]-prop ρ ⊢Δ (sucᵣ [n≡n′]) = sucᵣ (wkEqTermℕ ρ ⊢Δ [n≡n′])
  wk[Natural]-prop ρ ⊢Δ zeroᵣ = zeroᵣ
  wk[Natural]-prop ρ ⊢Δ (ne x) = ne (wkEqTermNe ρ ⊢Δ x)

-- Weakening of the logical relation

wk : ∀ {ρ Γ Δ A l} → ρ ∷ Δ ⊆ Γ → ⊢ Δ → Γ ⊩⟨ l ⟩ A → Δ ⊩⟨ l ⟩ U.wk ρ A
wk ρ ⊢Δ (Uᵣ′ l′ l< ⊢Γ) = Uᵣ′ l′ l< ⊢Δ
wk ρ ⊢Δ (ℕᵣ D) = ℕᵣ (wkRed:*: ρ ⊢Δ D)
wk {ρ} [ρ] ⊢Δ (ne′ K D neK K≡K) =
  ne′ (U.wk ρ K) (wkRed:*: [ρ] ⊢Δ D) (wkNeutral ρ neK) (~-wk [ρ] ⊢Δ K≡K)
wk {ρ} {Γ} {Δ} {A} {l} [ρ] ⊢Δ (Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) =
  let ⊢ρF = T.wk [ρ] ⊢Δ ⊢F
      [F]′ : ∀ {ρ ρ′ E} ([ρ] : ρ ∷ E ⊆ Δ) ([ρ′] : ρ′ ∷ Δ ⊆ Γ) (⊢E : ⊢ E)
           → E ⊩⟨ l ⟩ U.wk ρ (U.wk ρ′ F)
      [F]′ {ρ} {ρ′} [ρ] [ρ′] ⊢E = irrelevance′
                              (PE.sym (wk-comp ρ ρ′ F))
                              ([F] ([ρ] •ₜ [ρ′]) ⊢E)
      [a]′ : ∀ {ρ ρ′ E a} ([ρ] : ρ ∷ E ⊆ Δ) ([ρ′] : ρ′ ∷ Δ ⊆ Γ) (⊢E : ⊢ E)
             ([a] : E ⊩⟨ l ⟩ a ∷ U.wk ρ (U.wk ρ′ F) / [F]′ [ρ] [ρ′] ⊢E)
           → E ⊩⟨ l ⟩ a ∷ U.wk (ρ • ρ′) F / [F] ([ρ] •ₜ [ρ′]) ⊢E
      [a]′ {ρ} {ρ′} [ρ] [ρ′] ⊢E [a] = irrelevanceTerm′ (wk-comp ρ ρ′ F)
                                          ([F]′ [ρ] [ρ′] ⊢E) ([F] ([ρ] •ₜ [ρ′]) ⊢E) [a]
      [G]′ : ∀ {ρ ρ′ E a} ([ρ] : ρ ∷ E ⊆ Δ) ([ρ′] : ρ′ ∷ Δ ⊆ Γ) (⊢E : ⊢ E)
             ([a] : E ⊩⟨ l ⟩ a ∷ U.wk ρ (U.wk ρ′ F) / [F]′ [ρ] [ρ′] ⊢E)
           → E ⊩⟨ l ⟩ U.wk (lift (ρ • ρ′)) G [ a ]
      [G]′ η η′ ⊢E [a] = [G] (η •ₜ η′) ⊢E ([a]′ η η′ ⊢E [a])
  in  Πᵣ′ (U.wk ρ F) (U.wk (lift ρ) G) (T.wkRed:*: [ρ] ⊢Δ D) ⊢ρF
           (T.wk (lift [ρ]) (⊢Δ ∙ ⊢ρF) ⊢G)
           (≅-wk [ρ] ⊢Δ A≡A)
           (λ {ρ₁} [ρ₁] ⊢Δ₁ → irrelevance′ (PE.sym (wk-comp ρ₁ ρ F))
                                    ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
           (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] → irrelevance′ (wk-comp-subst ρ₁ ρ G)
                                        ([G]′ [ρ₁] [ρ] ⊢Δ₁ [a]))
           (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] [b] [a≡b] →
              let [a≡b]′ = irrelevanceEqTerm′ (wk-comp ρ₁ ρ F)
                                              ([F]′ [ρ₁] [ρ] ⊢Δ₁)
                                              ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁)
                                              [a≡b]
              in  irrelevanceEq″ (wk-comp-subst ρ₁ ρ G)
                                  (wk-comp-subst ρ₁ ρ G)
                                  ([G]′ [ρ₁] [ρ] ⊢Δ₁ [a])
                                  (irrelevance′
                                            (wk-comp-subst ρ₁ ρ G)
                                            ([G]′ [ρ₁] [ρ] ⊢Δ₁ [a]))
                                  (G-ext ([ρ₁] •ₜ [ρ]) ⊢Δ₁
                                         ([a]′ [ρ₁] [ρ] ⊢Δ₁ [a])
                                         ([a]′ [ρ₁] [ρ] ⊢Δ₁ [b])
                                         [a≡b]′))
wk ρ ⊢Δ (emb′ 0<1 x) = emb′ 0<1 (wk ρ ⊢Δ x)

wkEq : ∀ {ρ Γ Δ A B l} → ([ρ] : ρ ∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
       ([A] : Γ ⊩⟨ l ⟩ A)
     → Γ ⊩⟨ l ⟩ A ≡ B / [A]
     → Δ ⊩⟨ l ⟩ U.wk ρ A ≡ U.wk ρ B / wk [ρ] ⊢Δ [A]
wkEq ρ ⊢Δ (Uᵣ′ _ _ _) (U₌ PE.refl) = (U₌ PE.refl)
wkEq ρ ⊢Δ (ℕᵣ D) (ιx (ℕ₌ A≡B)) = ιx (ℕ₌ (wkRed* ρ ⊢Δ A≡B))
wkEq {ρ} [ρ] ⊢Δ (ne′ _ _ _ _) (ιx (ne₌ M D′ neM K≡M)) =
  ιx (ne₌ (U.wk ρ M) (wkRed:*: [ρ] ⊢Δ D′)
      (wkNeutral ρ neM) (~-wk [ρ] ⊢Δ K≡M))
wkEq {ρ} [ρ] ⊢Δ (Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                (Π₌ F′ G′ D′ A≡B [F≡F′] [G≡G′]) =
  Π₌ (U.wk ρ F′) (U.wk (lift ρ) G′) (T.wkRed* [ρ] ⊢Δ D′) (≅-wk [ρ] ⊢Δ A≡B)
     (λ {ρ₁} [ρ₁] ⊢Δ₁ → irrelevanceEq″ (PE.sym (wk-comp ρ₁ ρ F))
                                 (PE.sym (wk-comp ρ₁ ρ F′))
                                 ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁)
                                 (irrelevance′ (PE.sym (wk-comp ρ₁ ρ F))
                                               ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
                                 ([F≡F′] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
     (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] →
        let [a]′ = irrelevanceTerm′ (wk-comp ρ₁ ρ F)
                                    (irrelevance′ (PE.sym (wk-comp ρ₁ ρ F))
                                                  ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁))
                                    ([F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁) [a]
        in  irrelevanceEq″ (wk-comp-subst ρ₁ ρ G)
                            (wk-comp-subst ρ₁ ρ G′)
                            ([G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′)
                            (irrelevance′ (wk-comp-subst ρ₁ ρ G)
                                          ([G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
                            ([G≡G′] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
wkEq ρ ⊢Δ (emb′ 0<1 x) (ιx A≡B) = ιx (wkEq ρ ⊢Δ x A≡B)

wkTerm : ∀ {ρ Γ Δ A t l} ([ρ] : ρ ∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
         ([A] : Γ ⊩⟨ l ⟩ A)
       → Γ ⊩⟨ l ⟩ t ∷ A / [A]
       → Δ ⊩⟨ l ⟩ U.wk ρ t ∷ U.wk ρ A / wk [ρ] ⊢Δ [A]
wkTerm {ρ} [ρ] ⊢Δ (Uᵣ′ .⁰ 0<1 ⊢Γ) (Uₜ A d typeA A≡A [t]) =
  Uₜ (U.wk ρ A) (wkRed:*:Term [ρ] ⊢Δ d)
     (wkType ρ typeA) (≅ₜ-wk [ρ] ⊢Δ A≡A) (wk [ρ] ⊢Δ [t])
wkTerm ρ ⊢Δ (ℕᵣ D) (ιx [t]) = ιx (wkTermℕ ρ ⊢Δ [t])
--
wkTerm {ρ} [ρ] ⊢Δ (ne′ K D neK K≡K) (ιx (neₜ k d nf)) =
  ιx (neₜ (U.wk ρ k) (wkRed:*:Term [ρ] ⊢Δ d) (wkTermNe [ρ] ⊢Δ nf))
wkTerm {ρ} [ρ] ⊢Δ (Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) (Πₜ f d funcF f≡f [f] [f]₁) =
  Πₜ (U.wk ρ f) (wkRed:*:Term [ρ] ⊢Δ d) (wkFunction ρ funcF)
     (≅ₜ-wk [ρ] ⊢Δ f≡f)
     (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] [b] [a≡b] →
        let F-compEq = wk-comp ρ₁ ρ F
            G-compEq = wk-comp-subst ρ₁ ρ G
            [F]₁ = [F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁
            [F]₂ = irrelevance′ (PE.sym (wk-comp ρ₁ ρ F)) [F]₁
            [a]′ = irrelevanceTerm′ F-compEq [F]₂ [F]₁ [a]
            [b]′ = irrelevanceTerm′ F-compEq [F]₂ [F]₁ [b]
            [G]₁ = [G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′
            [G]₂ = irrelevance′ G-compEq [G]₁
            [a≡b]′ = irrelevanceEqTerm′ F-compEq [F]₂ [F]₁ [a≡b]
        in  irrelevanceEqTerm″ (PE.cong (λ x → x ∘ _) (PE.sym (wk-comp ρ₁ ρ _)))
                                (PE.cong (λ x → x ∘ _) (PE.sym (wk-comp ρ₁ ρ _)))
                                G-compEq
                                [G]₁ [G]₂
                                ([f] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′ [b]′ [a≡b]′))
     (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] →
        let [F]₁ = [F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁
            [F]₂ = irrelevance′ (PE.sym (wk-comp ρ₁ ρ F)) [F]₁
            [a]′ = irrelevanceTerm′ (wk-comp ρ₁ ρ F) [F]₂ [F]₁ [a]
            [G]₁ = [G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′
            [G]₂ = irrelevance′ (wk-comp-subst ρ₁ ρ G) [G]₁
        in  irrelevanceTerm″ (wk-comp-subst ρ₁ ρ G)
                              (PE.cong (λ x → x ∘ _) (PE.sym (wk-comp ρ₁ ρ _)))
                              [G]₁ [G]₂ ([f]₁ ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
wkTerm ρ ⊢Δ (emb′ 0<1 x) (ιx t) = ιx (wkTerm ρ ⊢Δ x t)

wkEqTerm : ∀ {ρ Γ Δ A t u l} ([ρ] : ρ ∷ Δ ⊆ Γ) (⊢Δ : ⊢ Δ)
           ([A] : Γ ⊩⟨ l ⟩ A)
         → Γ ⊩⟨ l ⟩ t ≡ u ∷ A / [A]
         → Δ ⊩⟨ l ⟩ U.wk ρ t ≡ U.wk ρ u ∷ U.wk ρ A / wk [ρ] ⊢Δ [A]
wkEqTerm {ρ} [ρ] ⊢Δ (Uᵣ′ .⁰ 0<1 ⊢Γ) (Uₜ₌ A B d d′ typeA typeB A≡B [t] [u] [t≡u]) =
  Uₜ₌ (U.wk ρ A) (U.wk ρ B) (wkRed:*:Term [ρ] ⊢Δ d) (wkRed:*:Term [ρ] ⊢Δ d′)
      (wkType ρ typeA) (wkType ρ typeB) (≅ₜ-wk [ρ] ⊢Δ A≡B)
      (wk [ρ] ⊢Δ [t]) (wk [ρ] ⊢Δ [u]) (wkEq [ρ] ⊢Δ [t] [t≡u])
wkEqTerm ρ ⊢Δ (ℕᵣ D) (ιx [t≡u]) = ιx (wkEqTermℕ ρ ⊢Δ [t≡u])
wkEqTerm {ρ} [ρ] ⊢Δ (ne′ K D neK K≡K) (ιx (neₜ₌ k m d d′ nf)) =
  ιx (neₜ₌ (U.wk ρ k) (U.wk ρ m)
       (wkRed:*:Term [ρ] ⊢Δ d) (wkRed:*:Term [ρ] ⊢Δ d′)
       (wkEqTermNe [ρ] ⊢Δ nf))
wkEqTerm {ρ} [ρ] ⊢Δ (Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext)
                    (Πₜ₌ f g d d′ funcF funcG f≡g [t] [u] [f≡g]) =
  let [A] = Πᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext
  in  Πₜ₌ (U.wk ρ f) (U.wk ρ g) (wkRed:*:Term [ρ] ⊢Δ d) (wkRed:*:Term [ρ] ⊢Δ d′)
          (wkFunction ρ funcF) (wkFunction ρ funcG)
          (≅ₜ-wk [ρ] ⊢Δ f≡g) (wkTerm [ρ] ⊢Δ [A] [t]) (wkTerm [ρ] ⊢Δ [A] [u])
          (λ {ρ₁} [ρ₁] ⊢Δ₁ [a] →
             let [F]₁ = [F] ([ρ₁] •ₜ [ρ]) ⊢Δ₁
                 [F]₂ = irrelevance′ (PE.sym (wk-comp ρ₁ ρ F)) [F]₁
                 [a]′ = irrelevanceTerm′ (wk-comp ρ₁ ρ F) [F]₂ [F]₁ [a]
                 [G]₁ = [G] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′
                 [G]₂ = irrelevance′ (wk-comp-subst ρ₁ ρ G) [G]₁
             in  irrelevanceEqTerm″ (PE.cong (λ y → y ∘ _) (PE.sym (wk-comp ρ₁ ρ _)))
                                     (PE.cong (λ y → y ∘ _) (PE.sym (wk-comp ρ₁ ρ _)))
                                     (wk-comp-subst ρ₁ ρ G)
                                     [G]₁ [G]₂
                                     ([f≡g] ([ρ₁] •ₜ [ρ]) ⊢Δ₁ [a]′))
wkEqTerm ρ ⊢Δ (emb′ 0<1 x) (ιx t≡u) = ιx (wkEqTerm ρ ⊢Δ x t≡u)


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

open import HoTT
open import homotopy.PtdAdjoint
open import groups.FromSusp
open import groups.ToOmega

module groups.SuspAdjointLoop {i} where

  import homotopy.SuspAdjointLoop {i} as A

  module _ (X Y : Ptd i) where

    private
      pres-comp : preserves-comp
        (GroupStructure.comp (⊙→Ω-group-structure (⊙Susp X) Y))
        (GroupStructure.comp (⊙→Ω-group-structure X (⊙Ω Y)))
        (–> (A.eq X (⊙Ω Y)))
    abstract
      pres-comp h₁ h₂ =
        B.nat-cod h₁ h₂ ⊙Ω-∙
        ∙ ap (_⊙∘ ⊙fanout (–> (A.eq X (⊙Ω Y)) h₁) (–> (A.eq X (⊙Ω Y)) h₂))
             arr2-lemma
        where
        module A× = RightAdjoint× A.hadj
        module B = RightAdjointBinary A.hadj

        ap2-lemma : ∀ {i j k} {A : Type i} {B : Type j} {C : Type k}
          (f : A × B → C) {r s : A × B} (p : r == s)
          → ap f p == ap2 (curry f) (ap fst p) (ap snd p)
        ap2-lemma f idp = idp

        ⊙ap2-lemma : ∀ {i j k} {X : Ptd i} {Y : Ptd j} {Z : Ptd k}
          (f : X ⊙× Y ⊙→ Z)
          → ⊙Ω-fmap f == ⊙Ω-fmap2 f ⊙∘ ⊙fanout (⊙Ω-fmap ⊙fst) (⊙Ω-fmap ⊙snd)
        ⊙ap2-lemma (f , idp) = ⊙λ=' (ap2-lemma f) idp

        arr2-lemma : B.arr2 ⊙Ω-∙ == ⊙Ω-∙
        arr2-lemma =
          ⊙Ω-fmap ⊙Ω-∙ ⊙∘ A×.⊙out _ _
            =⟨ ⊙ap2-lemma ⊙Ω-∙ |in-ctx _⊙∘ A×.⊙out _ _ ⟩
          (⊙Ω-fmap2 ⊙Ω-∙ ⊙∘ A×.⊙into _ _) ⊙∘ A×.⊙out _ _
            =⟨ ⊙λ= $ ⊙∘-assoc (⊙Ω-fmap2 ⊙Ω-∙) (A×.⊙into _ _) (A×.⊙out _ _) ⟩
          ⊙Ω-fmap2 ⊙Ω-∙ ⊙∘ (A×.⊙into _ _ ⊙∘ A×.⊙out _ _)
            =⟨ A×.⊙into-out _ _ |in-ctx ⊙Ω-fmap2 ⊙Ω-∙ ⊙∘_ ⟩
          ⊙Ω-fmap2 ⊙Ω-∙
            =⟨ ⊙Ω-fmap2-∙ ⟩
          ⊙Ω-∙ ∎

    ⊙→Ω-iso-⊙→Ω : ⊙→Ω-group-structure (⊙Susp X) Y ≃ᴳˢ ⊙→Ω-group-structure X (⊙Ω Y)
    ⊙→Ω-iso-⊙→Ω = ≃-to-≃ᴳˢ (A.eq X (⊙Ω Y)) pres-comp

    Trunc-⊙→Ω-iso-Trunc-⊙→Ω : Trunc-⊙→Ω-group (⊙Susp X) Y ≃ᴳ Trunc-⊙→Ω-group X (⊙Ω Y)
    Trunc-⊙→Ω-iso-Trunc-⊙→Ω = Trunc-group-emap ⊙→Ω-iso-⊙→Ω

  abstract
    Trunc-⊙→Ω-iso-Trunc-⊙→Ω-nat-dom : {X Y : Ptd i} (f : X ⊙→ Y) (Z : Ptd i)
      → fst (Trunc-⊙→Ω-iso-Trunc-⊙→Ω X Z) ∘ᴳ Trunc-⊙→Ω-group-fmap-dom (⊙Susp-fmap f) Z
        == Trunc-⊙→Ω-group-fmap-dom f (⊙Ω Z) ∘ᴳ fst (Trunc-⊙→Ω-iso-Trunc-⊙→Ω Y Z)
    Trunc-⊙→Ω-iso-Trunc-⊙→Ω-nat-dom f Z = group-hom= $ λ= $ Trunc-elim
      (λ g → ap [_] (! (A.nat-dom f (⊙Ω Z) g)))

  module _ (X Y : Ptd i) where

    private
      pres-comp'' : ∀ h₀ h₁ →
          fst (<– (A.eq X Y) (GroupStructure.comp (⊙→Ω-group-structure X Y) (–> (A.eq X Y) h₀) (–> (A.eq X Y) h₁)))
        ∼ fst (GroupStructure.comp (Susp⊙→-group-structure X Y) h₀ h₁)
    abstract
      pres-comp'' (h₀ , idp) (h₁ , h₁-pt) =
        Susp-elim
          idp
          (! h₁-pt ∙ ap h₁ (merid (pt X)))
          (λ x → ↓-='-in' $
            ap (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt)) ∘ pinch X) (merid x)
              =⟨ ap-∘ (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt))) (pinch X) (merid x)  ⟩
            ap (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt))) (ap (pinch X) (merid x))
              =⟨ ap (ap (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt)))) (Pinch.merid-β X x) ⟩
            ap (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt))) (ap winl (σloop X x) ∙ wglue ∙ ap winr (merid x))
              =⟨ ap-∙∙ (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt))) (ap winl (σloop X x)) wglue (ap winr (merid x)) ⟩
            ap (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt))) (ap winl (σloop X x))
            ∙ ap (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt))) wglue
            ∙ ap (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt))) (ap winr (merid x))
              =⟨ ap3 (λ p q r → p ∙ q ∙ r)
                   (∘-ap (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt))) winl (σloop X x))
                   (⊙WedgeRec.glue-β (h₀ , idp) (h₁ , h₁-pt))
                   (∘-ap (fst (⊙Wedge-rec (h₀ , idp) (h₁ , h₁-pt))) winr (merid x)) ⟩
            ap h₀ (σloop X x) ∙ ! h₁-pt ∙ ap h₁ (merid x)
              =⟨ lemma h₁ (ap h₀ (σloop X x)) h₁-pt (merid x) (merid (pt X)) h₁-pt ⟩
            (ap h₀ (σloop X x) ∙ (! h₁-pt ∙ ap h₁ (σloop X x) ∙' h₁-pt)) ∙' (! h₁-pt ∙ ap h₁ (merid (pt X)))
              =⟨ ap (λ p → p ∙' (! h₁-pt ∙ ap h₁ (merid (pt X)))) $
                  ap h₀ (σloop X x) ∙ (! h₁-pt ∙ ap h₁ (σloop X x) ∙' h₁-pt)
                    =⟨ ! $ ap (ap h₀ (σloop X x) ∙_) (Ω-fmap-β (h₁ , h₁-pt) (σloop X x)) ⟩
                  ap h₀ (σloop X x) ∙ Ω-fmap (h₁ , h₁-pt) (σloop X x)
                    =⟨ ! $ A.Eta.merid-β Y (ap h₀ (σloop X x) ∙ Ω-fmap (h₁ , h₁-pt) (σloop X x)) ⟩
                  ap (fst (A.ε Y)) (merid (ap h₀ (σloop X x) ∙ Ω-fmap (h₁ , h₁-pt) (σloop X x)))
                    =⟨ ! $ ap (ap (fst (A.ε Y))) (SuspFmap.merid-β (λ x → ap h₀ (σloop X x) ∙ Ω-fmap (h₁ , h₁-pt) (σloop X x)) x) ⟩
                  ap (fst (A.ε Y)) (ap (Susp-fmap (λ x → ap h₀ (σloop X x) ∙ Ω-fmap (h₁ , h₁-pt) (σloop X x))) (merid x))
                    =⟨ ∘-ap (fst (A.ε Y)) (Susp-fmap (λ x → ap h₀ (σloop X x) ∙ Ω-fmap (h₁ , h₁-pt) (σloop X x))) (merid x) ⟩
                  ap (fst (A.ε Y) ∘ Susp-fmap (λ x → ap h₀ (σloop X x) ∙ Ω-fmap (h₁ , h₁-pt) (σloop X x))) (merid x)
                    =∎ ⟩
            ap (fst (A.ε Y) ∘ Susp-fmap (λ x → ap h₀ (σloop X x) ∙ Ω-fmap (h₁ , h₁-pt) (σloop X x))) (merid x)
            ∙' (! h₁-pt ∙ ap h₁ (merid (pt X)))
              =∎)
        where
        lemma : ∀ {i j} {A : Type i} {B : Type j} (f : A → B)
          {a₀ a₁ a₂ : A} {b₀ b₁ b₂ : B} (p₀ : b₀ == b₁) (p₁ : f a₀ == b₁)
          (p₂ : a₀ == a₁) (p₃ : a₂ == a₁) (p₄ : f a₂ == b₂)
          → p₀ ∙ ! p₁ ∙ ap f p₂ == (p₀ ∙ (! p₁ ∙ ap f (p₂ ∙ ! p₃) ∙' p₄)) ∙' (! p₄ ∙ ap f p₃)
        lemma f idp idp idp idp idp = idp

      private
        pres-comp' : ∀ h₀ h₁ →
             <– (A.eq X Y) (GroupStructure.comp (⊙→Ω-group-structure X Y) (–> (A.eq X Y) h₀) (–> (A.eq X Y) h₁))
          ⊙∼ GroupStructure.comp (Susp⊙→-group-structure X Y) h₀ h₁
      abstract
        pres-comp' (h₀ , idp) (h₁ , h₁-pt) =
          pres-comp'' (h₀ , idp) (h₁ , h₁-pt) , idp

      private
        pres-comp : preserves-comp
          (GroupStructure.comp (Susp⊙→-group-structure X Y))
          (GroupStructure.comp (⊙→Ω-group-structure X Y))
          (–> (A.eq X Y))
      abstract
        pres-comp h₀ h₁ =
          –> (A.eq X Y) (GroupStructure.comp (Susp⊙→-group-structure X Y) h₀ h₁)
            =⟨ ! (ap (–> (A.eq X Y)) (⊙λ= (pres-comp' h₀ h₁))) ⟩
          –> (A.eq X Y) (<– (A.eq X Y) (GroupStructure.comp (⊙→Ω-group-structure X Y) (–> (A.eq X Y) h₀) (–> (A.eq X Y) h₁)))
            =⟨ <–-inv-r (A.eq X Y) (GroupStructure.comp (⊙→Ω-group-structure X Y) (–> (A.eq X Y) h₀) (–> (A.eq X Y) h₁)) ⟩
          GroupStructure.comp (⊙→Ω-group-structure X Y) (–> (A.eq X Y) h₀) (–> (A.eq X Y) h₁)
            =∎

    Susp⊙→-iso-⊙→Ω : Susp⊙→-group-structure X Y ≃ᴳˢ ⊙→Ω-group-structure X Y
    Susp⊙→-iso-⊙→Ω = ≃-to-≃ᴳˢ (A.eq X Y) pres-comp

    Trunc-Susp⊙→-iso-Trunc-⊙→Ω : Trunc-Susp⊙→-group X Y ≃ᴳ Trunc-⊙→Ω-group X Y
    Trunc-Susp⊙→-iso-Trunc-⊙→Ω = Trunc-group-emap Susp⊙→-iso-⊙→Ω

  module _ (X Y : Ptd i) where

    private
      pres-comp : preserves-comp
        (GroupStructure.comp (Susp⊙→-group-structure X Y))
        (GroupStructure.comp (Susp⊙→-group-structure (⊙Susp X) (⊙Susp Y)))
        (⊙Susp-fmap :> ((⊙Susp X ⊙→ Y) → _))
    abstract
      pres-comp = ∼-preserves-preserves-comp
        (GroupStructure.comp (Susp⊙→-group-structure X Y))
        (GroupStructure.comp (Susp⊙→-group-structure (⊙Susp X) (⊙Susp Y)))
        (λ f →
          <– (A.eq (⊙Susp X) (⊙Susp Y)) (<– (A.eq X (⊙Ω (⊙Susp Y))) (⊙Ω-fmap (A.η Y) ⊙∘ –> (A.eq X Y) f))
            =⟨ ap (<– (A.eq (⊙Susp X) (⊙Susp Y)) ∘ <– (A.eq X (⊙Ω (⊙Susp Y)))) $ A.nat-cod X (A.η Y) f ⟩
          <– (A.eq (⊙Susp X) (⊙Susp Y)) (<– (A.eq X (⊙Ω (⊙Susp Y))) (–> (A.eq X (⊙Ω (⊙Susp Y))) (A.η Y ⊙∘ f)))
            =⟨ ap (<– (A.eq (⊙Susp X) (⊙Susp Y))) $ <–-inv-l (A.eq X (⊙Ω (⊙Susp Y))) (A.η Y ⊙∘ f)  ⟩
          <– (A.eq (⊙Susp X) (⊙Susp Y)) (A.η Y ⊙∘ f)
            =⟨ ap (<– (A.eq (⊙Susp X) (⊙Susp Y))) $ A.η-natural f ⟩
          <– (A.eq (⊙Susp X) (⊙Susp Y)) (–> (A.eq (⊙Susp X) (⊙Susp Y)) (⊙Susp-fmap f))
            =⟨ <–-inv-l (A.eq (⊙Susp X) (⊙Susp Y)) (⊙Susp-fmap f) ⟩
          ⊙Susp-fmap f
            =∎)
        (GroupStructureHom.pres-comp $
              GroupStructureIso.g-shom (Susp⊙→-iso-⊙→Ω (⊙Susp X) (⊙Susp Y))
          ∘ᴳˢ GroupStructureIso.g-shom (⊙→Ω-iso-⊙→Ω X (⊙Susp Y))
          ∘ᴳˢ ⊙→Ω-group-structure-fmap-codom X (A.η Y)
          ∘ᴳˢ GroupStructureIso.f-shom (Susp⊙→-iso-⊙→Ω X Y))

    Susp⊙→-Susp-fmap-shom : Susp⊙→-group-structure X Y →ᴳˢ Susp⊙→-group-structure (⊙Susp X) (⊙Susp Y)
    Susp⊙→-Susp-fmap-shom = group-structure-hom ⊙Susp-fmap pres-comp

    Trunc-Susp⊙→-Susp-fmap-hom : Trunc-Susp⊙→-group X Y →ᴳ Trunc-Susp⊙→-group (⊙Susp X) (⊙Susp Y)
    Trunc-Susp⊙→-Susp-fmap-hom = Trunc-group-fmap Susp⊙→-Susp-fmap-shom