module Values where

open import Data.Bool
open import Data.List
open import Data.List.All
open import Data.Nat
open import Data.Product
open import Data.Sum
open import Relation.Binary.PropositionalEquality

open import Typing
open import Syntax
open import Global
open import Channel

mutual
-- a value indexed by a *relevant* session context, which is "used up" by the value
  data Val (G : SCtx) : Type → Set where
    VUnit : (inaG : Inactive G)
      → Val G TUnit
    VInt  : (i : ℕ)
      → (inaG : Inactive G)
      → Val G TInt
    VPair : ∀ {t₁ t₂ G₁ G₂}
      → (ss-GG₁G₂ : SSplit G G₁ G₂)
      → (v₁ : Val G₁ t₁)
      → (v₂ : Val G₂ t₂)
      → Val G (TPair t₁ t₂)
    VChan : ∀ {s}
      → (ce : ChannelEnd)
      → (cr : ChannelRef G ce s)
      → Val G (TChan s)
    VFun : ∀ {φ lu t₁ t₂}
      → (lu ≡ LL ⊎ All Unr φ)
      → (ϱ : VEnv G φ)
      → (e : Expr (t₁ ∷ φ) t₂)
      → Val G (TFun lu t₁ t₂)

  -- type environment-indexed value environment
  -- session context G describes the entire environment, it splits over all (channel) values
  data VEnv (G : SCtx) : TCtx → Set where
    vnil : (ina : Inactive G) → VEnv G []
    vcons : ∀{t φ G₁ G₂} → (ssp : SSplit G G₁ G₂) → (v : Val G₁ t) → (ϱ : VEnv G₂ φ) → VEnv G (t ∷ φ)

unrestricted-val :  ∀ {t G} → Unr t → Val G t → Inactive G
unrestricted-venv : ∀ {φ G} → All Unr φ → VEnv G φ → Inactive G

unrestricted-val UUnit (VUnit x) = x
unrestricted-val UInt (VInt i x) = x
unrestricted-val (UPair unrt unrt₁) (VPair x v v₁) =
  ssplit-inactive x (unrestricted-val unrt v) (unrestricted-val unrt₁ v₁)
unrestricted-val {TFun UU t₁ t₂} UFun (VFun (inj₁ ()) ϱ e)
unrestricted-val {TFun UU t₁ t₂} UFun (VFun (inj₂ unr-φ) ϱ e) = unrestricted-venv unr-φ ϱ

unrestricted-venv unr-φ (vnil ina) = ina
unrestricted-venv (px ∷ unr-φ) (vcons ssp v ϱ) =
  ssplit-inactive ssp (unrestricted-val px v) (unrestricted-venv unr-φ ϱ)

-- access a value in an indexed environment
access : ∀ {φ t} {G : SCtx} → VEnv G φ → t ∈ φ → ∃ λ G₁ → ∃ λ G₂ → Inactive G₂ × SSplit G G₁ G₂ × Val G₁ t
access (vcons ssp v ϱ) (here allUnr) =  _ , _ , unrestricted-venv allUnr ϱ , ssp , v
access (vcons ssp x₀ ϱ) (there unrX₀ x) with access ϱ x
access (vcons ssp x₀ ϱ) (there unrX₀ x) | G₁ , G₂ , inaG₂ , ssp12 , v with ssplit-compose4 ssp ssp12
... | Gi , ssp1 , ssp2 = G₁ , Gi , ssplit-inactive ssp2 (unrestricted-val unrX₀ x₀) inaG₂ , ssp1 , v

-- coerce a value to a supertype
coerce : ∀ {G t t'} → Val G t → SubT t t' → Val G t'
coerce (VUnit inaG) sub-unit = VUnit inaG
coerce (VInt i inaG) sub-int = VInt i inaG
coerce (VPair ss-GG₁G₂ v v₁) (sub-pair t≤t' t≤t'') = VPair ss-GG₁G₂ (coerce v t≤t') (coerce v₁ t≤t'')
coerce (VChan b vcr) (sub-chan s≲s') = VChan b (vcr-coerce vcr s≲s')
coerce (VFun lu ϱ e) (sub-fun t≤t' t≤t'') = VFun lu ϱ (expr-coerce e t≤t'' t≤t')

rewrite-ssplit : ∀ {G G' G₁ G₂} → G ≡ G' → SSplit G G₁ G₂ → SSplit G' G₁ G₂
rewrite-ssplit p ssp rewrite p = ssp

rewrite-ssplit1 : ∀ {G G₁ G₁' G₂} → G₁ ≡ G₁' → SSplit G G₁ G₂ → SSplit G G₁' G₂
rewrite-ssplit1 p ssp rewrite p = ssp

-- split environment according to type context split
split-env : ∀ {Φ Φ₁ Φ₂} {G : SCtx}
  → Split Φ Φ₁ Φ₂
  → VEnv G Φ
  → Σ (SCtx × SCtx) λ { (G₁ , G₂) → SSplit G G₁ G₂ × VEnv G₁ Φ₁ × VEnv G₂ Φ₂ }
split-env{G = G} [] (vnil ina) =  (G , G) , inactive-ssplit-trivial ina , vnil ina , vnil ina
split-env (dupl unrt sp) (vcons ssp v ϱ) with split-env sp ϱ | unrestricted-val unrt v
split-env (dupl unrt sp) (vcons ssp v ϱ) | (G₁' , G₂') , ssp12 , ϱ₁' , ϱ₂' | unr-v rewrite inactive-left-ssplit ssp unr-v with ssplit-compose4 ssp ssp12 | ssplit-compose3 ssp ssp12
... | Gi , ssp-GG1Gi , ssp-GiG1G2' | Gi-1 , ssp-GGiG2' , ssp-GiG1G1' =
  let p₁ = (inactive-left-ssplit ssp-GiG1G1' unr-v) in
  let p₂ = (inactive-left-ssplit ssp-GiG1G2' unr-v) in
  (G₁' , G₂') ,  ssp12 , vcons (rewrite-ssplit p₁ ssp-GiG1G1') v ϱ₁' , vcons (rewrite-ssplit p₂ ssp-GiG1G2') v ϱ₂'
split-env (drop px sp) (vcons ssp v ϱ)
  rewrite inactive-left-ssplit ssp (unrestricted-val px v)
  = split-env sp ϱ
split-env (left sp) (vcons ssp v ϱ) with split-env sp ϱ
split-env{G = G} (left sp) (vcons ssp v ϱ) | (G₁' , G₂') , ssp12 , ϱ₁' , ϱ₂' with ssplit-compose3 ssp ssp12
... | Gi , ssp-GiG2' , ssp-GiG1G1' = (Gi , G₂') , ssp-GiG2' , vcons ssp-GiG1G1' v ϱ₁' , ϱ₂'
split-env (rght sp) (vcons ssp v ϱ) with split-env sp ϱ
split-env (rght sp) (vcons ssp v ϱ) | (G₁' , G₂') , ssp12 , ϱ₁' , ϱ₂' with ssplit-compose4 ssp ssp12
...| Gi , ssp-GG1'Gi , ssp-GiG1G2' = (G₁' , Gi) , ssp-GG1'Gi , ϱ₁' , vcons ssp-GiG1G2' v ϱ₂'


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

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

open import Cubical.Relation.Nullary

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.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.Instances.Unit

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
  renaming (rec to trRec)
open import Cubical.HITs.SetTruncation
  renaming (rec to sRec ; elim to sElim ; elim2 to sElim2 ; map to sMap)
open import Cubical.HITs.PropositionalTruncation
  renaming (rec to pRec)

-- The pushout of the hopf invariant
HopfInvariantPush : (n : ℕ)
             → (f : S₊ (3 +ℕ n +ℕ n) → S₊ (2 +ℕ n))
             → Type _
HopfInvariantPush n f = Pushout (λ _ → tt) f

Hopfα : (n : ℕ)
     → (f : S₊∙ (3 +ℕ n +ℕ n) →∙ S₊∙ (2 +ℕ n))
     → coHom (2 +ℕ n) (HopfInvariantPush n (fst f))
Hopfα n f = ∣ (λ { (inl x) → 0ₖ _
                ; (inr x) → ∣ x ∣
                ; (push a i) → help a (~ i)}) ∣₂
  where
  help : (a : S₊ (3 +ℕ n +ℕ n)) → ∣ fst f a ∣ ≡ 0ₖ (2 +ℕ n)
  help = sphereElim _ (λ _ → isOfHLevelPlus' {n = n} (3 +ℕ n)
         (isOfHLevelPath' (3 +ℕ n) (isOfHLevelTrunc (4 +ℕ n)) _ _))
                                    (cong ∣_∣ₕ (snd f))

Hopfβ : (n : ℕ)
     → (f : S₊∙ (3 +ℕ n +ℕ n) →∙ S₊∙ (2 +ℕ n))
     → coHom (4 +ℕ (n +ℕ n)) (HopfInvariantPush n (fst f))
Hopfβ n f = fst (MV.d _ _ _ (λ _ → tt) (fst f) (3 +ℕ n +ℕ n)) ∣ ∣_∣ ∣₂

-- To define the Hopf invariant, we need to establish the
-- non-trivial isos of Hⁿ(HopfInvariant).
module _ (n : ℕ) (f : S₊∙ (3 +ℕ n +ℕ n) →∙ S₊∙ (2 +ℕ n)) where
  module M = MV _ _ _ (λ _ → tt) (fst f)
  ¬lemHopf : (n m : ℕ) → ¬ suc (suc (m +ℕ n)) ≡ suc n
  ¬lemHopf zero zero p = snotz (cong predℕ p)
  ¬lemHopf (suc n) zero p = ¬lemHopf n zero (cong predℕ p)
  ¬lemHopf zero (suc m) p = snotz (cong predℕ p)
  ¬lemHopf (suc n) (suc m) p =
    ¬lemHopf n (suc m) (cong (suc ∘ suc )
      (sym (+-suc m n)) ∙ (cong predℕ p))

  SphereHopfCohomIso :
    GroupEquiv
      (coHomGr (3 +ℕ n +ℕ n) (S₊ (3 +ℕ n +ℕ n)))
      ((coHomGr (suc (3 +ℕ n +ℕ n)) (HopfInvariantPush _ (fst f))))
  fst (fst SphereHopfCohomIso) =
    fst (MV.d _ _ _ (λ _ → tt) (fst f) (3 +ℕ n +ℕ n))
  snd (fst SphereHopfCohomIso) = help
    where
      abstract
        help : isEquiv (fst (MV.d _ _ _ (λ _ → tt) (fst f)
                                 (3 +ℕ n +ℕ n)))
        help =
          SES→isEquiv
            (isContr→≡UnitGroup
              (isContrΣ (GroupIsoUnitGroup→isContr (invGroupIso (Hⁿ-Unit≅0 _)))
                        λ _ → GroupIsoUnitGroup→isContr
                            (invGroupIso (Hⁿ-Sᵐ≅0 _ _ (¬lemHopf n n)))))
            (isContr→≡UnitGroup
              (isContrΣ (GroupIsoUnitGroup→isContr
                        (invGroupIso (Hⁿ-Unit≅0 _)))
                        λ _ → GroupIsoUnitGroup→isContr
                          (invGroupIso (Hⁿ-Sᵐ≅0 _ _ (¬lemHopf n (suc n))))))
              (M.Δ (3 +ℕ n +ℕ n))
              (M.d (3 +ℕ n +ℕ n))
              (M.i (4 +ℕ n +ℕ n))
              (M.Ker-d⊂Im-Δ _)
              (M.Ker-i⊂Im-d _)
  snd SphereHopfCohomIso = d-inv
    where
      -- Currently, type checking is very slow without the abstract flag.
      -- TODO : Remove abstract
      abstract
        d-inv : IsGroupHom (snd (coHomGr (3 +ℕ n +ℕ n) (S₊ (3 +ℕ n +ℕ n))))
                  (fst (MV.d _ _ _ (λ _ → tt) (fst f) (3 +ℕ n +ℕ n)))
                  (snd (coHomGr (suc (3 +ℕ n +ℕ n))
                       (Pushout (λ _ → tt) (fst f))))
        d-inv = snd (MV.d _ _ _ (λ _ → tt) (fst f) (3 +ℕ n +ℕ n))

  Hopfβ-Iso : GroupIso (coHomGr (suc (3 +ℕ n +ℕ n))
                                (HopfInvariantPush _ (fst f)))
                       ℤGroup
  fst Hopfβ-Iso = compIso (invIso (equivToIso (fst SphereHopfCohomIso)))
                           (fst (Hⁿ-Sⁿ≅ℤ (suc (suc (n +ℕ n)))))
  snd Hopfβ-Iso = grHom
    where
      abstract
        grHom :
          IsGroupHom (coHomGr (suc (3 +ℕ n +ℕ n))
                              (Pushout (λ _ → tt) (fst f)) .snd)
                     (λ x → Iso.fun (fst ((Hⁿ-Sⁿ≅ℤ (suc (suc (n +ℕ n))))))
                       (invEq (fst SphereHopfCohomIso) x))
                     (ℤGroup .snd)
        grHom = snd (compGroupIso
                  (GroupEquiv→GroupIso (invGroupEquiv (SphereHopfCohomIso)))
                  (Hⁿ-Sⁿ≅ℤ (suc (suc (n +ℕ n)))))

Hⁿ-Sⁿ≅ℤ-nice-generator : (n : ℕ) → Iso.inv (fst (Hⁿ-Sⁿ≅ℤ (suc n))) 1 ≡ ∣ ∣_∣ ∣₂
Hⁿ-Sⁿ≅ℤ-nice-generator zero = Iso.leftInv (fst (Hⁿ-Sⁿ≅ℤ (suc zero))) _
Hⁿ-Sⁿ≅ℤ-nice-generator (suc n) =
  (λ i → Iso.inv (fst (suspensionAx-Sn (suc n) (suc n)))
                  (Hⁿ-Sⁿ≅ℤ-nice-generator n i))
     ∙ cong ∣_∣₂
       (funExt λ { north → refl
                 ; south → cong ∣_∣ₕ (merid north)
                 ; (merid a i) j → ∣ compPath-filler (merid a)
                                      (sym (merid north)) (~ j) i ∣ₕ})

Hopfβ↦1 : (n : ℕ) (f : S₊∙ (3 +ℕ n +ℕ n) →∙ S₊∙ (2 +ℕ n))
  → Iso.fun (fst (Hopfβ-Iso n f)) (Hopfβ n f) ≡ 1
Hopfβ↦1 n f =
    sym (cong (Iso.fun (fst (Hopfβ-Iso n f))) lem)
  ∙ Iso.rightInv (fst (Hopfβ-Iso n f)) (pos 1)
  where
  lem : Iso.inv (fst (Hopfβ-Iso n f)) (pos 1) ≡ Hopfβ n f
  lem = (λ i → fst (MV.d _ _ _ (λ _ → tt) (fst f) (3 +ℕ n +ℕ n))
               (Hⁿ-Sⁿ≅ℤ-nice-generator _ i))
    ∙ cong ∣_∣₂ (funExt (λ { (inl x) → refl
                          ; (inr x) → refl
                          ; (push a i) → refl}))

module _ (n : ℕ) (f : S₊∙ (3 +ℕ n +ℕ n) →∙ S₊∙ (2 +ℕ n)) where
  private
    2+n = 2 +ℕ n
    H = HopfInvariantPush n (fst f)

    H→Sphere : coHom 2+n H → coHom 2+n (S₊ (suc (suc n)))
    H→Sphere = sMap (_∘ inr)

    grHom : IsGroupHom (snd (coHomGr 2+n H))
                       H→Sphere (snd (coHomGr 2+n (S₊ (suc (suc n)))))
    grHom =
      makeIsGroupHom
        (sElim2 (λ _ _ → isOfHLevelPath 2 squash₂ _ _)
          λ f g → refl)

    preSphere→H : (g : (S₊ (suc (suc n)) → coHomK 2+n))
                 → H → coHomK (2 +ℕ n)
    preSphere→H g (inl x) = 0ₖ _
    preSphere→H g (inr x) = g x -ₖ g north
    preSphere→H g (push a i) = lem a i
      where
      lem : (a : S₊ (suc (suc (suc (n +ℕ n)))))
          → 0ₖ (suc (suc n)) ≡ g (fst f a) -ₖ g north
      lem =
        sphereElim _
          (λ x → isOfHLevelPlus' {n = n} (3 +ℕ n)
                    (isOfHLevelTrunc (4 +ℕ n) _ _))
                    (sym (rCancelₖ _ (g north))
                     ∙ cong (λ x → g x -ₖ g north) (sym (snd f)))

    Sphere→H : coHom 2+n (S₊ (suc (suc n))) → coHom 2+n H
    Sphere→H = sMap preSphere→H

    conCohom2+n : (x : _) → ∥ x ≡ 0ₖ (suc (suc n)) ∥₁
    conCohom2+n =
      coHomK-elim _ (λ _ → isProp→isOfHLevelSuc (suc n) squash₁) ∣ refl ∣₁

  HIPSphereCohomIso :
    Iso (coHom (2 +ℕ n) (HopfInvariantPush n (fst f)))
        (coHom (2 +ℕ n) (S₊ (2 +ℕ n)))
  Iso.fun HIPSphereCohomIso = H→Sphere
  Iso.inv HIPSphereCohomIso = Sphere→H
  Iso.rightInv HIPSphereCohomIso =
    sElim (λ _ → isOfHLevelPath 2 squash₂ _ _)
      λ g → pRec (squash₂ _ _)
                  (λ p → cong ∣_∣₂ (funExt λ x → cong (g x +ₖ_) (cong (-ₖ_) p)
                                 ∙ rUnitₖ _ (g x)))
                  (conCohom2+n (g north))
  Iso.leftInv HIPSphereCohomIso =
    sElim (λ _ → isOfHLevelPath 2 squash₂ _ _)
      λ g →
       pRec (squash₂ _ _)
        (pRec (isPropΠ (λ _ → squash₂ _ _))
         (λ gn gtt →
          trRec (isProp→isOfHLevelSuc n (squash₂ _ _))
            (λ r → cong ∣_∣₂ (funExt λ {
                          (inl x) → sym gtt
                        ; (inr x) → (λ i → g (inr x) -ₖ gn i)
                                    ∙ rUnitₖ _ (g (inr x))
                        ; (push a i)
                          → sphereElim _
                               {A = λ a →
                                 PathP (λ i → preSphere→H (λ x → g (inr x))
                                                            (push a i)
                                      ≡ g (push a i))
                                     (sym gtt)
                                     ((λ i → g (inr (fst f a)) -ₖ gn i)
                                     ∙ rUnitₖ _ (g (inr (fst f a))))}
               (λ _ → isOfHLevelPathP' (suc (suc (suc (n +ℕ n))))
                        (isOfHLevelPath (suc (suc (suc (suc (n +ℕ n)))))
                         (isOfHLevelPlus' {n = n} (suc (suc (suc (suc n))))
                          (isOfHLevelTrunc (suc (suc (suc (suc n)))))) _ _) _ _)
                               r a i}))
                       (push-helper g gtt gn))
               (conCohom2+n (g (inr north))))
             (conCohom2+n (g (inl tt)))
    where
    push-helper : (g : HopfInvariantPush n (fst f) → coHomK (suc (suc n)))
     → (gtt : g (inl tt) ≡ 0ₖ (suc (suc n)))
     → (gn : g (inr north) ≡ 0ₖ (suc (suc n)))
     → hLevelTrunc (suc n)
             (PathP (λ i → preSphere→H (λ x → g (inr x)) (push north i)
                          ≡ g (push north i))
             (sym gtt)
             ((λ i → g (inr (fst f north)) -ₖ gn i)
              ∙ rUnitₖ _ (g (inr (fst f north)))))
    push-helper g gtt gn =
      isConnectedPathP _ (isConnectedPath _ (isConnectedKn _) _ _) _ _ .fst

  Hopfα-Iso : GroupIso (coHomGr (2 +ℕ n) (HopfInvariantPush n (fst f))) ℤGroup
  Hopfα-Iso =
    compGroupIso
      (HIPSphereCohomIso , grHom)
      (Hⁿ-Sⁿ≅ℤ (suc n))

Hopfα-Iso-α : (n : ℕ) (f : _)
            → Iso.fun (fst (Hopfα-Iso n f)) (Hopfα n f)
            ≡ 1
Hopfα-Iso-α n f =
    sym (cong (Iso.fun (fst (Hⁿ-Sⁿ≅ℤ (suc n)))) (Hⁿ-Sⁿ≅ℤ-nice-generator n))
  ∙ Iso.rightInv (fst (Hⁿ-Sⁿ≅ℤ (suc n))) (pos 1)
  where
  hz : Iso.fun (HIPSphereCohomIso n f) (Hopfα n f) ≡ ∣ ∣_∣ ∣₂
  hz = refl

⌣-α : (n : ℕ) → (f : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n)) → _
⌣-α n f = Hopfα n f ⌣ Hopfα n f

HopfInvariant : (n : ℕ)
             → (f : S₊∙ (3 +ℕ (n +ℕ n)) →∙ S₊∙ (2 +ℕ n))
             → ℤ
HopfInvariant n f = Iso.fun (fst (Hopfβ-Iso n f))
  (subst (λ x → coHom x (HopfInvariantPush n (fst f)))
         (λ i → suc (suc (suc (+-suc n n i))))
         (⌣-α n f))

HopfInvariant-π' : (n : ℕ) → π' (3 +ℕ (n +ℕ n)) (S₊∙ (2 +ℕ n)) → ℤ
HopfInvariant-π' n = sRec isSetℤ (HopfInvariant n)

HopfInvariant-π : (n : ℕ) → π (3 +ℕ (n +ℕ n)) (S₊∙ (2 +ℕ n)) → ℤ
HopfInvariant-π n =  sRec isSetℤ λ x → HopfInvariant-π' n ∣ Ω→SphereMap _ x ∣₂

-- Elimination principle for the pushout defining the Hopf Invariant
HopfInvariantPushElim :
       ∀ {ℓ} n → (f : _)
    → {P : HopfInvariantPush n f → Type ℓ}
    → (isOfHLevel (suc (suc (suc (suc (n +ℕ n))))) (P (inl tt)))
    → (e : P (inl tt))
       (g : (x : _) → P (inr x))
       (r : PathP (λ i → P (push north i)) e (g (f north)))
    → (x : _) → P x
HopfInvariantPushElim n f {P = P} hlev e g r (inl x) = e
HopfInvariantPushElim n f {P = P} hlev e g r (inr x) = g x
HopfInvariantPushElim n f {P = P} hlev e g r (push a i₁) = help a i₁
  where
  help : (a : Susp (Susp (S₊ (suc (n +ℕ n)))))
       → PathP (λ i → P (push a i)) e (g (f a))
  help =
    sphereElim _
      (sphereElim _
        (λ _ → isProp→isOfHLevelSuc (suc (suc (n +ℕ n)))
                                      (isPropIsOfHLevel _))
                  (isOfHLevelPathP' (suc (suc (suc (n +ℕ n))))
                    (subst (isOfHLevel (suc (suc (suc (suc (n +ℕ n))))))
                           (cong P (push north))
                           hlev) _ _)) r


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

module Generics.Telescope where

open import Data.String using (String)
open import Generics.Prelude hiding (curry)

private variable
  l     : Level
  A B   : Set l
  a x y : A

data Telescope (A : Set l) : Setω

private variable T : Telescope A

levelOfTel : Telescope A → Level
⟦_⟧tel     : (T : Telescope A) → A → Set (levelOfTel T)

infixl 7 _⊢<_>_ _⊢_

data Telescope A where
  ε      : Telescope A
  _⊢<_>_ : ∀ (T : Telescope A) (ai : String × ArgInfo) {ℓ} (f : Σ A ⟦ T ⟧tel → Set ℓ) → Telescope A

_⊢_ : ∀ (T : Telescope A) {ℓ} (f : Σ A ⟦ T ⟧tel → Set ℓ) → Telescope A
T ⊢ f = T ⊢< "_" , arg-info visible (modality relevant quantity-ω) > f

extendTel : (T : Telescope A) (f : ∀ {x} → ⟦ T ⟧tel x → Set l) → Telescope A
extendTel T f = T ⊢ f ∘ proj₂

infixr 3 extendTel
syntax extendTel A (λ x → B) = x ∶ A , B

levelOfTel ε = lzero
levelOfTel (_⊢<_>_ T _ {ℓ} f) = levelOfTel T ⊔ ℓ

⟦ ε ⟧tel x = ⊤
⟦ T ⊢< n , i > f ⟧tel x = Σ[ t ∈ ⟦ T ⟧tel x ] < relevance i > f (x , t)

hideInfo : ArgInfo → ArgInfo
hideInfo i = arg-info hidden (getModality i)

ExTele : Telescope ⊤ → Setω
ExTele T = Telescope (⟦ T ⟧tel tt)

⟦_,_⟧xtel : (P : Telescope ⊤) (I : ExTele P) → Set (levelOfTel P ⊔ levelOfTel I)
⟦ P , V ⟧xtel = Σ (⟦ P ⟧tel tt) ⟦ V ⟧tel

⟦_,_&_⟧xtel : (P : Telescope ⊤) (V I : ExTele P) → Set (levelOfTel P ⊔ levelOfTel V ⊔ levelOfTel I)
⟦ P , V & I ⟧xtel = Σ[ p ∈ ⟦ P ⟧tel tt ] ⟦ V ⟧tel p × ⟦ I ⟧tel p

----------------------
-- Telescope currying

Curried′ : (T : Telescope A) → (⟦ T ⟧tel x → Set l) → Set (l ⊔ levelOfTel T)
Curried′ ε P = P tt
Curried′ (T ⊢< ai > g) P = Curried′ T λ t → Π< ai > (g (_ , t)) λ y → P (t , y)

Pred′ : (T : Telescope A) → (⟦ T ⟧tel x → Set l) → Set (l ⊔ levelOfTel T)
Pred′ ε P = P tt
Pred′ (T ⊢< n , i > g) P = Pred′ T λ t → Π< n , hideInfo i > (g (_ , t)) λ y → P (t , y)

uncurry′ : (T : Telescope A) (P : ⟦ T ⟧tel x → Set l) (B : Curried′ T P) → (y : ⟦ T ⟧tel x) → P y
uncurry′ ε P B tt = B
uncurry′ (T ⊢< i > f) P B (tx , gx) =
  app< i > (uncurry′ T (λ p → Π< i > (f (_ , p)) (λ y → P (p , y))) B tx) _

curry′ : (T : Telescope A) (P : ⟦ T ⟧tel x → Set l) → ((y : ⟦ T ⟧tel x) → P y) → Curried′ T P
curry′ ε P f = f tt
curry′ (T ⊢< i > S) P f =
  curry′ T _ λ t → fun< i > λ s → f (t , s)

unpred′ : (T : Telescope A) (P : ⟦ T ⟧tel x → Set l) (B : Pred′ T P) → (y : ⟦ T ⟧tel x) → P y
unpred′ ε P B tt = B
unpred′ (T ⊢< n , i > f) P B (tx , gx) =
  app< n , hideInfo i > (unpred′ T (λ p → Π< n , hideInfo i > (f (_ , p)) λ y → P (p , y)) B tx) _

pred′ : (T : Telescope A) (P : ⟦ T ⟧tel x → Set l) → ((y : ⟦ T ⟧tel x) → P y) → Pred′ T P
pred′ ε P f = f tt
pred′ (T ⊢< n , i > S) P f =
  pred′ T _ λ t → fun< n , hideInfo i > λ s → f (t , s)

Curried : ∀ P (I : ExTele P) {ℓ} (Pr : ⟦ P , I ⟧xtel → Set ℓ) → Set (levelOfTel P ⊔ levelOfTel I ⊔ ℓ)
Curried P I {ℓ} Pr = Curried′ P λ p → Curried′ I λ i → Pr (p , i)

curry : ∀ {P} {I : ExTele P} {ℓ} {Pr : ⟦ P , I ⟧xtel → Set ℓ}
      → ((pi : ⟦ P , I ⟧xtel) →  Pr pi) → Curried P I Pr
curry f = curry′ _ _ λ p → curry′ _ _ λ i → f (p , i)

uncurry : ∀ P (I : ExTele P) {ℓ} {Pr : ⟦ P , I ⟧xtel → Set ℓ} → Curried P I Pr → (pi : ⟦ P , I ⟧xtel) → Pr pi
uncurry P I C (p , i) = uncurry′ I _ (uncurry′ P _ C p) i

-- Type of parametrized, indexed sets: (p₁ : A₁) ... (pₙ : Aₙ) (i₁ : B₁) ... (iₚ : Bₚ) → Set ℓ
Indexed : ∀ P (I : ExTele P) ℓ → Set (levelOfTel P ⊔ levelOfTel I ⊔ lsuc ℓ)
Indexed P I ℓ = Curried P I (const (Set ℓ))

unindexed : ∀ P (I : ExTele P) ℓ → Indexed P I ℓ → ⟦ P , I ⟧xtel → Set ℓ
unindexed P I ℓ = uncurry P I

-- Type of predicates on parametrized & indexed families:
--   {p₁ : A₁} ... {pₙ : Aₙ} {i₁ : B₁} ... {iₚ : Bₚ} → X (p₁ ... iₚ) → Set ℓ
Pred : ∀ P (I : ExTele P) {a} (X : ⟦ P , I ⟧xtel → Set a) ℓ
     → Set (levelOfTel P ⊔ levelOfTel I ⊔ a ⊔ lsuc ℓ)
Pred P I X ℓ = Pred′ P λ p → Pred′ I λ i → X (p , i) → Set ℓ

unpred : ∀ P (I : ExTele P) {a} {X : ⟦ P , I ⟧xtel → Set a} ℓ → Pred P I X ℓ
       → (pi : ⟦ P , I ⟧xtel) → X pi → Set ℓ
unpred P I ℓ C (p , i) = unpred′ I _ (unpred′ P _ C p) i


------------------------------------------------------------------------
-- Fω with interval kinds
------------------------------------------------------------------------

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

-- Author: Sandro Stucki
-- Copyright (c) 2017-2021 EPFL

-- The code in this repository contains an Agda mechanization of the
-- type system Fω extended with interval kinds ("F omega int").  An
-- interval kind `A..B' represents a type interval bounded by a pair
-- of types `A', `B'.  It is inhabited by all proper types `C : A..B'
-- that are both supertypes of `A' and subtypes of `B'.  Interval
-- kinds are flexible enough to express various features found in
-- other type systems, such as
--
--  * F-<:-style bounded polymorphism,
--  * bounded type operators,
--  * singleton kinds and first-class type definitions.
--
-- The mechanization includes a small-step operational call-by-value
-- semantics, declarative and canonical presentations of typing and
-- kinding, along with (syntactic) proofs of various meta-theoretic
-- properties, such as
--
--  * weak normalization of types (and kinds) via hereditary substitution,
--  * subject reduction for types (w.r.t. full β-reduction),
--  * type safety (progress & preservation) w.r.t. to the CBV semantics,
--  * undecidability of subtyping.
--
-- For more details on Fω with interval kinds, see my PhD thesis
-- "Higher-Order Subtyping with Type Intervals", which is available at
--
--   http://dx.doi.org/10.5075/epfl-thesis-8014
--
-- The code makes heavy use of the Agda standard library, which is
-- freely available at
--
--   https://github.com/agda/agda-stdlib/
--
-- The code has been tested using Agda 2.6.1.3 and version 1.6 of
-- the Agda standard library.

module README where

------------------------------------------------------------------------
-- Modules related to Fω with interval kinds.

-- Syntax of (untyped) terms along with support for substitutions,
-- (untyped) hereditary substitutions and normalization.
open import FOmegaInt.Syntax
open import FOmegaInt.Syntax.SingleVariableSubstitution
open import FOmegaInt.Syntax.HereditarySubstitution
open import FOmegaInt.Syntax.Normalization

-- Weak equality of (untyped) terms up to kind annotations in
-- abstractions.
open import FOmegaInt.Syntax.WeakEquality

-- Variants of β-reduction/equivalence and properties thereof.
open import FOmegaInt.Reduction.Cbv
open import FOmegaInt.Reduction.Full

-- Typing, kinding, subtyping, etc. along with corresponding
-- substitution lemmas.
open import FOmegaInt.Typing

-- An alternate presentation of kinding and subtyping that is better
-- suited for proving functionality and validity lemmas, along with a
-- proof that the two presentations are equivalent.
open import FOmegaInt.Kinding.Declarative
open import FOmegaInt.Kinding.Declarative.Validity
open import FOmegaInt.Kinding.Declarative.Equivalence

-- Soundness of normalization w.r.t. to declarative kinding.
open import FOmegaInt.Kinding.Declarative.Normalization

-- Simple kinding of types, (hereditary) substitution lemmas.
open import FOmegaInt.Kinding.Simple

-- Lemmas about η-expansion of simply kinded and normalization and
-- simultaneous simplification of declaratively kinded types.
open import FOmegaInt.Kinding.Simple.EtaExpansion
open import FOmegaInt.Kinding.Simple.Normalization

-- Canonical kinding of types along with (hereditary) substitution,
-- validity and inversion lemmas for canonical kinding and subtyping.
open import FOmegaInt.Kinding.Canonical
open import FOmegaInt.Kinding.Canonical.HereditarySubstitution
open import FOmegaInt.Kinding.Canonical.Validity
open import FOmegaInt.Kinding.Canonical.Inversion

-- Lifting of weak (untyped) kind and type equality to canonical kind
-- and type equality.
open import FOmegaInt.Kinding.Canonical.WeakEquality

-- Equivalence of canonical and declarative kinding.
open import FOmegaInt.Kinding.Canonical.Equivalence

-- Generation of typing and inversion of declarative subtyping in the
-- empty context.
open import FOmegaInt.Typing.Inversion

-- Type safety (preservation/subject reduction and progress).
open import FOmegaInt.Typing.Preservation
open import FOmegaInt.Typing.Progress

-- Encodings and properties of higher-order extremal types, interval
-- kinds and bounded quantifiers.
open import FOmegaInt.Typing.Encodings

-- A reduced variant of the canonical system to be used in the
-- undecidability proof.
open import FOmegaInt.Kinding.Canonical.Reduced

-- Setup for the undecidability proof: syntax and lemmas for the SK
-- combinator calculus, and support for encoding/decoding SK terms and
-- equality proofs into types and subtyping derivations.
open import FOmegaInt.Undecidable.SK
open import FOmegaInt.Undecidable.Encoding
open import FOmegaInt.Undecidable.Decoding

-- Undecidability of subtyping
open import FOmegaInt.Undecidable

------------------------------------------------------------------------
-- Modules containing generic functionality

-- Extra lemmas that are derivable in the substitution framework of
-- the Agda standard library, as well as support for binary (term)
-- relations lifted to substitutions, typed substitutions, and typed
-- relations lifted to substitutions.
open import Data.Context
open import Data.Context.WellFormed
open import Data.Context.Properties
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Fin.Substitution.Relation
open import Data.Fin.Substitution.Typed
open import Data.Fin.Substitution.TypedRelation

-- Support for generic reduction relations.
open import Relation.Binary.Reduction

-- Relational reasoning for transitive relations.
open import Relation.Binary.TransReasoning


-- MIT License

-- Copyright (c) 2021 Luca Ciccone and Luca Padovani

-- Permission is hereby granted, free of charge, to any person
-- obtaining a copy of this software and associated documentation
-- files (the "Software"), to deal in the Software without
-- restriction, including without limitation the rights to use,
-- copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following
-- conditions:

-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-- OTHER DEALINGS IN THE SOFTWARE.

{-# OPTIONS --guardedness #-}

open import Data.Empty
open import Data.Maybe
open import Data.Product
open import Data.Sum
open import Data.List using ([]; _∷_; _++_)

open import Relation.Nullary
open import Relation.Unary using (_∪_; _∩_; Satisfiable)
open import Relation.Binary.Construct.Closure.ReflexiveTransitive using (Star; ε; _◅_)

open import Common

module Session {ℙ : Set} (message : Message ℙ)
  where

open import Trace message
open import SessionType message
open import Transitions message
open import HasTrace message

data Session : Set where
  _#_  : (T : SessionType) (S : SessionType) -> Session

data Reduction : Session -> Session -> Set where
  sync : ∀{α R R' T T'} (r : Transition R (co-action α) R') (t : Transition T α T') -> Reduction (R # T) (R' # T')

Reductions : Session -> Session -> Set
Reductions = Star Reduction

Reduce : Session -> Set
Reduce S = Satisfiable (Reduction S)

data Success : Session -> Set where
  win#def : ∀{T S} (w : Win T) (def : Defined S) -> Success (T # S)

ProgressS : Session -> Set
ProgressS = Success ∪ Reduce

MaySucceed : Session -> Set
MaySucceed S = Satisfiable (Reductions S ∩ Success)

InvariantS : (Session -> Set) -> Session -> Set
InvariantS P S = ∀{S'} -> Reductions S S' -> P S'

ImplyS : (Session -> Set) -> SessionType -> SessionType -> Set
ImplyS P T S = ∀{R} -> P (R # T) -> P (R # S)

ComplianceS : Session -> Set
ComplianceS = InvariantS ProgressS

FairComplianceS : Session -> Set
FairComplianceS = InvariantS MaySucceed

SubtypingS : SessionType -> SessionType -> Set
SubtypingS = ImplyS ComplianceS

FairSubtypingS : SessionType -> SessionType -> Set
FairSubtypingS = ImplyS FairComplianceS

ConvergenceS : SessionType -> SessionType -> Set
ConvergenceS T S = ∀{R} -> FairComplianceS (R # T) -> MaySucceed (R # S)

unzip-red* : ∀{T T' S S'} -> Reductions (T # S) (T' # S') ->
             ∃[ φ ] (Transitions T (co-trace φ) T' × Transitions S φ S')
unzip-red* ε = _ , refl , refl
unzip-red* (sync r t ◅ reds) =
  let _ , tr , sr = unzip-red* reds in
  _ , step r tr , step t sr

zip-red* : ∀{φ T T' S S'} ->
           Transitions T (co-trace φ) T' ->
           Transitions S φ S' ->
           Reductions (T # S) (T' # S')
zip-red* refl refl = ε
zip-red* (step t tr) (step s sr) = sync t s ◅ zip-red* tr sr

zip-traces :
  ∀{R T φ}
  (rφ : R HasTrace (co-trace φ))
  (tφ : T HasTrace φ) ->
  Reductions (R # T) (after rφ # after tφ)
zip-traces (_ , _ , rr) (_ , _ , tr) = zip-red* rr tr

success-not-reduce : ∀{S S'} → Success S → ¬ (Reduction S S')
success-not-reduce (win#def win _) (sync r _) = win-reduces-⊥ win r


-- Andreas, 2016-12-29, issue #2366
-- With clauses need to be printed in fresh environment,
-- as they DON'T shadow the pattern variables of the parent clause.
-- (These are not even in scope)

-- {-# OPTIONS -v error.checkclause:60 #-}

open import Agda.Builtin.List

test : {A : Set} → List A → List A
test (x ∷ y ∷ ys) with []
test (x ∷ xs) | q = []
test _ = []

-- ERROR WAS:
-- With clause pattern xs is not an instance of its parent pattern y ∷ ys
-- when checking that the clause
-- test (x ∷ y ∷ ys) with []
-- test (x₁ ∷ xs) | q = []
-- has type {A : Set} → List A → List A
-- NOTE THE x₁!

-- Expected error:
-- With clause pattern xs is not an instance of its parent pattern y ∷ ys
-- when checking that the clause
-- test (x ∷ y ∷ ys) with []
-- test (x ∷ xs) | q = []
-- has type {A : Set} → List A → List A


{-# OPTIONS --allow-unsolved-metas #-}
module TermNode where

open import OscarPrelude
open import TermCode

record TermNode : Set
 where
  inductive
  field
    children : List (TermCode × TermNode)
    number : Nat

open TermNode public

open import Membership

_child∈_ : TermCode → TermNode → Set
_child∈_ 𝔠 𝔫 = 𝔠 ∈ (fst <$> children 𝔫)

_child∉_ : TermCode → TermNode → Set
𝔠 child∉ 𝔫 = ¬ (𝔠 child∈ 𝔫)

open import DecidableMembership

_child∈?_ : (𝔠 : TermCode) → (𝔫 : TermNode) → Dec $ 𝔠 child∈ 𝔫
c child∈? record { children = cs } = c ∈? (fst <$> cs)

getChild : {𝔠 : TermCode} → (𝔫 : TermNode) → 𝔠 child∈ 𝔫 → TermNode
getChild {𝔠} (record { children = [] ; number = number₁ }) ()
getChild {._} (record { children = (fst₁ , snd₁) ∷ children₁ ; number = number₁ }) zero = snd₁
getChild {𝔠} (𝔫@record { children = x ∷ children₁ ; number = number₁ }) (suc x₁) = getChild record 𝔫 { children = children₁ } x₁

addChild : {𝔠 : TermCode} (𝔫 : TermNode) → 𝔠 child∉ 𝔫 → TermNode → TermNode
addChild {𝔠} 𝔫 𝔠∉𝔫 𝔫' =
  record 𝔫 { children = (𝔠 , 𝔫') ∷ children 𝔫 }

setChild : {𝔠 : TermCode} (𝔫 : TermNode) → 𝔠 child∈ 𝔫 → TermNode → TermNode
setChild {𝔠} record { children = [] ; number = number₁ } () 𝔫'
setChild 𝔫@record { children = ((fst₁ , snd₁) ∷ children₁) ; number = number₁ } (zero) 𝔫' =
  record 𝔫 { children = ((fst₁ , 𝔫') ∷ children₁) }
setChild {𝔠} 𝔫@record { children = (x ∷ children₁) ; number = number₁ } (suc 𝔠∈𝔫) 𝔫' =
  record 𝔫 { children = (x ∷ children (setChild (record 𝔫 { children = children₁ }) 𝔠∈𝔫 𝔫')) }

setGet-ok : ∀ {𝔠} 𝔫 → (𝔠∈𝔫 : 𝔠 child∈ 𝔫) → setChild 𝔫 𝔠∈𝔫 (getChild 𝔫 𝔠∈𝔫) ≡ 𝔫
setGet-ok record { children = [] ; number = number₁ } ()
setGet-ok record { children = ((fst₁ , snd₁) ∷ children₁) ; number = number₁ } (zero) = refl
setGet-ok record { children = ((fst₁ , snd₁) ∷ children₁) ; number = number₁ } (suc 𝔠∈𝔫) rewrite setGet-ok (record { children = children₁ ; number = number₁ }) 𝔠∈𝔫 = refl

storeTermCodes : List TermCode → Nat → StateT TermNode Identity Nat
storeTermCodes [] 𝔑 = return 𝔑
storeTermCodes (𝔠 ∷ 𝔠s) 𝔑 =
  𝔫 ← get -|
  case 𝔠 child∈? 𝔫 of λ
  { (no 𝔠∉tests) →
    let 𝔑' , 𝔫' = runIdentity $
                  runStateT
                    (storeTermCodes 𝔠s $ suc 𝔑)
                    (record
                      { children = []
                      ; number = suc 𝔑 }) in
    put ((addChild 𝔫 𝔠∉tests 𝔫')) ~|
    return 𝔑'
  ; (yes 𝔠∈tests) →
    let 𝔑' , 𝔫' = runIdentity $
                  runStateT
                    (storeTermCodes 𝔠s $ suc 𝔑)
                    ((getChild 𝔫 𝔠∈tests)) in
    put ((setChild 𝔫 𝔠∈tests 𝔫')) ~|
    return 𝔑' }

storeTermCodes[] : (𝔫 : TermNode) (𝔑 : Nat) → (runIdentity $ runStateT (storeTermCodes [] 𝔑) 𝔫) ≡ (𝔑 , 𝔫)
storeTermCodes[] 𝔫 𝔑 = refl

--{-# REWRITE storeTermCodes[] #-}

storeTermCodes' : List TermCode → StateT Nat (StateT TermNode Identity) ⊤
storeTermCodes' 𝔠s =
  𝔑 ← get -|
  tn ← lift get -|
  (let 𝔑' , tn' = runIdentity $ runStateT (storeTermCodes 𝔠s 𝔑) tn in
   put 𝔑' ~| lift (put tn') ~| return tt)

open import Term
open import Vector

mutual

  storeTerm : Term → StateT Nat (StateT TermNode Identity) ⊤
  storeTerm τ@(variable _) = storeTermCodes' (encodeTerm τ)
  storeTerm τ@(function _ τs) = storeTermCodes' (encodeTerm τ) ~| storeTerms τs

  storeTerms : Terms → StateT Nat (StateT TermNode Identity) ⊤
  storeTerms ⟨ ⟨ [] ⟩ ⟩ = return tt
  storeTerms ⟨ ⟨ τ ∷ τs ⟩ ⟩ = storeTerm τ ~| storeTerms ⟨ ⟨ τs ⟩ ⟩ ~| return tt

module ExampleStoreTerm where

  open import FunctionName
  open import VariableName

  example-Term₁ : Term
  example-Term₁ =
    (function ⟨ 2 ⟩
              ⟨ ⟨ variable ⟨ 0 ⟩
              ∷ function ⟨ 3 ⟩
                         ⟨ ⟨ variable ⟨ 2 ⟩ ∷ [] ⟩ ⟩
              ∷ variable ⟨ 5 ⟩
              ∷ []
              ⟩ ⟩
    )

  example-Term₂ : Term
  example-Term₂ =
    (function ⟨ 2 ⟩
              ⟨ ⟨ variable ⟨ 0 ⟩
              ∷ variable ⟨ 2 ⟩
              ∷ function ⟨ 3 ⟩
                         ⟨ ⟨ variable ⟨ 2 ⟩ ∷ [] ⟩ ⟩
              ∷ variable ⟨ 5 ⟩
              ∷ []
              ⟩ ⟩
    )

  topNode : TermNode
  topNode = record { children = [] ; number = 0 }

  example-storeTerm : (⊤ × Nat) × TermNode
  example-storeTerm = runIdentity $ runStateT (runStateT (storeTerm example-Term₁ >> storeTerm example-Term₂) 0) topNode

NodeStateT = StateT TermNode
TopNodeState = StateT Nat (NodeStateT Identity)

open import LiteralFormula
open import IsLiteralFormula

storeLiteralFormulaTerms : LiteralFormula → StateT Nat (StateT TermNode Identity) ⊤
storeLiteralFormulaTerms ⟨ atomic 𝑃 τs ⟩ = storeTerms τs
storeLiteralFormulaTerms ⟨ logical 𝑃 τs ⟩ = storeTerms τs

open import 𝓢equent

storeSequentLiteralFormulaTerms : 𝓢equent LiteralFormula → StateT Nat (StateT TermNode Identity) ⊤′
storeSequentLiteralFormulaTerms (φˢs ⊢ φᵗ) = sequence $ storeLiteralFormulaTerms <$> ({!φᵗ!} ∷ φˢs)


module Implicits.Resolution.Ambiguous.Undecidable where

open import Prelude hiding (Dec)

open import Relation.Nullary
open import Relation.Nullary.Decidable as DecM
open import Function.Equivalence using (_⇔_; equivalence)
open import Relation.Binary.HeterogeneousEquality as H using ()

open import Implicits.Syntax
open import Implicits.Resolution.Ambiguous.Resolution
open import Implicits.Resolution.Ambiguous.Semantics
open import Implicits.Resolution.Ambiguous.SystemFEquiv
open import Implicits.Resolution.Embedding
open import Implicits.Resolution.Embedding.Lemmas

open import SystemF.Everything as F using ()

{-
  Assuming undecidability of the type inhabitation problem for System F
  (as proven by e.g. Barendregt) we can prove the undecidability of Ambiguous resolution
-}

private
  -- type of inhabitation problem decider
  ?:-Dec : Set
  ?:-Dec = ∀ {n ν} (Γ : F.Ctx ν n) (a : F.Type ν) → Dec (∃ λ t → Γ F.⊢ t ∈ a)

module Undecidable (?:-undec : ¬ ?:-Dec) where

  -- type of decider for ambiguous resolution
  ⊢ᵣ-Dec : Set
  ⊢ᵣ-Dec = ∀ {ν} (Δ : ICtx ν) (a : Type ν) → Dec (Δ ⊢ᵣ a)

  -- proof that such a decider would imply a decider for type inhabitation problem
  reduction : ⊢ᵣ-Dec → ?:-Dec
  reduction f Γ x = DecM.map (equivalent' Γ x) (f ⟦ Γ ⟧ctx← ⟦ x ⟧tp←)

  -- completing the proof
  undecidable : ¬ ⊢ᵣ-Dec
  undecidable f = ?:-undec (reduction f)


------------------------------------------------------------------------
-- Do the parser combinators form a Kleene algebra?
------------------------------------------------------------------------

module TotalParserCombinators.Laws.KleeneAlgebra where

open import Algebra
open import Data.List
open import Data.List.Properties
open import Data.Nat using (ℕ)
open import Data.Product using (_,_; proj₂)
open import Function using (_$_)
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence
  using (_⇔_; equivalence; module Equivalence)
import Relation.Binary.PropositionalEquality as P
open import Relation.Nullary

open import TotalParserCombinators.Lib
open import TotalParserCombinators.Parser
open import TotalParserCombinators.Semantics
  hiding (_>>=_) renaming (return to return′; _⊛_ to _⊛′_)

------------------------------------------------------------------------
-- A variant of _≲_

infix 4 _≲′_

-- The AdditiveMonoid module shows that _∣_ can be viewed as the join
-- operation of a join-semilattice (if language equivalence is used).
-- This means that the following definition of order is natural.

_≲′_ : ∀ {Tok R xs₁ xs₂} → Parser Tok R xs₁ → Parser Tok R xs₂ → Set₁
p₁ ≲′ p₂ = p₁ ∣ p₂ ≈ p₂

-- This order coincides with _≲_.

≲⇔≲′ : ∀ {Tok R xs₁ xs₂}
       (p₁ : Parser Tok R xs₁) (p₂ : Parser Tok R xs₂) →
       p₁ ≲ p₂ ⇔ p₁ ≲′ p₂
≲⇔≲′ {xs₁ = xs₁} p₁ p₂ = equivalence
  (λ (p₁≲p₂ : p₁ ≲ p₂) {x s} → equivalence (helper p₁≲p₂) (∣-right xs₁))
  (λ (p₁≲′p₂ : p₁ ≲′ p₂) s∈p₁ → Equivalence.to p₁≲′p₂ ⟨$⟩ ∣-left s∈p₁)
  where
  helper : p₁ ≲ p₂ → p₁ ∣ p₂ ≲ p₂
  helper p₁≲p₂ (∣-left       s∈p₁) = p₁≲p₂ s∈p₁
  helper p₁≲p₂ (∣-right .xs₁ s∈p₂) = s∈p₂

------------------------------------------------------------------------
-- A limited notion of *-continuity

-- Least upper bounds.

record _LeastUpperBoundOf_
         {Tok R xs} {f : ℕ → List R}
         (lub : Parser Tok R xs)
         (p : (n : ℕ) → Parser Tok R (f n)) : Set₁ where
  field
    upper-bound : ∀ n → p n ≲ lub
    least       : ∀ {ys} {ub : Parser Tok R ys} →
                  (∀ n → p n ≲ ub) → lub ≲ ub

-- For argument parsers which are not nullable we can prove that the
-- Kleene star operator is *-continuous.

*-continuous :
  ∀ {Tok R₁ R₂ R₃ fs xs}
  (p₁ : Parser Tok (List R₁ → R₂ → R₃) fs)
  (p₂ : Parser Tok R₁ [])
  (p₃ : Parser Tok R₂ xs) →
  (p₁ ⊛ p₂ ⋆ ⊛ p₃) LeastUpperBoundOf (λ n → p₁ ⊛ p₂ ↑ n ⊛ p₃)
*-continuous {Tok} {R₁ = R₁} {R₃ = R₃} {fs} {xs} p₁ p₂ p₃ =
  record { upper-bound = upper-bound; least = least }
  where
  upper-bound : ∀ n → p₁ ⊛ p₂ ↑ n ⊛ p₃ ≲ p₁ ⊛ p₂ ⋆ ⊛ p₃
  upper-bound n (∈p₁ ⊛′ ∈p₂ⁿ ⊛′ ∈p₃) =
    [ ○ - ○ ] [ ○ - ○ ] ∈p₁ ⊛ Exactly.↑≲⋆ n ∈p₂ⁿ ⊛ ∈p₃

  least : ∀ {ys} {p : Parser Tok R₃ ys} →
          (∀ i → p₁ ⊛ p₂ ↑ i ⊛ p₃ ≲ p) → p₁ ⊛ p₂ ⋆ ⊛ p₃ ≲ p
  least ub (∈p₁ ⊛′ ∈p₂⋆ ⊛′ ∈p₃) with Exactly.⋆≲∃↑ ∈p₂⋆
  ... | (n , ∈p₂ⁿ) = ub n ([ ○ - ○ ] [ ○ - ○ ] ∈p₁ ⊛ ∈p₂ⁿ ⊛ ∈p₃)

------------------------------------------------------------------------
-- The parser combinators do not form a Kleene algebra

-- If we allow arbitrary argument parsers, then we cannot prove the
-- following (variant of a) Kleene algebra axiom.

not-Kleene-algebra :
  ∀ {Tok} →
  Tok →
  (f : ∀ {R xs} → Parser Tok R xs → List (List R)) →
  (_⋆′ : ∀ {R xs} (p : Parser Tok R xs) →
         Parser Tok (List R) (f p)) →
  ¬ (∀ {R xs} {p : Parser Tok R xs} →
     return [] ∣ (p >>= λ x → (p ⋆′) >>= λ xs → return (x ∷ xs))
       ≲ (p ⋆′))
not-Kleene-algebra {Tok} t f _⋆′ fold =
  KleeneStar.unrestricted-incomplete t f _⋆′ ⋆′-complete
  where
  ⋆′-complete : ∀ {xs ys s} {p : Parser Tok Tok ys} →
                xs ∈[ p ]⋆· s → xs ∈ p ⋆′ · s
  ⋆′-complete []         = fold (∣-left return′)
  ⋆′-complete (∈p ∷ ∈p⋆) =
    fold (∣-right [ [] ]
            ([ ○ - ○ ] ∈p >>=
                       fix ([ ○ - ○ ] ⋆′-complete ∈p⋆ >>= return′)))
    where
    fix = cast∈ P.refl P.refl $ ++-identityʳ _

-- This shows that the parser combinators do not form a Kleene
-- algebra (interpreted liberally) using _⊛_ for composition, return
-- for unit, etc. However, it should be straightforward to build a
-- recogniser library, based on the parser combinators, which does
-- satisfy the Kleene algebra axioms (see
-- TotalRecognisers.LeftRecursion.KleeneAlgebra).


{-# OPTIONS --allow-unsolved-metas #-}

open import Oscar.Class
open import Oscar.Class.Reflexivity
open import Oscar.Class.Symmetry
open import Oscar.Class.Transitivity
open import Oscar.Class.Transleftidentity
open import Oscar.Prelude

module Test.ProblemWithDerivation-5 where

module Map
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  {𝔯₁₂} {𝔯₁₂'}
  (ℜ₁₂ : 𝔛₁ → 𝔛₂ → Ø 𝔯₁₂)
  (ℜ₁₂' : 𝔛₁ → 𝔛₂ → Ø 𝔯₁₂')
  = ℭLASS (ℜ₁₂ , ℜ₁₂')
          (∀ P₁ P₂
           → ℜ₁₂ P₁ P₂ → ℜ₁₂' P₁ P₂)

module _
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  {𝔯₁₂} {ℜ₁₂ : 𝔛₁ → 𝔛₂ → Ø 𝔯₁₂}
  {𝔯₁₂'} {ℜ₁₂' : 𝔛₁ → 𝔛₂ → Ø 𝔯₁₂'}
  where
  map = Map.method ℜ₁₂ ℜ₁₂'

module _
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔯₁₂} {ℜ₁₂ : 𝔛₁ → 𝔛₁ → Ø 𝔯₁₂}
  {𝔯₁₂'} {ℜ₁₂' : 𝔛₁ → 𝔛₁ → Ø 𝔯₁₂'}
  where
  smap = Map.method ℜ₁₂ ℜ₁₂' -- FIXME this looks like a viable solution; but what if `-MapFromTransleftidentity` builds something addressable by smap (i.e., where 𝔛₁ ≡ 𝔛₂)?

module _
  {𝔵₁} {𝔛₁ : Ø 𝔵₁}
  {𝔵₂} {𝔛₂ : Ø 𝔵₂}
  {𝔯₁₂} {ℜ₁₂ : 𝔛₁ → 𝔛₂ → Ø 𝔯₁₂}
  where
  hmap = Map.method ℜ₁₂ ℜ₁₂

postulate
  A : Set
  B : Set
  _~A~_ : A → A → Set
  _~B~_ : B → B → Set
  s1 : A → B
  f1 : ∀ {x y} → x ~A~ y → s1 x ~B~ s1 y

instance
  𝓢urjectivity1 : Map.class
                    {𝔛₁ = A}
                    {𝔛₂ = A}
                    _~A~_ (λ x y → s1 x ~B~ s1 y)
  𝓢urjectivity1 .⋆ _ _ = f1

-- Oscar.Class.Hmap.Transleftidentity
instance
  postulate
    -MapFromTransleftidentity : ∀
      {𝔵} {𝔛 : Ø 𝔵}
      {𝔞} {𝔄 : 𝔛 → Ø 𝔞}
      {𝔟} {𝔅 : 𝔛 → Ø 𝔟}
      (let _∼_ = Arrow 𝔄 𝔅)
      {ℓ̇} {_∼̇_ : ∀ {x y} → x ∼ y → x ∼ y → Ø ℓ̇}
      {transitivity : Transitivity.type _∼_}
      {reflexivity : Reflexivity.type _∼_}
      {ℓ}
      ⦃ _ : Transleftidentity.class _∼_ _∼̇_ reflexivity transitivity ⦄
      → ∀ {m n}
      → Map.class {𝔛₁ = Arrow 𝔄 𝔅 m n}
                  {𝔛₂ = LeftExtensionṖroperty ℓ (Arrow 𝔄 𝔅) _∼̇_ m}
                  (λ f P → π₀ (π₀ P) f)
                  (λ f P → π₀ (π₀ P) (transitivity f reflexivity))

test-before : ∀ {x y} → x ~A~ y → s1 x ~B~ s1 y
test-before = map _ _

postulate
  XX : Set
  BB : XX → Set
  EQ : ∀ {x y} → Arrow BB BB x y → Arrow BB BB x y → Set

instance
  postulate
    -transleftidentity : Transleftidentity.class (Arrow BB BB) EQ (λ x₁ → magic) (λ x₁ x₂ x₃ → magic)

test-after : ∀ {x y} → x ~A~ y → s1 x ~B~ s1 y
test-after {x} {y} = map _ _ -- FIXME yellow

test-after-s : ∀ {x y} → x ~A~ y → s1 x ~B~ s1 y
test-after-s {x} {y} = smap _ _

testr : ∀ {m n ℓ}
          (P₁ : Arrow BB BB m n)
          (P₂ : LeftExtensionṖroperty ℓ (Arrow BB BB) EQ m)
          → π₀ (π₀ P₂) P₁ → π₀ (π₀ P₂) (flip {∅̂} {∅̂} {∅̂}
                                         {Arrow (λ z → BB z) (λ z → BB z) m n}
                                         {Arrow (λ z → BB z) (λ z → BB z) n n}
                                         {λ _ _ → Arrow BB BB m n}
                                         (λ _ _ _ → magic) (λ _ → magic) P₁)
testr = map


-- Algorithmic equality.

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

module Definition.Conversion where

open import Definition.Untyped
open import Definition.Typed

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

infix 10 _⊢_~_↑_
infix 10 _⊢_~_↓_
infix 10 _⊢_[conv↑]_
infix 10 _⊢_[conv↓]_
infix 10 _⊢_[conv↑]_∷_
infix 10 _⊢_[conv↓]_∷_

mutual
  -- Neutral equality.
  data _⊢_~_↑_ (Γ : Con Term) : (k l A : Term) → Set where
    var-refl      : ∀ {x y A}
                  → Γ ⊢ var x ∷ A
                  → x PE.≡ y
                  → Γ ⊢ var x ~ var y ↑ A
    app-cong      : ∀ {k l t v F G}
                  → Γ ⊢ k ~ l ↓ Π F ▹ G
                  → Γ ⊢ t [conv↑] v ∷ F
                  → Γ ⊢ k ∘ t ~ l ∘ v ↑ G [ t ]
    fst-cong      : ∀ {p r F G}
                  → Γ ⊢ p ~ r ↓ Σ F ▹ G
                  → Γ ⊢ fst p ~ fst r ↑ F
    snd-cong      : ∀ {p r F G}
                  → Γ ⊢ p ~ r ↓ Σ F ▹ G
                  → Γ ⊢ snd p ~ snd r ↑ G [ fst p ]
    natrec-cong   : ∀ {k l h g a₀ b₀ F G}
                  → Γ ∙ ℕ ⊢ F [conv↑] G
                  → Γ ⊢ a₀ [conv↑] b₀ ∷ F [ zero ]
                  → Γ ⊢ h [conv↑] g ∷ Π ℕ ▹ (F ▹▹ F [ suc (var 0) ]↑)
                  → Γ ⊢ k ~ l ↓ ℕ
                  → Γ ⊢ natrec F a₀ h k ~ natrec G b₀ g l ↑ F [ k ]
    Emptyrec-cong : ∀ {k l F G}
                  → Γ ⊢ F [conv↑] G
                  → Γ ⊢ k ~ l ↓ Empty
                  → Γ ⊢ Emptyrec F k ~ Emptyrec G l ↑ F

  -- Neutral equality with types in WHNF.
  record _⊢_~_↓_ (Γ : Con Term) (k l B : Term) : Set where
    inductive
    constructor [~]
    field
      A     : Term
      D     : Γ ⊢ A ⇒* B
      whnfB : Whnf B
      k~l   : Γ ⊢ k ~ l ↑ A

  -- Type equality.
  record _⊢_[conv↑]_ (Γ : Con Term) (A B : Term) : Set where
    inductive
    constructor [↑]
    field
      A′ B′  : Term
      D      : Γ ⊢ A ⇒* A′
      D′     : Γ ⊢ B ⇒* B′
      whnfA′ : Whnf A′
      whnfB′ : Whnf B′
      A′<>B′ : Γ ⊢ A′ [conv↓] B′

  -- Type equality with types in WHNF.
  data _⊢_[conv↓]_ (Γ : Con Term) : (A B : Term) → Set where
    U-refl     : ⊢ Γ → Γ ⊢ U [conv↓] U
    ℕ-refl     : ⊢ Γ → Γ ⊢ ℕ [conv↓] ℕ
    Empty-refl : ⊢ Γ → Γ ⊢ Empty [conv↓] Empty
    Unit-refl  : ⊢ Γ → Γ ⊢ Unit [conv↓] Unit
    ne         : ∀ {K L}
               → Γ ⊢ K ~ L ↓ U
               → Γ ⊢ K [conv↓] L
    Π-cong     : ∀ {F G H E}
               → Γ ⊢ F
               → Γ ⊢ F [conv↑] H
               → Γ ∙ F ⊢ G [conv↑] E
               → Γ ⊢ Π F ▹ G [conv↓] Π H ▹ E
    Σ-cong     : ∀ {F G H E}
               → Γ ⊢ F
               → Γ ⊢ F [conv↑] H
               → Γ ∙ F ⊢ G [conv↑] E
               → Γ ⊢ Σ F ▹ G [conv↓] Σ H ▹ E

  -- Term equality.
  record _⊢_[conv↑]_∷_ (Γ : Con Term) (t u A : Term) : Set where
    inductive
    constructor [↑]ₜ
    field
      B t′ u′ : Term
      D       : Γ ⊢ A ⇒* B
      d       : Γ ⊢ t ⇒* t′ ∷ B
      d′      : Γ ⊢ u ⇒* u′ ∷ B
      whnfB   : Whnf B
      whnft′  : Whnf t′
      whnfu′  : Whnf u′
      t<>u    : Γ ⊢ t′ [conv↓] u′ ∷ B

  -- Term equality with types and terms in WHNF.
  data _⊢_[conv↓]_∷_ (Γ : Con Term) : (t u A : Term) → Set where
    ℕ-ins     : ∀ {k l}
              → Γ ⊢ k ~ l ↓ ℕ
              → Γ ⊢ k [conv↓] l ∷ ℕ
    Empty-ins : ∀ {k l}
              → Γ ⊢ k ~ l ↓ Empty
              → Γ ⊢ k [conv↓] l ∷ Empty
    Unit-ins  : ∀ {k l}
              → Γ ⊢ k ~ l ↓ Unit
              → Γ ⊢ k [conv↓] l ∷ Unit
    ne-ins    : ∀ {k l M N}
              → Γ ⊢ k ∷ N
              → Γ ⊢ l ∷ N
              → Neutral N
              → Γ ⊢ k ~ l ↓ M
              → Γ ⊢ k [conv↓] l ∷ N
    univ      : ∀ {A B}
              → Γ ⊢ A ∷ U
              → Γ ⊢ B ∷ U
              → Γ ⊢ A [conv↓] B
              → Γ ⊢ A [conv↓] B ∷ U
    zero-refl : ⊢ Γ → Γ ⊢ zero [conv↓] zero ∷ ℕ
    suc-cong  : ∀ {m n}
              → Γ ⊢ m [conv↑] n ∷ ℕ
              → Γ ⊢ suc m [conv↓] suc n ∷ ℕ
    η-eq      : ∀ {f g F G}
              → Γ ⊢ f ∷ Π F ▹ G
              → Γ ⊢ g ∷ Π F ▹ G
              → Function f
              → Function g
              → Γ ∙ F ⊢ wk1 f ∘ var 0 [conv↑] wk1 g ∘ var 0 ∷ G
              → Γ ⊢ f [conv↓] g ∷ Π F ▹ G
    Σ-η       : ∀ {p r F G}
              → Γ ⊢ p ∷ Σ F ▹ G
              → Γ ⊢ r ∷ Σ F ▹ G
              → Product p
              → Product r
              → Γ ⊢ fst p [conv↑] fst r ∷ F
              → Γ ⊢ snd p [conv↑] snd r ∷ G [ fst p ]
              → Γ ⊢ p [conv↓] r ∷ Σ F ▹ G
    η-unit    : ∀ {k l}
              → Γ ⊢ k ∷ Unit
              → Γ ⊢ l ∷ Unit
              → Whnf k
              → Whnf l
              → Γ ⊢ k [conv↓] l ∷ Unit

star-refl : ∀ {Γ} → ⊢ Γ → Γ ⊢ star [conv↓] star ∷ Unit
star-refl ⊢Γ = η-unit (starⱼ ⊢Γ) (starⱼ ⊢Γ) starₙ starₙ


------------------------------------------------------------------------
-- A variant of Coherently with an erased field
------------------------------------------------------------------------

{-# OPTIONS --cubical --guardedness #-}

import Equality.Path as P

module Lens.Non-dependent.Higher.Coherently.Coinductive.Erased
  {e⁺} (eq : ∀ {a p} → P.Equality-with-paths a p e⁺) where

open P.Derived-definitions-and-properties eq

import Equality.Path.Univalence as EPU
open import Logical-equivalence using (_⇔_)
open import Prelude

open import Bijection equality-with-J as B using (_↔_)
import Bijection P.equality-with-J as PB
open import Equality.Path.Isomorphisms eq
open import Equivalence equality-with-J as Eq using (_≃_)
import Equivalence P.equality-with-J as PEq
open import Equivalence.Erased equality-with-J as EEq using (_≃ᴱ_)
open import Function-universe equality-with-J hiding (_∘_)
import Function-universe P.equality-with-J as PF
open import H-level.Truncation.Propositional.One-step eq as O
  using (∥_∥¹; ∥∥¹ᴱ→∥∥¹)
open import H-level.Truncation.Propositional.One-step.Erased eq as OE
  using (∥_∥¹ᴱ)
open import Univalence-axiom equality-with-J
import Univalence-axiom P.equality-with-J as PU

open import Lens.Non-dependent.Higher.Coherently.Coinductive eq as C
  using (Coherently)

private
  variable
    a b ℓ p p₁ p₂ : Level
    A A₁ A₂ B C   : Type a
    x y           : A
    f             : A → B

-- A variant of
-- Lens.Non-dependent.Higher.Coherently.Coinductive.Coherently with an
-- erased "coherent" field.

record Coherentlyᴱ
         {A : Type a} {B : Type b}
         (P : {A : Type a} → (A → B) → Type p)
         (@0 step : {A : Type a} (f : A → B) → P f → ∥ A ∥¹ᴱ → B)
         (f : A → B) : Type p where
  coinductive
  field
    property    : P f
    @0 coherent : Coherentlyᴱ P step (step f property)

open Coherentlyᴱ public

-- A preservation lemma for Coherentlyᴱ. (See also Coherentlyᴱ-cong
-- below.)

@0 Coherentlyᴱ-cong′ :
  {P : {A : Type a} → (A → B) → Type p}
  {step : {A : Type a} (f : A → B) → P f → ∥ A ∥¹ᴱ → B} →
  Univalence a →
  (A₁≃A₂ : A₁ ≃ A₂) →
  Coherentlyᴱ P step (f ∘ _≃_.to A₁≃A₂) ≃ Coherentlyᴱ P step f
Coherentlyᴱ-cong′ {f = f} {P = P} {step = step} univ A₁≃A₂ =
  ≃-elim₁ univ
    (λ A₁≃A₂ →
       Coherentlyᴱ P step (f ∘ _≃_.to A₁≃A₂) ≃
       Coherentlyᴱ P step f)
    Eq.id
    A₁≃A₂

-- Coherently can be converted to Coherentlyᴱ in a certain way
-- (assuming univalence).

Coherently→Coherentlyᴱ :
  {P : {A : Type a} → (A → B) → Type p}
  {step : {A : Type a} (f : A → B) → P f → ∥ A ∥¹ → B} →
  @0 Univalence a →
  Coherently P step f → Coherentlyᴱ P (λ f p → step f p ∘ ∥∥¹ᴱ→∥∥¹) f
Coherently→Coherentlyᴱ _    c .property = c .C.property
Coherently→Coherentlyᴱ univ c .coherent =
  Coherently→Coherentlyᴱ univ
    (_≃_.from (C.Coherently-cong′ univ O.∥∥¹ᴱ≃∥∥¹)
       (c .C.coherent))

-- In erased contexts Coherently and Coherentlyᴱ are, in a certain
-- sense, logically equivalent (assuming univalence).

@0 Coherentlyᴱ⇔Coherently :
  {P : {A : Type a} → (A → B) → Type p}
  {step : {A : Type a} (f : A → B) → P f → ∥ A ∥¹ → B} →
  Univalence a →
  Coherentlyᴱ P (λ f p → step f p ∘ ∥∥¹ᴱ→∥∥¹) f ⇔ Coherently P step f
Coherentlyᴱ⇔Coherently univ ._⇔_.from = Coherently→Coherentlyᴱ univ

Coherentlyᴱ⇔Coherently univ ._⇔_.to c .C.property = c .property
Coherentlyᴱ⇔Coherently univ ._⇔_.to c .C.coherent =
  Coherentlyᴱ⇔Coherently univ ._⇔_.to
    (_≃_.to (Coherentlyᴱ-cong′ univ O.∥∥¹ᴱ≃∥∥¹)
       (c .coherent))

private

  -- A preservation lemma for Coherentlyᴱ.
  --
  -- The lemma does not use the univalence argument, instead it uses
  -- EPU.univ (and EPU.≃⇒≡) directly.

  Coherentlyᴱ-cong-≡ :
    Block "Coherentlyᴱ-cong-≡" →
    {A : Type a}
    {P₁ P₂ : {A : Type a} → (A → B) → Type p}
    {step₁ : {A : Type a} (f : A → B) → P₁ f → ∥ A ∥¹ᴱ → B}
    {step₂ : {A : Type a} (f : A → B) → P₂ f → ∥ A ∥¹ᴱ → B}
    {f : A → B} →
    Univalence p →
    (P₁≃P₂ : {A : Type a} (f : A → B) → P₁ f ≃ P₂ f) →
    ({A : Type a} (f : A → B) (x : P₂ f) →
     step₁ f (_≃_.from (P₁≃P₂ f) x) ≡ step₂ f x) →
    Coherentlyᴱ P₁ step₁ f P.≡ Coherentlyᴱ P₂ step₂ f
  Coherentlyᴱ-cong-≡
    {a = a} {B = B} {p = p} ⊠ {P₁ = P₁} {P₂ = P₂}
    {step₁ = step₁} {step₂ = step₂} {f = f} _ P₁≃P₂′ step₁≡step₂ =

    P.cong (λ ((P , step) :
              ∃ λ (P : (A : Type a) → (A → B) → Type p) →
                  {A : Type a} (f : A → B) → P A f → ∥ A ∥¹ᴱ → B) →
             Coherentlyᴱ (P _) step f) $

    Σ-≡,≡→≡′

      (P.⟨ext⟩ λ A → P.⟨ext⟩ λ (f : A → B) →
       EPU.≃⇒≡ (P₁≃P₂ f))

      (P.implicit-extensionality P.ext λ A → P.⟨ext⟩ λ f →
         P.subst (λ P → {A : Type a} (f : A → B) → P A f → ∥ A ∥¹ᴱ → B)
           (P.⟨ext⟩ λ A → P.⟨ext⟩ λ (f : A → B) →
            EPU.≃⇒≡ (P₁≃P₂ f))
           step₁ f                                                       P.≡⟨ P.trans (P.cong (_$ f) $ P.sym $
                                                                                P.push-subst-implicit-application
                                                                                (P.⟨ext⟩ λ A → P.⟨ext⟩ λ (f : A → B) → EPU.≃⇒≡ (P₁≃P₂ f))
                                                                                (λ P A → (f : A → B) → P A f → ∥ A ∥¹ᴱ → B)
                                                                                {f = const _} {g = step₁}) $
                                                                              P.sym $ P.push-subst-application
                                                                                (P.⟨ext⟩ λ A → P.⟨ext⟩ λ (f : A → B) → EPU.≃⇒≡ (P₁≃P₂ f))
                                                                                (λ P f → P A f → ∥ A ∥¹ᴱ → B)
                                                                                {f = const f} {g = step₁} ⟩
         P.subst (λ P → P A f → ∥ A ∥¹ᴱ → B)
           (P.⟨ext⟩ λ A → P.⟨ext⟩ λ (f : A → B) →
            EPU.≃⇒≡ (P₁≃P₂ f))
           (step₁ f)                                                     P.≡⟨⟩

         P.subst (λ P → P → ∥ A ∥¹ᴱ → B)
           (EPU.≃⇒≡ (P₁≃P₂ f))
           (step₁ f)                                                     P.≡⟨ P.sym $
                                                                              PU.transport-theorem
                                                                                (λ P → P → ∥ A ∥¹ᴱ → B)
                                                                                (PF.→-cong₁ _)
                                                                                (λ _ → P.refl)
                                                                                EPU.univ
                                                                                (P₁≃P₂ f)
                                                                                (step₁ f) ⟩
         PF.→-cong₁ _ (P₁≃P₂ f) (step₁ f)                                P.≡⟨⟩

         step₁ f ∘ PEq._≃_.from (P₁≃P₂ f)                                P.≡⟨ P.⟨ext⟩ (_↔_.to ≡↔≡ ∘ step₁≡step₂ f) ⟩∎

         step₂ f                                                         ∎)
    where
    P₁≃P₂ : {A : Type a} (f : A → B) → P₁ f PEq.≃ P₂ f
    P₁≃P₂ f = _↔_.to ≃↔≃ (P₁≃P₂′ f)

    Σ-≡,≡→≡′ :
      {P : A → Type b} {p₁ p₂ : Σ A P} →
      (p : proj₁ p₁ P.≡ proj₁ p₂) →
      P.subst P p (proj₂ p₁) P.≡ proj₂ p₂ →
      p₁ P.≡ p₂
    Σ-≡,≡→≡′ {P = P} {p₁ = _ , y₁} {p₂ = _ , y₂} p q i =
      p i , lemma i
      where
      lemma : P.[ (λ i → P (p i)) ] y₁ ≡ y₂
      lemma = PB._↔_.from (P.heterogeneous↔homogeneous _) q

  -- A "computation rule".

  to-Coherentlyᴱ-cong-≡-property :
    (bl : Block "Coherentlyᴱ-cong-≡")
    {A : Type a} {B : Type b}
    {P₁ P₂ : {A : Type a} → (A → B) → Type p}
    {step₁ : {A : Type a} (f : A → B) → P₁ f → ∥ A ∥¹ᴱ → B}
    {step₂ : {A : Type a} (f : A → B) → P₂ f → ∥ A ∥¹ᴱ → B}
    {f : A → B}
    (univ : Univalence p)
    (P₁≃P₂ : {A : Type a} (f : A → B) → P₁ f ≃ P₂ f)
    (step₁≡step₂ :
       {A : Type a} (f : A → B) (x : P₂ f) →
       step₁ f (_≃_.from (P₁≃P₂ f) x) ≡ step₂ f x)
    (c : Coherentlyᴱ P₁ step₁ f) →
    PU.≡⇒→ (Coherentlyᴱ-cong-≡ bl univ P₁≃P₂ step₁≡step₂) c .property
      P.≡
    _≃_.to (P₁≃P₂ f) (c .property)
  to-Coherentlyᴱ-cong-≡-property
    ⊠ {P₁ = P₁} {P₂ = P₂} {f = f} _ P₁≃P₂ step₁≡step₂ c =

    P.transport (λ _ → P₂ f) P.0̲
      (_≃_.to (P₁≃P₂ f) (P.transport (λ _ → P₁ f) P.0̲ (c .property)))  P.≡⟨ P.cong (_$ _≃_.to (P₁≃P₂ f)
                                                                                (P.transport (λ _ → P₁ f) P.0̲ (c .property))) $
                                                                            P.transport-refl P.0̲ ⟩

    _≃_.to (P₁≃P₂ f) (P.transport (λ _ → P₁ f) P.0̲ (c .property))      P.≡⟨ P.cong (λ g → _≃_.to (P₁≃P₂ f) (g (c .property))) $
                                                                            P.transport-refl P.0̲ ⟩∎
    _≃_.to (P₁≃P₂ f) (c .property)                                     ∎

  -- Another "computation rule".

  from-Coherentlyᴱ-cong-≡-property :
    (bl : Block "Coherentlyᴱ-cong-≡")
    {A : Type a} {B : Type b}
    {P₁ P₂ : {A : Type a} → (A → B) → Type p}
    {step₁ : {A : Type a} (f : A → B) → P₁ f → ∥ A ∥¹ᴱ → B}
    {step₂ : {A : Type a} (f : A → B) → P₂ f → ∥ A ∥¹ᴱ → B}
    {f : A → B}
    (univ : Univalence p)
    (P₁≃P₂ : {A : Type a} (f : A → B) → P₁ f ≃ P₂ f)
    (step₁≡step₂ :
       {A : Type a} (f : A → B) (x : P₂ f) →
       step₁ f (_≃_.from (P₁≃P₂ f) x) ≡ step₂ f x)
    (c : Coherentlyᴱ P₂ step₂ f) →
    PEq._≃_.from
      (PU.≡⇒≃ (Coherentlyᴱ-cong-≡ bl {step₁ = step₁} univ
                 P₁≃P₂ step₁≡step₂))
      c .property P.≡
    _≃_.from (P₁≃P₂ f) (c .property)
  from-Coherentlyᴱ-cong-≡-property
    ⊠ {P₁ = P₁} {P₂ = P₂} {f = f} _ P₁≃P₂ step₁≡step₂ c =

    P.transport (λ _ → P₁ f) P.0̲
      (_≃_.from (P₁≃P₂ f) (P.transport (λ _ → P₂ f) P.0̲ (c .property)))  P.≡⟨ P.cong (_$ _≃_.from (P₁≃P₂ f)
                                                                                (P.transport (λ _ → P₂ f) P.0̲ (c .property))) $
                                                                              P.transport-refl P.0̲ ⟩

    _≃_.from (P₁≃P₂ f) (P.transport (λ _ → P₂ f) P.0̲ (c .property))      P.≡⟨ P.cong (λ g → _≃_.from (P₁≃P₂ f) (g (c .property))) $
                                                                              P.transport-refl P.0̲ ⟩∎
    _≃_.from (P₁≃P₂ f) (c .property)                                     ∎

  -- A preservation lemma for Coherentlyᴱ.
  --
  -- The two directions of this equivalence compute the property
  -- fields in certain ways, see the "unit tests" below.
  --
  -- Note that P₁ and P₂ have to target the same universe. A more
  -- general result is given below (Coherentlyᴱ-cong).

  Coherentlyᴱ-cong-≃ᴱ :
    {A : Type a}
    {P₁ P₂ : {A : Type a} → (A → B) → Type p}
    {@0 step₁ : {A : Type a} (f : A → B) → P₁ f → ∥ A ∥¹ᴱ → B}
    {@0 step₂ : {A : Type a} (f : A → B) → P₂ f → ∥ A ∥¹ᴱ → B}
    {f : A → B} →
    @0 Univalence p →
    (P₁≃P₂ : {A : Type a} (f : A → B) → P₁ f ≃ᴱ P₂ f) →
    @0 ({A : Type a} (f : A → B) (x : P₂ f) →
        step₁ f (_≃ᴱ_.from (P₁≃P₂ f) x) ≡ step₂ f x) →
    Coherentlyᴱ P₁ step₁ f ≃ᴱ Coherentlyᴱ P₂ step₂ f
  Coherentlyᴱ-cong-≃ᴱ
    {P₁ = P₁} {P₂ = P₂} {step₁ = step₁} {step₂ = step₂} {f = f}
    univ P₁≃P₂ step₁≡step₂ =

    block λ bl →
    EEq.[≃]→≃ᴱ
      (EEq.[proofs]
         (Eq.with-other-inverse
            (Eq.with-other-function
               (equiv bl)
               (to bl)
               (_↔_.from ≡↔≡ ∘ ≡to bl))
            (from bl)
            (_↔_.from ≡↔≡ ∘ ≡from bl)))

    where

    @0 equiv :
      Block "Coherentlyᴱ-cong-≡" →
      Coherentlyᴱ P₁ step₁ f ≃ Coherentlyᴱ P₂ step₂ f
    equiv bl =
      _↔_.from ≃↔≃ $ PU.≡⇒≃ $
      Coherentlyᴱ-cong-≡ bl univ (EEq.≃ᴱ→≃ ∘ P₁≃P₂) step₁≡step₂

    to :
      Block "Coherentlyᴱ-cong-≡" →
      Coherentlyᴱ P₁ step₁ f → Coherentlyᴱ P₂ step₂ f
    to _  c .property = _≃ᴱ_.to (P₁≃P₂ f) (c .property)
    to bl c .coherent =
      P.subst
        (Coherentlyᴱ P₂ step₂)
        (P.cong (step₂ f) $
         to-Coherentlyᴱ-cong-≡-property
           bl univ (EEq.≃ᴱ→≃ ∘ P₁≃P₂) step₁≡step₂ c) $
      _≃_.to (equiv bl) c .coherent

    @0 ≡to : ∀ bl c → _≃_.to (equiv bl) c P.≡ to bl c
    ≡to bl c i .property = to-Coherentlyᴱ-cong-≡-property
                             bl univ (EEq.≃ᴱ→≃ ∘ P₁≃P₂) step₁≡step₂ c i
    ≡to bl c i .coherent = lemma i
      where
      lemma :
        P.[ (λ i → Coherentlyᴱ P₂ step₂
                     (step₂ f (to-Coherentlyᴱ-cong-≡-property
                                 bl univ (EEq.≃ᴱ→≃ ∘ P₁≃P₂)
                                 step₁≡step₂ c i))) ]
          _≃_.to (equiv bl) c .coherent ≡
          P.subst
            (Coherentlyᴱ P₂ step₂)
            (P.cong (step₂ f) $
             to-Coherentlyᴱ-cong-≡-property
               bl univ (EEq.≃ᴱ→≃ ∘ P₁≃P₂) step₁≡step₂ c)
            (_≃_.to (equiv bl) c .coherent)
      lemma =
        PB._↔_.from (P.heterogeneous↔homogeneous _) P.refl

    from :
      Block "Coherentlyᴱ-cong-≡" →
      Coherentlyᴱ P₂ step₂ f → Coherentlyᴱ P₁ step₁ f
    from _  c .property = _≃ᴱ_.from (P₁≃P₂ f) (c .property)
    from bl c .coherent =
      P.subst
        (Coherentlyᴱ P₁ step₁)
        (P.cong (step₁ f) $
         from-Coherentlyᴱ-cong-≡-property
           bl univ (EEq.≃ᴱ→≃ ∘ P₁≃P₂) step₁≡step₂ c) $
      _≃_.from (equiv bl) c .coherent

    @0 ≡from : ∀ bl c → _≃_.from (equiv bl) c P.≡ from bl c
    ≡from bl c i .property = from-Coherentlyᴱ-cong-≡-property
                               bl univ (EEq.≃ᴱ→≃ ∘ P₁≃P₂)
                               step₁≡step₂ c i
    ≡from bl c i .coherent = lemma i
      where
      lemma :
        P.[ (λ i → Coherentlyᴱ P₁ step₁
                     (step₁ f (from-Coherentlyᴱ-cong-≡-property
                                 bl univ (EEq.≃ᴱ→≃ ∘ P₁≃P₂)
                                 step₁≡step₂ c i))) ]
          _≃_.from (equiv bl) c .coherent ≡
          P.subst
            (Coherentlyᴱ P₁ step₁)
            (P.cong (step₁ f) $
             from-Coherentlyᴱ-cong-≡-property
               bl univ (EEq.≃ᴱ→≃ ∘ P₁≃P₂) step₁≡step₂ c)
            (_≃_.from (equiv bl) c .coherent)
      lemma =
        PB._↔_.from (P.heterogeneous↔homogeneous _) P.refl

  -- Unit tests that ensure that Coherentlyᴱ-cong-≃ᴱ computes the
  -- property fields in certain ways.

  module _
    {A : Type a}
    {P₁ P₂ : {A : Type a} → (A → B) → Type p}
    {@0 step₁ : {A : Type a} (f : A → B) → P₁ f → ∥ A ∥¹ᴱ → B}
    {@0 step₂ : {A : Type a} (f : A → B) → P₂ f → ∥ A ∥¹ᴱ → B}
    {f : A → B}
    {@0 univ : Univalence p}
    {P₁≃P₂ : {A : Type a} (f : A → B) → P₁ f ≃ᴱ P₂ f}
    {@0 step₁≡step₂ :
       {A : Type a} (f : A → B) (x : P₂ f) →
       step₁ f (_≃ᴱ_.from (P₁≃P₂ f) x) ≡ step₂ f x}
    where

    _ :
      {c : Coherentlyᴱ P₁ step₁ f} →
      _≃ᴱ_.to (Coherentlyᴱ-cong-≃ᴱ univ P₁≃P₂ step₁≡step₂) c .property ≡
      _≃ᴱ_.to (P₁≃P₂ f) (c .property)
    _ = refl _

    _ :
      {c : Coherentlyᴱ P₂ step₂ f} →
      _≃ᴱ_.from (Coherentlyᴱ-cong-≃ᴱ {step₁ = step₁} univ
                   P₁≃P₂ step₁≡step₂)
        c .property ≡
      _≃ᴱ_.from (P₁≃P₂ f) (c .property)
    _ = refl _

  -- A lemma involving Coherentlyᴱ and ↑.

  Coherentlyᴱ-↑ :
    {A : Type a}
    {P : {A : Type a} → (A → B) → Type p}
    {@0 step : {A : Type a} (f : A → B) → P f → ∥ A ∥¹ᴱ → B}
    {f : A → B} →
    Coherentlyᴱ (↑ ℓ ∘ P) ((_∘ lower) ∘ step) f ≃
    Coherentlyᴱ P step f
  Coherentlyᴱ-↑ {ℓ = ℓ} {P = P} {step = step} =
    Eq.↔→≃
      to
      from
      (_↔_.from ≡↔≡ ∘ to-from)
      (_↔_.from ≡↔≡ ∘ from-to)
    where
    to :
      Coherentlyᴱ (↑ ℓ ∘ P) ((_∘ lower) ∘ step) f →
      Coherentlyᴱ P step f
    to c .property = lower (c .property)
    to c .coherent = to (c .coherent)

    from :
      Coherentlyᴱ P step f →
      Coherentlyᴱ (↑ ℓ ∘ P) ((_∘ lower) ∘ step) f
    from c .property = lift (c .property)
    from c .coherent = from (c .coherent)

    to-from :
      (c : Coherentlyᴱ P step f) →
      to (from c) P.≡ c
    to-from c i .property = c .property
    to-from c i .coherent = to-from (c .coherent) i

    from-to :
      (c : Coherentlyᴱ (↑ ℓ ∘ P) ((_∘ lower) ∘ step) f) →
      from (to c) P.≡ c
    from-to c i .property = c .property
    from-to c i .coherent = from-to (c .coherent) i

-- A preservation lemma for Coherentlyᴱ.
--
-- The two directions of this equivalence compute the property
-- fields in certain ways, see the "unit tests" below.

Coherentlyᴱ-cong :
  {A : Type a}
  {P₁ : {A : Type a} → (A → B) → Type p₁}
  {P₂ : {A : Type a} → (A → B) → Type p₂}
  {@0 step₁ : {A : Type a} (f : A → B) → P₁ f → ∥ A ∥¹ᴱ → B}
  {@0 step₂ : {A : Type a} (f : A → B) → P₂ f → ∥ A ∥¹ᴱ → B}
  {f : A → B} →
  @0 Univalence (p₁ ⊔ p₂) →
  (P₁≃P₂ : {A : Type a} (f : A → B) → P₁ f ≃ᴱ P₂ f) →
  @0 ({A : Type a} (f : A → B) (x : P₂ f) →
      step₁ f (_≃ᴱ_.from (P₁≃P₂ f) x) ≡ step₂ f x) →
  Coherentlyᴱ P₁ step₁ f ≃ᴱ Coherentlyᴱ P₂ step₂ f
Coherentlyᴱ-cong
  {p₁ = p₁} {p₂ = p₂}
  {P₁ = P₁} {P₂ = P₂} {step₁ = step₁} {step₂ = step₂} {f = f}
  univ P₁≃P₂ step₁≡step₂ =

  Coherentlyᴱ P₁ step₁ f                          ↔⟨ inverse Coherentlyᴱ-↑ ⟩

  Coherentlyᴱ (↑ p₂ ∘ P₁) ((_∘ lower) ∘ step₁) f  ↝⟨ Coherentlyᴱ-cong-≃ᴱ
                                                       univ
                                                       (λ f →
    ↑ p₂ (P₁ f)                                           ↔⟨ B.↑↔ ⟩
    P₁ f                                                  ↝⟨ P₁≃P₂ f ⟩
    P₂ f                                                  ↔⟨ inverse B.↑↔ ⟩□
    ↑ p₁ (P₂ f)                                           □)
                                                       ((_∘ lower) ∘ step₁≡step₂) ⟩

  Coherentlyᴱ (↑ p₁ ∘ P₂) ((_∘ lower) ∘ step₂) f  ↔⟨ Coherentlyᴱ-↑ ⟩□

  Coherentlyᴱ P₂ step₂ f                          □

-- Unit tests that ensure that Coherentlyᴱ-cong computes the property
-- fields in certain ways.

module _
  {A : Type a}
  {P₁ : {A : Type a} → (A → B) → Type p₁}
  {P₂ : {A : Type a} → (A → B) → Type p₂}
  {@0 step₁ : {A : Type a} (f : A → B) → P₁ f → ∥ A ∥¹ᴱ → B}
  {@0 step₂ : {A : Type a} (f : A → B) → P₂ f → ∥ A ∥¹ᴱ → B}
  {f : A → B}
  {@0 univ : Univalence (p₁ ⊔ p₂)}
  {P₁≃P₂ : {A : Type a} (f : A → B) → P₁ f ≃ᴱ P₂ f}
  {@0 step₁≡step₂ :
     {A : Type a} (f : A → B) (x : P₂ f) →
     step₁ f (_≃ᴱ_.from (P₁≃P₂ f) x) ≡ step₂ f x}
  where

  _ :
    {c : Coherentlyᴱ P₁ step₁ f} →
    _≃ᴱ_.to (Coherentlyᴱ-cong univ P₁≃P₂ step₁≡step₂) c .property ≡
    _≃ᴱ_.to (P₁≃P₂ f) (c .property)
  _ = refl _

  _ :
    {c : Coherentlyᴱ P₂ step₂ f} →
    _≃ᴱ_.from (Coherentlyᴱ-cong {step₁ = step₁} univ P₁≃P₂ step₁≡step₂)
      c .property ≡
    _≃ᴱ_.from (P₁≃P₂ f) (c .property)
  _ = refl _

-- A "computation rule".

subst-Coherentlyᴱ-property :
  ∀ {B : Type b} {P : C → {A : Type a} → (A → B) → Type p}
    {@0 step : (c : C) {A : Type a} (f : A → B) → P c f → ∥ A ∥¹ᴱ → B}
    {f : C → A → B} {eq : x ≡ y} {c} →
  subst (λ x → Coherentlyᴱ (P x) (step x) (f x)) eq c .property ≡
  subst (λ x → P x (f x)) eq (c .property)
subst-Coherentlyᴱ-property
  {P = P} {step = step} {f = f} {eq = eq} {c = c} =
  elim¹
    (λ eq →
       subst (λ x → Coherentlyᴱ (P x) (step x) (f x)) eq c .property ≡
       subst (λ x → P x (f x)) eq (c .property))
    (subst (λ x → Coherentlyᴱ (P x) (step x) (f x)) (refl _) c .property  ≡⟨ cong property $ subst-refl _ _ ⟩
     c .property                                                          ≡⟨ sym $ subst-refl _ _ ⟩∎
     subst (λ x → P x (f x)) (refl _) (c .property)                       ∎)
    eq


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

open import HoTT

module groups.HomSequence where

infix 15 _⊣|ᴳ
infixr 10 _→⟨_⟩ᴳ_

data HomSequence {i} : (G : Group i) (H : Group i) → Type (lsucc i) where
  _⊣|ᴳ : (G : Group i) → HomSequence G G
  _→⟨_⟩ᴳ_ : (G : Group i) {H K : Group i}
    → (G →ᴳ H) → HomSequence H K
    → HomSequence G K

HomSeq-++ : ∀ {i} {G H K : Group i}
  → HomSequence G H → HomSequence H K → HomSequence G K
HomSeq-++ (_ ⊣|ᴳ) seq = seq
HomSeq-++ (_ →⟨ φ ⟩ᴳ seq₁) seq₂ = _ →⟨ φ ⟩ᴳ HomSeq-++ seq₁ seq₂

HomSeq-snoc : ∀ {i} {G H K : Group i}
  → HomSequence G H → (H →ᴳ K) → HomSequence G K
HomSeq-snoc seq φ = HomSeq-++ seq (_ →⟨ φ ⟩ᴳ _ ⊣|ᴳ)

{- maps between two hom sequences -}

infix 15 _↓|ᴳ
infixr 10 _↓⟨_⟩ᴳ_

data HomSeqMap {i₀ i₁} : {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
  → HomSequence G₀ H₀ → HomSequence G₁ H₁
  → (G₀ →ᴳ G₁) → (H₀ →ᴳ H₁) → Type (lsucc (lmax i₀ i₁)) where
  _↓|ᴳ : {G₀ : Group i₀} {G₁ : Group i₁} (ξ : G₀ →ᴳ G₁) → HomSeqMap (G₀ ⊣|ᴳ) (G₁ ⊣|ᴳ) ξ ξ
  _↓⟨_⟩ᴳ_ : {G₀ H₀ K₀ : Group i₀} {G₁ H₁ K₁ : Group i₁}
    → {φ : G₀ →ᴳ H₀} {seq₀ : HomSequence H₀ K₀}
    → {ψ : G₁ →ᴳ H₁} {seq₁ : HomSequence H₁ K₁}
    → (ξG : G₀ →ᴳ G₁) {ξH : H₀ →ᴳ H₁} {ξK : K₀ →ᴳ K₁}
    → CommSquareᴳ φ ψ ξG ξH
    → HomSeqMap seq₀ seq₁ ξH ξK
    → HomSeqMap (G₀ →⟨ φ ⟩ᴳ seq₀) (G₁ →⟨ ψ ⟩ᴳ seq₁) ξG ξK

HomSeqMap-snoc : ∀ {i₀ i₁} {G₀ H₀ K₀ : Group i₀} {G₁ H₁ K₁ : Group i₁}
  {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
  {φ₀ : H₀ →ᴳ K₀} {φ₁ : H₁ →ᴳ K₁}
  {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁} {ξK : K₀ →ᴳ K₁}
  → HomSeqMap seq₀ seq₁ ξG ξH
  → CommSquareᴳ φ₀ φ₁ ξH ξK
  → HomSeqMap (HomSeq-snoc seq₀ φ₀) (HomSeq-snoc seq₁ φ₁) ξG ξK
HomSeqMap-snoc (ξG ↓|ᴳ) □ = ξG ↓⟨ □ ⟩ᴳ _ ↓|ᴳ
HomSeqMap-snoc (ξG ↓⟨ □₁ ⟩ᴳ seq) □₂ = ξG ↓⟨ □₁ ⟩ᴳ HomSeqMap-snoc seq □₂

{- equivalences between two hom sequences -}

is-seqᴳ-equiv : ∀ {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
  {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
  {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁}
  → HomSeqMap seq₀ seq₁ ξG ξH
  → Type (lmax i₀ i₁)
is-seqᴳ-equiv (ξ ↓|ᴳ) = is-equiv (GroupHom.f ξ)
is-seqᴳ-equiv (ξ ↓⟨ _ ⟩ᴳ seq) = is-equiv (GroupHom.f ξ) × is-seqᴳ-equiv seq

is-seqᴳ-equiv-snoc : ∀ {i₀ i₁} {G₀ H₀ K₀ : Group i₀} {G₁ H₁ K₁ : Group i₁}
  {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
  {φ₀ : H₀ →ᴳ K₀} {φ₁ : H₁ →ᴳ K₁}
  {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁} {ξK : K₀ →ᴳ K₁}
  {seq-map : HomSeqMap seq₀ seq₁ ξG ξH}
  {cs : CommSquareᴳ φ₀ φ₁ ξH ξK}
  → is-seqᴳ-equiv seq-map → is-equiv (GroupHom.f ξK)
  → is-seqᴳ-equiv (HomSeqMap-snoc seq-map cs)
is-seqᴳ-equiv-snoc {seq-map = ξG ↓|ᴳ} ξG-is-equiv ξH-is-equiv = ξG-is-equiv , ξH-is-equiv
is-seqᴳ-equiv-snoc {seq-map = ξG ↓⟨ _ ⟩ᴳ seq} (ξG-is-equiv , seq-is-equiv) ξH-is-equiv =
  ξG-is-equiv , is-seqᴳ-equiv-snoc seq-is-equiv ξH-is-equiv

private
  is-seqᴳ-equiv-head : ∀ {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
    {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
    {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁}
    {seq-map : HomSeqMap seq₀ seq₁ ξG ξH}
    → is-seqᴳ-equiv seq-map → is-equiv (GroupHom.f ξG)
  is-seqᴳ-equiv-head {seq-map = ξ ↓|ᴳ} ise = ise
  is-seqᴳ-equiv-head {seq-map = ξ ↓⟨ _ ⟩ᴳ _} ise = fst ise

  is-seqᴳ-equiv-last : ∀ {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
    {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
    {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁}
    {seq-map : HomSeqMap seq₀ seq₁ ξG ξH}
    → is-seqᴳ-equiv seq-map → is-equiv (GroupHom.f ξH)
  is-seqᴳ-equiv-last {seq-map = ξ ↓|ᴳ} ise = ise
  is-seqᴳ-equiv-last {seq-map = _ ↓⟨ _ ⟩ᴳ rest} (_ , rest-ise) = is-seqᴳ-equiv-last rest-ise

module is-seqᴳ-equiv {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
  {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
  {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁}
  {seq-map : HomSeqMap seq₀ seq₁ ξG ξH}
  (seq-map-is-equiv : is-seqᴳ-equiv seq-map) where

  head = is-seqᴳ-equiv-head seq-map-is-equiv
  last = is-seqᴳ-equiv-last seq-map-is-equiv

HomSeqEquiv : ∀ {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
  (seq₀ : HomSequence G₀ H₀) (seq₁ : HomSequence G₁ H₁)
  (ξG : G₀ →ᴳ G₁) (ξH : H₀ →ᴳ H₁) → Type (lsucc (lmax i₀ i₁))
HomSeqEquiv seq₀ seq₁ ξG ξH = Σ (HomSeqMap seq₀ seq₁ ξG ξH) is-seqᴳ-equiv

HomSeqEquiv-inverse : ∀ {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
  {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
  {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁}
  (equiv : HomSeqEquiv seq₀ seq₁ ξG ξH)
  → HomSeqEquiv seq₁ seq₀
      (GroupIso.g-hom (ξG , is-seqᴳ-equiv-head (snd equiv)))
      (GroupIso.g-hom (ξH , is-seqᴳ-equiv-last (snd equiv)))
HomSeqEquiv-inverse ((ξ ↓|ᴳ) , ξ-ise) =
  (GroupIso.g-hom (ξ , ξ-ise) ↓|ᴳ) , is-equiv-inverse ξ-ise
HomSeqEquiv-inverse ((ξ ↓⟨ □ ⟩ᴳ rest) , (ξ-ise , rest-ise)) =
  (GroupIso.g-hom (ξ , ξ-ise)
    ↓⟨ CommSquareᴳ-inverse-v □ ξ-ise (is-seqᴳ-equiv-head rest-ise) ⟩ᴳ
  fst rest-inverse-equiv) ,
  is-equiv-inverse ξ-ise , snd rest-inverse-equiv
  where
    rest-inverse-equiv = HomSeqEquiv-inverse (rest , rest-ise)

{- Doesn't seem useful.

infix 15 _↕|ᴳ
infixr 10 _↕⟨_⟩ᴳ_

_↕|ᴳ : ∀ {i} {G₀ G₁ : Group i} (iso : G₀ ≃ᴳ G₁)
  → HomSeqEquiv (G₀ ⊣|ᴳ) (G₁ ⊣|ᴳ) (fst iso) (fst iso)
iso ↕|ᴳ = (fst iso ↓|ᴳ) , snd iso

_↕⟨_⟩ᴳ_ : ∀ {i} {G₀ G₁ H₀ H₁ K₀ K₁ : Group i}
  → {φ : G₀ →ᴳ H₀} {seq₀ : HomSequence H₀ K₀}
  → {ψ : G₁ →ᴳ H₁} {seq₁ : HomSequence H₁ K₁}
  → (isoG : G₀ ≃ᴳ G₁) {isoH : H₀ ≃ᴳ H₁} {isoK : K₀ ≃ᴳ K₁}
  → HomCommSquare φ ψ (fst isoG) (fst isoH)
  → HomSeqEquiv seq₀ seq₁ (fst isoH) (fst isoK)
  → HomSeqEquiv (G₀ →⟨ φ ⟩ᴳ seq₀) (G₁ →⟨ ψ ⟩ᴳ seq₁) (fst isoG) (fst isoK)
(ξG , hG-is-equiv) ↕⟨ sqr ⟩ᴳ (seq-map , seq-map-is-equiv) =
  (ξG ↓⟨ sqr ⟩ᴳ seq-map) , hG-is-equiv , seq-map-is-equiv
-}

private
  hom-seq-map-index-type : ∀ {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
    {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
    {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁}
    → ℕ → HomSeqMap seq₀ seq₁ ξG ξH → Type (lmax i₀ i₁)
  hom-seq-map-index-type _     (_ ↓|ᴳ) = Lift ⊤
  hom-seq-map-index-type O     (_↓⟨_⟩ᴳ_ {φ = φ} {ψ = ψ} ξG {ξH} _ _)
    = CommSquareᴳ φ ψ ξG ξH
  hom-seq-map-index-type (S n) (_ ↓⟨ _ ⟩ᴳ seq-map)
    = hom-seq-map-index-type n seq-map

abstract
  hom-seq-map-index : ∀ {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
    {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
    {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁}
    (n : ℕ) (seq-map : HomSeqMap seq₀ seq₁ ξG ξH)
    → hom-seq-map-index-type n seq-map
  hom-seq-map-index _     (_ ↓|ᴳ) = lift tt
  hom-seq-map-index O     (_ ↓⟨ □ ⟩ᴳ _) = □
  hom-seq-map-index (S n) (_ ↓⟨ _ ⟩ᴳ seq-map)
    = hom-seq-map-index n seq-map

private
  hom-seq-equiv-index-type : ∀ {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
    {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
    {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁}
    → ℕ → HomSeqMap seq₀ seq₁ ξG ξH → Type (lmax i₀ i₁)
  hom-seq-equiv-index-type {ξG = ξG} O _ = is-equiv (GroupHom.f ξG)
  hom-seq-equiv-index-type (S _) (_ ↓|ᴳ) = Lift ⊤
  hom-seq-equiv-index-type (S n) (_ ↓⟨ _ ⟩ᴳ seq-map)
    = hom-seq-equiv-index-type n seq-map

abstract
  hom-seq-equiv-index : ∀ {i₀ i₁} {G₀ H₀ : Group i₀} {G₁ H₁ : Group i₁}
    {seq₀ : HomSequence G₀ H₀} {seq₁ : HomSequence G₁ H₁}
    {ξG : G₀ →ᴳ G₁} {ξH : H₀ →ᴳ H₁}
    (n : ℕ) (seq-equiv : HomSeqEquiv seq₀ seq₁ ξG ξH)
    → hom-seq-equiv-index-type n (fst seq-equiv)
  hom-seq-equiv-index O     (seq-map , ise) = is-seqᴳ-equiv-head ise
  hom-seq-equiv-index (S _) ((_ ↓|ᴳ) , _) = lift tt
  hom-seq-equiv-index (S n) ((_ ↓⟨ _ ⟩ᴳ seq-map) , ise)
    = hom-seq-equiv-index n (seq-map , snd ise)


postulate
  P : {A : Set} → A → A → Set
  p : {A : Set} {x : A} → P x x
  q : {A : Set} (x : A) {y : A} → P x y → P x y
  A : Set

record R (F : Set → Set) : Set₁ where
  field
    f₁ : (A → A) → F A → F A
    f₂ : P (f₁ (λ x → x)) (λ (x : F A) → x)

open R ⦃ … ⦄ public

instance

  r : R (λ O → O → O)
  r = record
    { f₁ = λ f g x → f (g x)
    ; f₂ = p
    }

postulate
  F : Set → Set

  instance
    rF : R F

Foo : P (f₁ (λ x → x)) (λ (x : F A) → x)
Foo = q (f₁ (λ x → x)) f₂

-- An internal error has occurred. Please report this as a bug.
-- Location of the error:
-- src/full/Agda/TypeChecking/InstanceArguments.hs:224


------------------------------------------------------------------------
-- An alternative but equivalent definition of the partiality monad
-- (but only for sets), based on the lifting construction in Lifting
------------------------------------------------------------------------

-- The code in this module is based on a suggestion from Paolo
-- Capriotti.

{-# OPTIONS --erased-cubical --safe #-}

open import Prelude hiding (T; ⊥)

module Lifting.Partiality-monad {a : Level} where

open import Equality.Propositional.Cubical
open import Logical-equivalence using (_⇔_)

open import Bijection equality-with-J using (_↔_)
open import Equivalence equality-with-J as Eq using (_≃_)
open import Function-universe equality-with-J hiding (⊥↔⊥)
open import H-level equality-with-J

import Lifting
open import Omega-cpo
import Partiality-monad.Inductive as I
import Partiality-monad.Inductive.Eliminators as IE

private
  module L {A : Set a} = Lifting (Set→ω-cpo A)

-- The partiality monad as an ω-cppo.

partiality-monad : Set a → ω-cppo a a
partiality-monad A = L.cppo {A = A}

-- The partiality monad.

infix 10 _⊥
infix  4 _⊑_

_⊥ : Set a → Type a
A ⊥ = ω-cppo.Carrier (partiality-monad A)

_⊑_ : ∀ {A} → A ⊥ → A ⊥ → Type a
_⊑_ {A = A} = ω-cppo._⊑_ (partiality-monad A)

-- This definition of the partiality monad is isomorphic to the
-- definition in Partiality-monad.Inductive.

private

  argsL :
    ∀ {A} →
    L.Rec-args (λ (_ : A ⊥) → ⌞ A ⌟ I.⊥) (λ x y _ → x I.⊑ y)
  argsL = record
    { pe = I.never
    ; po = I.now
    ; pl = λ _ → I.⨆
    ; pa = λ x⊑y y⊑x p-x p-y p-x⊑p-y p-y⊑p-x →
             subst (const _) (L.antisymmetry x⊑y y⊑x) p-x  ≡⟨ subst-const (L.antisymmetry x⊑y y⊑x) ⟩
             p-x                                           ≡⟨ I.antisymmetry p-x⊑p-y p-y⊑p-x ⟩∎
             p-y                                           ∎
    ; pp = I.⊥-is-set
    ; qr = λ _ → I.⊑-refl
    ; qt = λ _ _ _ _ _ → I.⊑-trans
    ; qe = λ _ → I.never⊑
    ; qu = λ _ → I.upper-bound
    ; ql = λ _ _ _ → I.least-upper-bound
    ; qm = λ { refl → I.⊑-refl _ }
    ; q⨆ = λ s →
             I.now (proj₁ s 0)                    I.⊑⟨ I.upper-bound _ 0 ⟩■
             I.⨆ ((λ n → I.now (proj₁ s n)) , _)  ■
    ; qp = λ _ _ _ → I.⊑-propositional
    }

  argsI : ∀ {A} → IE.Arguments-nd a a ⌞ A ⌟
  argsI {A} = record
    { P  = A ⊥
    ; Q  = _⊑_
    ; pe = L.never
    ; po = L.now
    ; pl = λ _ → L.⨆
    ; pa = λ _ _ → L.antisymmetry
    ; ps = L.Carrier-is-set
    ; qr = λ _ → L.⊑-refl
    ; qt = λ _ _ → L.⊑-trans
    ; qe = λ _ → L.never⊑
    ; qu = λ _ → L.upper-bound
    ; ql = λ _ _ _ → L.least-upper-bound
    ; qp = λ _ _ → L.⊑-propositional
    }

⊥↔⊥ : ∀ {A} → A ⊥ ↔ ⌞ A ⌟ I.⊥
⊥↔⊥ = record
  { surjection = record
    { logical-equivalence = record
      { to   = L.⊥-rec argsL
      ; from = IE.⊥-rec-nd argsI
      }
    ; right-inverse-of = IE.⊥-rec-⊥ (record
        { pe = L.⊥-rec argsL (IE.⊥-rec-nd argsI I.never)  ≡⟨ cong (L.⊥-rec argsL) (IE.⊥-rec-nd-never argsI) ⟩
               L.⊥-rec argsL L.never                      ≡⟨ L.⊥-rec-never _ ⟩∎
               I.never                                    ∎
        ; po = λ x →
                 L.⊥-rec argsL (IE.⊥-rec-nd argsI (I.now x))  ≡⟨ cong (L.⊥-rec argsL) (IE.⊥-rec-nd-now argsI _) ⟩
                 L.⊥-rec argsL (L.now x)                      ≡⟨ L.⊥-rec-now _ _ ⟩∎
                 I.now x ∎
        ; pl = λ s ih →
                 L.⊥-rec argsL (IE.⊥-rec-nd argsI (I.⨆ s))      ≡⟨ cong (L.⊥-rec argsL) (IE.⊥-rec-nd-⨆ argsI _) ⟩
                 L.⊥-rec argsL (L.⨆ (IE.inc-rec-nd argsI s))    ≡⟨ L.⊥-rec-⨆ _ _ ⟩
                 I.⨆ (L.inc-rec argsL (IE.inc-rec-nd argsI s))  ≡⟨ cong I.⨆ $ _↔_.to I.equality-characterisation-increasing ih ⟩∎
                 I.⨆ s                                          ∎
        ; pp = λ _ → I.⊥-is-set
        })
    }
  ; left-inverse-of = L.⊥-rec {Q = λ _ _ _ → ⊤} (record
      { pe = IE.⊥-rec-nd argsI (L.⊥-rec argsL L.never)  ≡⟨ cong (IE.⊥-rec-nd argsI) (L.⊥-rec-never _) ⟩
             IE.⊥-rec-nd argsI I.never                  ≡⟨ IE.⊥-rec-nd-never argsI ⟩∎
             L.never                                    ∎
      ; po = λ x →
               IE.⊥-rec-nd argsI (L.⊥-rec argsL (L.now x))  ≡⟨ cong (IE.⊥-rec-nd argsI) (L.⊥-rec-now _ _) ⟩
               IE.⊥-rec-nd argsI (I.now x)                  ≡⟨ IE.⊥-rec-nd-now argsI _ ⟩∎
               L.now x                                      ∎
      ; pl = λ s ih →
                 IE.⊥-rec-nd argsI (L.⊥-rec argsL (L.⨆ s))      ≡⟨ cong (IE.⊥-rec-nd argsI) (L.⊥-rec-⨆ _ _) ⟩
                 IE.⊥-rec-nd argsI (I.⨆ (L.inc-rec argsL s))    ≡⟨ IE.⊥-rec-nd-⨆ argsI _ ⟩
                 L.⨆ (IE.inc-rec-nd argsI (L.inc-rec argsL s))  ≡⟨ cong L.⨆ $ _↔_.to L.equality-characterisation-increasing (proj₁ ih) ⟩∎
                 L.⨆ s                                          ∎
      ; pa = λ _ _ _ _ _ _ → L.Carrier-is-set _ _
      ; pp = mono₁ 1 L.Carrier-is-set
      ; qp = λ _ _ _ _ _ → refl
      })
  }

⊑≃⊑ : ∀ {A} {x y : A ⊥} →
      (x ⊑ y) ≃ (_↔_.to ⊥↔⊥ x I.⊑ _↔_.to ⊥↔⊥ y)
⊑≃⊑ {x = x} {y} =
  _↔_.to (Eq.⇔↔≃ ext L.⊑-propositional I.⊑-propositional)
    (record
       { to   = L.⊑-rec argsL
       ; from =
                _↔_.to ⊥↔⊥ x I.⊑ _↔_.to ⊥↔⊥ y  ↝⟨ IE.⊑-rec-nd argsI ⟩

                _↔_.from ⊥↔⊥ (_↔_.to ⊥↔⊥ x) ⊑
                _↔_.from ⊥↔⊥ (_↔_.to ⊥↔⊥ y)    ↝⟨ ≡⇒↝ _ (cong₂ _⊑_ (_↔_.left-inverse-of ⊥↔⊥ _)
                                                                   (_↔_.left-inverse-of ⊥↔⊥ _)) ⟩□
                x ⊑ y                          □
       })


module TemporalOps.StrongMonad where

open import CategoryTheory.Instances.Reactive
open import CategoryTheory.Functor
open import CategoryTheory.Monad
open import CategoryTheory.Comonad
open import CategoryTheory.NatTrans
open import CategoryTheory.BCCCs
open import CategoryTheory.CartesianStrength

open import TemporalOps.Next
open import TemporalOps.Delay
open import TemporalOps.Diamond
open import TemporalOps.Diamond.Join
open import TemporalOps.Box
open import TemporalOps.Common.Other
open import TemporalOps.Common.Compare
open import TemporalOps.Common.Rewriting
open import TemporalOps.OtherOps

open import Relation.Binary.PropositionalEquality as ≡ hiding (inspect)
open import Data.Product
open import Data.Sum
open import Data.Nat hiding (_*_)
open import Data.Nat.Properties
            using (+-identityʳ ; +-comm ; +-suc ; +-assoc)

open import Holes.Term using (⌞_⌟)
open import Holes.Cong.Propositional

open Monad M-◇
open Comonad W-□
private module F-◇ = Functor F-◇
private module F-□ = Functor F-□
open ≡.≡-Reasoning

◇-□-Strong : WStrongMonad ℝeactive-cart M-◇ W-cart-□
◇-□-Strong = record
    { st = st-◇
    ; st-nat₁ = st-nat₁-◇
    ; st-nat₂ = st-nat₂-◇
    ; st-λ = st-λ-◇
    ; st-α = st-α-◇
    ; st-η = refl
    ; st-μ = λ{A}{B}{n}{a} -> st-μ-◇ {A}{B}{n}{a}
    }
    where
    open Functor F-◇ renaming (fmap to ◇-f ; omap to ◇-o)
    open Functor F-□ renaming (fmap to □-f ; omap to □-o)
    private module ▹ᵏ-C k = CartesianFunctor (F-cart-delay k)
    private module ▹ᵏ-F k = Functor (F-delay k)
    private module ▹-F = Functor F-▹
    private module ▹-C = CartesianFunctor (F-cart-▹)
    private module □-C = CartesianFunctor (F-cart-□)
    private module □-CW = CartesianComonad (W-cart-□)
    private module □-▹ᵏ k = _⟹_ (□-to-▹ᵏ k)

    st-◇ : ∀(A B : τ) -> □ A ⊗ ◇ B ⇴ ◇ (□ A ⊗ B)
    st-◇ A B n (□A , (k , a)) = k , ▹ᵏ-C.m k (□ A) B n (□-▹ᵏ.at k (□ A) n (δ.at A k □A) , a)

    st-nat₁-◇ : ∀{A B C : τ} (f : A ⇴ B)
             -> ◇-f (□-f f * id) ∘ st-◇ A C ≈ st-◇ B C ∘ □-f f * id
    st-nat₁-◇ {A} {B} {C} f {n} {□A , k , a}
        rewrite ▹ᵏ-C.m-nat₁ k (□-f f)
                {n} {(□-▹ᵏ.at k (□ A) n (δ.at A k □A)) , a}
              | (□-▹ᵏ.nat-cond k {□ A} {□ B} {□-f f} {n} {δ.at A k □A})
        = refl

    st-nat₂-◇ : ∀{A B C : τ} (f : A ⇴ B)
             -> ◇-f (id * f) ∘ st-◇ C A ≈ st-◇ C B ∘ id * ◇-f f
    st-nat₂-◇ {A} {B} {C} f {n} {□C , k , a}
        rewrite ▹ᵏ-C.m-nat₂ k f {n} {□-▹ᵏ.at k (□ C) n (δ.at C k □C) , a} = refl

    st-λ-◇ : ∀{A} -> ◇-f π₂ ∘ st-◇ ⊤ A ≈ π₂
    st-λ-◇ {A} {n} {□⊤ , (k , a)} =
        begin
            ◇-f π₂ n (st-◇ ⊤ A n (□⊤ , (k , a)))
        ≡⟨⟩
            ◇-f π₂ n (k , ▹ᵏ-C.m k (□ ⊤) A n (□-▹ᵏ.at k (□ ⊤) n (δ.at ⊤ n □⊤) , a))
        ≡⟨⟩
            k , ▹ᵏ-F.fmap k π₂ n (▹ᵏ-C.m k (□ ⊤) A n (□-▹ᵏ.at k (□ ⊤) n (δ.at ⊤ n □⊤) , a))
        ≡⟨ cong (λ x -> k , x) lemma ⟩
            k , a
        ∎
        where
        lemma : ▹ᵏ-F.fmap k π₂ ∘ ▹ᵏ-C.m k (□ ⊤) A ≈ π₂
        lemma {n} {t , b} =
            begin
                ▹ᵏ-F.fmap k π₂ n (▹ᵏ-C.m k (□ ⊤) A n (t , b))
            ≡⟨⟩
                ▹ᵏ-F.fmap k π₂ n (▹ᵏ-C.m k (□ ⊤) A n (⌞ t ⌟ , b))
            ≡⟨ cong! (▹ᵏ-F.fmap-id k {□ ⊤}{n}{t}) ⟩
                ▹ᵏ-F.fmap k π₂ n (▹ᵏ-C.m k (□ ⊤) A n (▹ᵏ-F.fmap k ⌞ id ⌟ n t , b))
            ≡⟨⟩
                ▹ᵏ-F.fmap k π₂ n (▹ᵏ-C.m k (□ ⊤) A n (⌞ ▹ᵏ-F.fmap k (u ∘ ε.at ⊤) n t ⌟ , b))
            ≡⟨ cong! (▹ᵏ-F.fmap-∘ k) ⟩
                ▹ᵏ-F.fmap k π₂ n (▹ᵏ-C.m k (□ ⊤) A n (▹ᵏ-F.fmap k u n (▹ᵏ-F.fmap k (ε.at ⊤) n t) , b))
            ≡⟨ cong (▹ᵏ-F.fmap k π₂ n) (sym (▹ᵏ-C.m-nat₁ k u)) ⟩
                ▹ᵏ-F.fmap k π₂ n (▹ᵏ-F.fmap k (u * id) n (▹ᵏ-C.m k ⊤ A n (▹ᵏ-F.fmap k (ε.at ⊤) n t , b)))
            ≡⟨ sym (▹ᵏ-F.fmap-∘ k) ⟩
                ▹ᵏ-F.fmap k (π₂ ∘ u * id) n (▹ᵏ-C.m k ⊤ A n (▹ᵏ-F.fmap k (ε.at ⊤) n t , b))
            ≡⟨⟩
                ▹ᵏ-F.fmap k π₂ n (▹ᵏ-C.m k ⊤ A n (⌞ ▹ᵏ-F.fmap k (ε.at ⊤) n t ⌟ , b))
            ≡⟨ cong! (⊤-eq k n t) ⟩
                ▹ᵏ-F.fmap k π₂ n (▹ᵏ-C.m k ⊤ A n (▹ᵏ-C.u k n top.tt , b))
            ≡⟨ ▹ᵏ-C.unital-left k ⟩
                b
            ∎
            where
            ⊤-eq : ∀ k n t -> ▹ᵏ-F.fmap k (ε.at ⊤) n t ≡ ▹ᵏ-C.u k n top.tt
            ⊤-eq zero n t = refl
            ⊤-eq (suc k) zero t = refl
            ⊤-eq (suc k) (suc n) t = ⊤-eq k n t

    st-α-◇ : ∀{A B C : τ}
          -> st-◇ A (□ B ⊗ C) ∘ id * st-◇ B C ∘ assoc-right ∘ m⁻¹ A B * id
           ≈ ◇-f (assoc-right ∘ m⁻¹ A B * id) ∘ st-◇ (A ⊗ B) C
    st-α-◇ {A} {B} {C} {n} {□A⊗B , (k , a)} =
        begin
            st-◇ A (□ B ⊗ C) n
                (□-f π₁ n □A⊗B , st-◇ B C n (□-f π₂ n □A⊗B , (k , a)))
        ≡⟨ cong (λ x → k , x) (
            begin
                ▹ᵏ-C.m k (□ A) (□ B ⊗ C) n
                    ( □-▹ᵏ.at k (□ A) n (δ.at A k (□-f π₁ n □A⊗B))
                    , ▹ᵏ-C.m k (□ B) C n (□-▹ᵏ.at k (□ B) n (δ.at B n (□-f π₂ n □A⊗B)) , a))
            ≡⟨⟩ -- Naturality and definitional equality
                ▹ᵏ-C.m k (□ A) (□ B ⊗ C) n
                    ((id {delay □ A by k} * ▹ᵏ-C.m k (□ B) C) n (assoc-right {delay □ A by k} {delay □ B by k} {delay C by k} n
                        (((□-▹ᵏ.at k (□ A) * □-▹ᵏ.at k (□ B)) n
                            ((δ.at A * δ.at B) n (m⁻¹ A B n □A⊗B)))
                        , a)))
            ≡⟨ ▹ᵏ-C.associative k  ⟩
                ▹ᵏ-F.fmap k assoc-right n
                    (▹ᵏ-C.m k (□ A ⊗ □ B) C n
                        ( ⌞ ▹ᵏ-C.m k (□ A) (□ B) n
                            ((□-▹ᵏ.at k (□ A) * □-▹ᵏ.at k (□ B)) n
                                ((δ.at A * δ.at B) n (m⁻¹ A B n □A⊗B))) ⌟
                        , a))
            ≡⟨ cong! (lemma k) ⟩
                ▹ᵏ-F.fmap k assoc-right n
                    (▹ᵏ-C.m k (□ A ⊗ □ B) C n
                        ( □-▹ᵏ.at k (□ A ⊗ □ B) n
                            (□-C.m (□ A) (□ B) n ((δ.at A * δ.at B) n (m⁻¹ A B n □A⊗B)))
                        , a))
            ≡⟨⟩
                ▹ᵏ-F.fmap k assoc-right n
                    (▹ᵏ-C.m k (□ A ⊗ □ B) C n
                        ( ⌞ □-▹ᵏ.at k (□ A ⊗ □ B) n
                            (□-f (m⁻¹ A B) n (δ.at (A ⊗ B) k □A⊗B)) ⌟
                        , a))
            ≡⟨ cong! (□-▹ᵏ.nat-cond k {f = m⁻¹ A B} {n} {δ.at (A ⊗ B) k □A⊗B}) ⟩
                ▹ᵏ-F.fmap k assoc-right n
                    ⌞ (▹ᵏ-C.m k (□ A ⊗ □ B) C n
                        (▹ᵏ-F.fmap k (m⁻¹ A B) n
                            (□-▹ᵏ.at k (□ (A ⊗ B)) n (δ.at (A ⊗ B) k □A⊗B))
                        , a)) ⌟
            ≡⟨ cong! (▹ᵏ-C.m-nat₁ k (m⁻¹ A B) {n}
                        {(□-▹ᵏ.at k (□ (A ⊗ B)) n (δ.at (A ⊗ B) k □A⊗B)) , a}) ⟩
                ▹ᵏ-F.fmap k assoc-right n
                    (▹ᵏ-F.fmap k (m⁻¹ A B * id) n (▹ᵏ-C.m k (□ (A ⊗ B)) C n
                        (□-▹ᵏ.at k (□ (A ⊗ B)) n (δ.at (A ⊗ B) k □A⊗B) , a)))
            ≡⟨ sym (▹ᵏ-F.fmap-∘ k) ⟩
                ▹ᵏ-F.fmap k (assoc-right ∘ m⁻¹ A B * id) n
                    (▹ᵏ-C.m k (□ (A ⊗ B)) C n (□-▹ᵏ.at k (□ (A ⊗ B)) n (δ.at (A ⊗ B) k □A⊗B) , a))
            ≡⟨⟩

                ▹ᵏ-F.fmap k (assoc-right ∘ m⁻¹ A B * id) n
                    (▹ᵏ-C.m k (□ (A ⊗ B)) C n (□-▹ᵏ.at k (□ (A ⊗ B)) n (δ.at (A ⊗ B) k □A⊗B) , a))
            ∎
        ) ⟩
            ◇-f (assoc-right ∘ m⁻¹ A B * id) n (st-◇ (A ⊗ B) C n (□A⊗B , (k , a)))
        ∎
        where
        lemma : ∀ k -> ▹ᵏ-C.m k (□ A) (□ B) ∘ □-▹ᵏ.at k (□ A) * □-▹ᵏ.at k (□ B)
                     ≈ □-▹ᵏ.at k (□ A ⊗ □ B) ∘ □-C.m (□ A) (□ B)
        lemma zero = refl
        lemma (suc k) {zero} {□□A , □□B} = refl
        lemma (suc k) {suc n} {□□A , □□B} = lemma k

    st-μ-◇ : ∀{A B : τ}
          -> μ.at (□ A ⊗ B) ∘ ◇-f (st-◇ A B) ∘ st-◇ A (◇ B)
           ≈ st-◇ A B ∘ (id * μ.at B)
    st-μ-◇ {A} {B} {n} {□A , (k , a)} with inspect (compareLeq k n)
    st-μ-◇ {A} {B} {.(k + l)} {□A , k , a} | snd==[ .k + l ] with≡ pf =
        begin
            μ.at (□ A ⊗ B) (k + l) (◇-f (st-◇ A B) (k + l) (st-◇ A (◇ B) (k + l) (□A , (k , a))))
        ≡⟨⟩
            μ-compare (□ A ⊗ B) (k + l) k
                (▹ᵏ-F.fmap k (st-◇ A B) (k + l)
                    (▹ᵏ-C.m k (□ A) (◇ B) (k + l)
                        (□-▹ᵏ.at k (□ A) (k + l) (δ.at A (k + l) □A) , a)))
                            (⌞ compareLeq k (k + l) ⌟)
        ≡⟨ cong! pf ⟩
            μ-compare (□ A ⊗ B) (k + l) k
                (▹ᵏ-F.fmap k (st-◇ A B) (k + l)
                    (▹ᵏ-C.m k (□ A) (◇ B) (k + l)
                        (□-▹ᵏ.at k (□ A) (k + l) (δ.at A (k + l) □A) , a)))
                            (snd==[ k + l ])
        ≡⟨⟩
            μ-shift k l
                (rew (delay-+-left0 k l)
                    (▹ᵏ-F.fmap k (st-◇ A B) (k + l)
                        (▹ᵏ-C.m k (□ A) (◇ B) (k + l)
                            ( □-▹ᵏ.at k (□ A) (k + l) (δ.at A (k + l) □A)
                            , a))))
        ≡⟨ cong (μ-shift k l) (fmap-delay-+-n+0 k l) ⟩
            μ-shift k l
                (st-◇ A B l
                    (rew (delay-+-left0 k l)
                        (▹ᵏ-C.m k (□ A) (◇ B) (k + l)
                            ( □-▹ᵏ.at k (□ A) (k + l) (δ.at A (k + l) □A)
                            , a))))
        ≡⟨ cong (λ x → μ-shift k l (st-◇ A B l x)) (m-delay-+-n+0 k l) ⟩
            μ-shift k l
                (st-◇ A B l
                    ( rew (delay-+-left0 k l) (□-▹ᵏ.at k (□ A) (k + l) (δ.at A (k + l) □A))
                    , rew (delay-+-left0 k l) a))
        ≡⟨ cong (λ x → μ-shift k l (st-◇ A B l (x , _))) (rew-□-left0 k l) ⟩
            μ-shift k l (st-◇ A B l (□A , rew (delay-+-left0 k l) a))
         ≡⟨ lemma k l (rew (delay-+-left0 k l) a) ⟩
            st-◇ A B (k + l) (□A , μ-shift k l (rew (delay-+-left0 k l) a))
        ≡⟨⟩
            st-◇ A B (k + l) (□A , μ-compare B (k + l) k a (⌞ snd==[ k + l ] ⌟))
        ≡⟨ cong! pf ⟩
            st-◇ A B (k + l) (□A , μ.at B (k + l) (k , a))
        ∎
        where
        rew-□-left0 : ∀ k l
             -> rew (delay-+-left0 k l) (□-▹ᵏ.at k (□ A) (k + l) (δ.at A (k + l) □A))
              ≡ □A
        rew-□-left0 zero l = refl
        rew-□-left0 (suc k) l = rew-□-left0 k l

        rew-□ : ∀ k l m
             -> rew (sym (delay-+ k m l)) (□-▹ᵏ.at m (□ A) l (δ.at A m □A))
              ≡ □-▹ᵏ.at (k + m) (□ A) (k + l) (δ.at A (k + m) □A)
        rew-□ zero l m = refl
        rew-□ (suc k) l m = rew-□ k l m

        lemma : ∀ k l a
             -> μ-shift k l (st-◇ A B l (□A , a))
              ≡ st-◇ A B (k + l) (□A , μ-shift k l a)
        lemma k l (m , a) =
            begin
                μ-shift k l (st-◇ A B l (□A , (m , a)))
            ≡⟨⟩
                k + m , rew (sym (delay-+ k m l)) (▹ᵏ-C.m m (□ A) B l (□-▹ᵏ.at m (□ A) l (δ.at A m □A) , a))
            ≡⟨ cong (λ x -> _ , x) (m-delay-+-sym k l m) ⟩
                k + m , ▹ᵏ-C.m (k + m) (□ A) B (k + l)
                            ( rew (sym (delay-+ k m l)) (□-▹ᵏ.at m (□ A) l (δ.at A m □A))
                            , rew (sym (delay-+ k m l)) a)
            ≡⟨ cong (λ x → _ , ▹ᵏ-C.m (k + m) (□ A) B (k + l) (x , _)) (rew-□ k l m) ⟩
                k + m , ▹ᵏ-C.m (k + m) (□ A) B (k + l)
                            ( □-▹ᵏ.at (k + m) (□ A) (k + l) (δ.at A (k + m) □A)
                            , rew (sym (delay-+ k m l)) a)
            ≡⟨⟩
                st-◇ A B (k + l) (□A , (k + m , rew (sym (delay-+ k m l)) a))
            ∎

    st-μ-◇ {A} {B} {.n} {□A , .(n + suc l) , a} | fst==suc[ n + l ] with≡ pf =
        begin
            μ.at (□ A ⊗ B) n (◇-f (st-◇ A B) n (st-◇ A (◇ B) n (□A , (n + suc l , a))))
        ≡⟨⟩
            μ-compare (□ A ⊗ B) n (n + suc l)
                (▹ᵏ-F.fmap (n + suc l) (st-◇ A B) n
                    (▹ᵏ-C.m (n + suc l) (□ A) (◇ B) n
                    (□-▹ᵏ.at (n + suc l) (□ A) n
                    (δ.at A (n + suc l) □A) , a)))
                    (⌞ compareLeq (n + suc l) n ⌟)
        ≡⟨ cong! pf ⟩
            μ-compare (□ A ⊗ B) n (n + suc l)
                (▹ᵏ-F.fmap (n + suc l) (st-◇ A B) n
                    (▹ᵏ-C.m (n + suc l) (□ A) (◇ B) n
                    (□-▹ᵏ.at (n + suc l) (□ A) n
                    (δ.at A (n + suc l) □A) , a)))
                    (fst==suc[ n + l ])
        ≡⟨⟩
            n + suc l , rew (delay-⊤ n l) top.tt
        ≡⟨ cong (λ x -> _ , x) (lemma n l) ⟩
            n + suc l , ▹ᵏ-C.m (n + suc l) (□ A) B n (□-▹ᵏ.at (n + suc l) (□ A) n (δ.at A (n + suc l) □A) , rew (delay-⊤ n l) top.tt)
        ≡⟨⟩
            st-◇ A B n (□A , μ-compare B n (n + suc l) a (⌞ fst==suc[ n + l ] ⌟))
        ≡⟨ cong! pf ⟩
            st-◇ A B n (□A , μ.at B n ((n + suc l) , a))
        ∎
        where
        lemma : ∀ n l
             -> rew (delay-⊤ n l) top.tt
              ≡ ▹ᵏ-C.m (n + suc l) (□ A) B n (□-▹ᵏ.at (n + suc l) (□ A) n (δ.at A (n + suc l) □A)
                                            , rew (delay-⊤ n l) top.tt)
        lemma zero l = refl
        lemma (suc n) l = lemma n l

open WStrongMonad ◇-□-Strong public