{-# OPTIONS --without-K #-}
module SimpleHoTT where

open import Data.Empty
open import Data.Sum renaming (map to _⊎→_)
open import Function renaming (_∘_ to _○_)

infixr 8  _∘_   -- path composition
infix  4  _≡_   -- propositional equality
infix  2  _∎       -- equational reasoning for paths
infixr 2  _≡⟨_⟩_   -- equational reasoning for paths

------------------------------------------------------------------------------
-- Equivalences a la HoTT (using HoTT paths and path induction)

-- Our own version of refl that makes 'a' explicit
data _≡_ {ℓ} {A : Set ℓ} : (a b : A) → Set ℓ where
  refl : (a : A) → (a ≡ a)

-- not sure where else to put this [Z]
hetType : {A B : Set} → (a : A) → A ≡ B → B
hetType a (refl _) = a

-- J
pathInd : ∀ {u ℓ} → {A : Set u} →
          (C : {x y : A} → x ≡ y → Set ℓ) →
          (c : (x : A) → C (refl x)) →
          ({x y : A} (p : x ≡ y) → C p)
pathInd C c (refl x) = c x

basedPathInd : {A : Set} → (a : A) → (C : (x : A) → (a ≡ x) → Set) →
  C a (refl a) → ((x : A) (p : a ≡ x) → C x p)
basedPathInd a C c .a (refl .a) = c

! : ∀ {u} → {A : Set u} {x y : A} → (x ≡ y) → (y ≡ x)
! = pathInd (λ {x} {y} _ → y ≡ x) refl

_∘_ : ∀ {u} → {A : Set u} → {x y z : A} → (x ≡ y) → (y ≡ z) → (x ≡ z)
_∘_ {u} {A} {x} {y} {z} p q =
  pathInd {u}
    (λ {x} {y} p → ((z : A) → (q : y ≡ z) → (x ≡ z)))
    (λ x z q → pathInd (λ {x} {z} _ → x ≡ z) refl {x} {z} q)
    {x} {y} p z q

-- p = p . refl

unitTransR : {A : Set} {x y : A} → (p : x ≡ y) → (p ≡ p ∘ refl y)
unitTransR {A} {x} {y} p =
  pathInd
    (λ {x} {y} p → p ≡ p ∘ (refl y))
    (λ x → refl (refl x))
    {x} {y} p

-- p = refl . p

unitTransL : {A : Set} {x y : A} → (p : x ≡ y) → (p ≡ refl x ∘ p)
unitTransL {A} {x} {y} p =
  pathInd
    (λ {x} {y} p → p ≡ (refl x) ∘ p)
    (λ x → refl (refl x))
    {x} {y} p

ap : ∀ {ℓ ℓ'} → {A : Set ℓ} {B : Set ℓ'} {x y : A} →
     (f : A → B) → (x ≡ y) → (f x ≡ f y)
ap {ℓ} {ℓ'} {A} {B} {x} {y} f p =
  pathInd -- on p
    (λ {x} {y} p → f x ≡ f y)
    (λ x → refl (f x))
    {x} {y} p

ap2 : ∀ {ℓ ℓ' ℓ''} → {A : Set ℓ} {B : Set ℓ'} {C : Set ℓ''}
     {x₁ y₁ : A} {x₂ y₂ : B} →
     (f : A → B → C) → (x₁ ≡ y₁) → (x₂ ≡ y₂) → (f x₁ x₂  ≡ f y₁ y₂)
ap2 {ℓ} {ℓ'} {ℓ''} {A} {B} {C} {x₁} {y₁} {x₂} {y₂} f p₁ p₂ =
  pathInd -- on p₁
    (λ {x₁} {y₁} p₁ → f x₁ x₂ ≡ f y₁ y₂)
    (λ x →
      pathInd -- on p₂
        (λ {x₂} {y₂} p₂ → f x x₂ ≡ f x y₂)
        (λ y → refl (f x y))
        {x₂} {y₂} p₂)
    {x₁} {y₁} p₁

-- Abbreviations for path compositions

_≡⟨_⟩_ : ∀ {u} → {A : Set u} (x : A) {y z : A} → (x ≡ y) → (y ≡ z) → (x ≡ z)
_ ≡⟨ p ⟩ q = p ∘ q

bydef : ∀ {u} → {A : Set u} {x : A} → (x ≡ x)
bydef {u} {A} {x} = refl x

_∎ : ∀ {u} → {A : Set u} (x : A) → x ≡ x
_∎ x = refl x

-- Transport; Lifting

transport : ∀ {ℓ ℓ'} → {A : Set ℓ} {x y : A} →
  (P : A → Set ℓ') → (p : x ≡ y) → P x → P y
transport {ℓ} {ℓ'} {A} {x} {y} P p =
  pathInd -- on p
    (λ {x} {y} p → (P x → P y))
    (λ _ → id)
    {x} {y} p

-- Lemma 2.3.10

transport-f : ∀ {ℓ ℓ' ℓ''} → {A : Set ℓ} {B : Set ℓ'} {x y : A} →
  (f : A → B) → (P : B → Set ℓ'') →
  (p : x ≡ y) → (u : P (f x)) →
  transport (P ○ f) p u ≡ transport P (ap f p) u
transport-f {ℓ} {ℓ'} {ℓ''} {A} {B} {x} {y} f P p u =
  pathInd -- on p
    (λ {x} {y} p → (u : P (f x)) →
      transport (P ○ f) p u ≡ transport P (ap f p) u)
    (λ x u → refl u)
    {x} {y} p u

-- Lemma 2.11.2

transportIdR : {A : Set} {a y z : A} → (p : y ≡ z) → (q : a ≡ y) →
  transport (λ x → a ≡ x) p q ≡ q ∘ p
transportIdR {A} {a} {y} {z} p q =
  pathInd
    (λ {y} {z} p → (q : a ≡ y) → transport (λ x → a ≡ x) p q ≡ q ∘ p)
    (λ y q → transport (λ x → a ≡ x) (refl y) q
               ≡⟨ bydef ⟩
             q
               ≡⟨ unitTransR q ⟩
             q ∘ refl y ∎)
    {y} {z} p q

transportIdL : {A : Set} {a y z : A} → (p : y ≡ z) → (q : y ≡ a) →
  transport (λ x → x ≡ a) p q ≡ ! p ∘ q
transportIdL {A} {a} {y} {z} p q =
  pathInd
    (λ {y} {z} p → (q : y ≡ a) → transport (λ x → x ≡ a) p q ≡ ! p ∘ q)
    (λ y q → transport (λ x → x ≡ a) (refl y) q
               ≡⟨ bydef ⟩
             q
               ≡⟨ unitTransL q ⟩
             ! (refl y) ∘ q ∎)
    {y} {z} p q

transportIdRefl : {A : Set} {y z : A} → (p : y ≡ z) → (q : y ≡ y) →
  transport (λ x → x ≡ x) p q ≡ ! p ∘ q ∘ p
transportIdRefl {A} {y} {z} p q =
  pathInd
    (λ {y} {z} p → (q : y ≡ y) → transport (λ x → x ≡ x) p q ≡ ! p ∘ q ∘ p)
    (λ y q → transport (λ x → x ≡ x) (refl y) q
               ≡⟨ bydef ⟩
             q
               ≡⟨ unitTransR q ⟩
             q ∘ refl y
               ≡⟨ unitTransL (q ∘ refl y) ⟩
             ! (refl y) ∘ q ∘ refl y ∎)
    {y} {z} p q

-- tools for coproducts (Sec. 2.12)

indCP : {A B : Set} → (C : A ⊎ B → Set) →
        ((a : A) → C (inj₁ a)) → ((b : B) → C (inj₂ b)) → ((x : A ⊎ B) → C x)
indCP C f g (inj₁ a) = f a
indCP C f g (inj₂ b) = g b

code : {A B : Set} → (a₀ : A) → A ⊎ B → Set
code a₀ (inj₁ a) = a₀ ≡ a
code a₀ (inj₂ b) = ⊥

encode : {A B : Set} → (a₀ : A) → (x : A ⊎ B) → (p : inj₁ a₀ ≡ x) → code a₀ x
encode {A} {B} a₀ x p = transport (code a₀) p (refl a₀)

decode : {A B : Set} → (a₀ : A) → (x : A ⊎ B) → (c : code a₀ x) → inj₁ a₀ ≡ x
decode a₀ (inj₁ a) c = ap inj₁ c
decode a₀ (inj₂ b) ()


module Pi.NoRepeat where
open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product
open import Relation.Binary.PropositionalEquality
open import Pi.Syntax
open import Pi.Opsem
open import Pi.AuxLemmas
import RevNoRepeat

-- Forward deterministic
deterministic : ∀ {st st₁ st₂} → st ↦ st₁ → st ↦ st₂ → st₁ ≡ st₂
deterministic (↦₁ {b = b₁}) (↦₁ {b = b₂}) with base-is-prop _ b₁ b₂
... | refl = refl
deterministic ↦₂ ↦₂ = refl
deterministic ↦₃ ↦₃ = refl
deterministic ↦₄ ↦₄ = refl
deterministic ↦₅ ↦₅ = refl
deterministic ↦₆ ↦₆ = refl
deterministic ↦₇ ↦₇ = refl
deterministic ↦₈ ↦₈ = refl
deterministic ↦₉ ↦₉ = refl
deterministic ↦₁₀ ↦₁₀ = refl
deterministic ↦₁₁ ↦₁₁ = refl
deterministic ↦₁₂ ↦₁₂ = refl

-- Backward deterministic
deterministicᵣₑᵥ : ∀ {st st₁ st₂} → st₁ ↦ st → st₂ ↦ st → st₁ ≡ st₂
deterministicᵣₑᵥ {［ c ∣ _ ∣ κ ］} {⟨ c ∣ v ∣ κ ⟩} {⟨ c' ∣ v' ∣ κ' ⟩} ↦₁ r with Lemma₁ r
... | refl , refl with Lemma₂ r
deterministicᵣₑᵥ {［ unite₊l  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ y        ∣ κ ⟩} {⟨ _ ∣ inj₂ y        ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ uniti₊l  ∣ _ ∣ κ ］} {⟨ _ ∣ v             ∣ κ ⟩} {⟨ _ ∣ v             ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ swap₊    ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ swap₊    ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ y        ∣ κ ⟩} {⟨ _ ∣ inj₂ y        ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} {⟨ _ ∣ inj₂ (inj₁ y) ∣ κ ⟩} ↦₁ () | refl , refl | refl , refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ x        ∣ κ ⟩} {⟨ _ ∣ inj₂ (inj₂ z) ∣ κ ⟩} ↦₁ () | refl , refl | refl , refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ (inj₁ y) ∣ κ ⟩} {⟨ _ ∣ inj₂ (inj₁ y) ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ (inj₂ z) ∣ κ ⟩} {⟨ _ ∣ inj₂ (inj₂ z) ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ (inj₁ x) ∣ κ ⟩} {⟨ _ ∣ inj₁ (inj₁ x) ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ (inj₂ y) ∣ κ ⟩} {⟨ _ ∣ inj₁ (inj₂ y) ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ z        ∣ κ ⟩} {⟨ _ ∣ inj₁ (inj₁ x) ∣ κ ⟩} ↦₁ () | refl , refl | refl , refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ z        ∣ κ ⟩} {⟨ _ ∣ inj₁ (inj₂ y) ∣ κ ⟩} ↦₁ () | refl , refl | refl , refl
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ z        ∣ κ ⟩} {⟨ _ ∣ inj₂ z        ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ unite⋆l  ∣ _ ∣ κ ］} {⟨ _ ∣ (tt , v)      ∣ κ ⟩} {⟨ _ ∣ (tt , v)      ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ uniti⋆l  ∣ _ ∣ κ ］} {⟨ _ ∣ v             ∣ κ ⟩} {⟨ _ ∣ v             ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ swap⋆    ∣ _ ∣ κ ］} {⟨ _ ∣ (x , y)       ∣ κ ⟩} {⟨ _ ∣ (x , y)       ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocl⋆  ∣ _ ∣ κ ］} {⟨ _ ∣ (x , (y , z)) ∣ κ ⟩} {⟨ _ ∣ (x , (y , z)) ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ assocr⋆  ∣ _ ∣ κ ］} {⟨ _ ∣ ((x , y) , z) ∣ κ ⟩} {⟨ _ ∣ ((x , y) , z) ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ dist     ∣ _ ∣ κ ］} {⟨ _ ∣ (inj₁ x , z)  ∣ κ ⟩} {⟨ _ ∣ (inj₁ x , z)  ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ dist     ∣ _ ∣ κ ］} {⟨ _ ∣ (inj₂ y , z)  ∣ κ ⟩} {⟨ _ ∣ (inj₂ y , z)  ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ factor   ∣ _ ∣ κ ］} {⟨ _ ∣ inj₁ (x , z)  ∣ κ ⟩} {⟨ _ ∣ inj₁ (x , z)  ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ factor   ∣ _ ∣ κ ］} {⟨ _ ∣ inj₂ (y , z)  ∣ κ ⟩} {⟨ _ ∣ inj₂ (y , z)  ∣ κ ⟩} ↦₁ ↦₁ | refl , refl | refl , refl = refl
deterministicᵣₑᵥ {［ unite₊l  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ uniti₊l  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ swap₊    ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ assocl₊  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ assocr₊  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ unite⋆l  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ uniti⋆l  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ swap⋆    ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ assocl⋆  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ assocr⋆  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ dist     ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ factor   ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} ↦₁ ()
deterministicᵣₑᵥ {［ id↔      ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} (↦₁ {b = ()})
deterministicᵣₑᵥ {［ c₁ ⨾ c₂  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} (↦₁ {b = ()})
deterministicᵣₑᵥ {［ c₁ ⊕ c₂  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} (↦₁ {b = ()})
deterministicᵣₑᵥ {［ c₁ ⊗ c₂  ∣ _ ∣ κ ］} {⟨ _ ∣ v  ∣ κ ⟩} {［ c' ∣ v' ∣ κ' ］} (↦₁ {b = ()})
deterministicᵣₑᵥ ↦₂ ↦₂ = refl
deterministicᵣₑᵥ ↦₃ ↦₃ = refl
deterministicᵣₑᵥ ↦₄ ↦₄ = refl
deterministicᵣₑᵥ ↦₅ ↦₅ = refl
deterministicᵣₑᵥ ↦₆ ↦₆ = refl
deterministicᵣₑᵥ ↦₇ ↦₇ = refl
deterministicᵣₑᵥ ↦₈ ↦₈ = refl
deterministicᵣₑᵥ ↦₉ ↦₉ = refl
deterministicᵣₑᵥ ↦₁₀ ↦₁₀ = refl
deterministicᵣₑᵥ {［ c₁ ⊕ c₂ ∣ inj₁ v ∣ κ ］} {_} {⟨ c₁' ⊕ c₂' ∣ v' ∣ κ' ⟩} ↦₁₁ r with Lemma₁ r
... | refl , refl with Lemma₂ r
... | refl , refl with Lemma₃ r
... | inj₂ refl with Lemma₄ r
... | inj₁ ()
... | inj₂ ()
deterministicᵣₑᵥ {［ c₁ ⊕ c₂ ∣ inj₁ v ∣ κ ］} {［ c₁ ∣ v ∣ ☐⊕ c₂ • κ ］} {［ c₁ ∣ v ∣ ☐⊕ c₂ • κ ］} ↦₁₁ ↦₁₁ = refl
deterministicᵣₑᵥ {［ c₁ ⊕ c₂ ∣ inj₂ y ∣ κ ］} {［ c₂ ∣ y ∣ c₁ ⊕☐• κ ］} {⟨ c ∣ x ∣ x₁ ⟩} ↦₁₂ r with Lemma₁ r
... | refl , refl with Lemma₂ r
... | refl , refl with Lemma₃ r
... | inj₂ refl with Lemma₄ r
... | inj₂ ()
deterministicᵣₑᵥ {［ c₁ ⊕ c₂ ∣ inj₂ y ∣ κ ］} {［ c₂ ∣ y ∣ c₁ ⊕☐• κ ］} {［ c₂ ∣ y ∣ (c₁ ⊕☐• κ) ］} ↦₁₂ ↦₁₂ = refl

-- Non-repeating Lemma
open RevNoRepeat (record { State = State
                         ; _↦_ = _↦_
                         ; deterministic = deterministic
                         ; deterministicᵣₑᵥ = deterministicᵣₑᵥ }) public


module examplesPaperJFP.Sized where

open import Data.Product using (_×_; _,_)
open import Data.String

open import Function using (case_of_)
open import Size

open import examplesPaperJFP.NativeIOSafe
open import examplesPaperJFP.BasicIO using (IOInterface; Command; Response)
open import examplesPaperJFP.ConsoleInterface
open import examplesPaperJFP.Console using (translateIOConsoleLocal)
open import examplesPaperJFP.Object using (Interface; Method; Result;
  cellJ; CellMethod; get; put; CellResult)

module UnfoldF where
  open import examplesPaperJFP.Coalgebra using (F; mapF)
  record νF (i : Size) : Set where
    coinductive
    constructor delay
    field force : ∀(j : Size< i) → F (νF j)

  open νF using (force)

  unfoldF : ∀{S} (t : S → F S) → ∀ i → (S → νF i)
  force (unfoldF t i s) j = mapF (unfoldF t j) (t s)

mutual
  record IO (Iᵢₒ : IOInterface) (i : Size)  (A : Set) : Set where
    coinductive

    constructor delay
    field force : {j : Size< i} → IO′ Iᵢₒ j A

  data IO′ (Iᵢₒ : IOInterface) (i : Size)  (A : Set) : Set where
    exec′      :  (c : Command Iᵢₒ) (f : Response Iᵢₒ c → IO Iᵢₒ i A)  → IO′ Iᵢₒ i A
    return′  :  (a : A)                                              → IO′ Iᵢₒ i A

module NestedRecursion (Iᵢₒ : IOInterface) (A : Set) where

  data F (X : Set) : Set where
    exec′      :  (c : Command Iᵢₒ) (f : Response Iᵢₒ c → X)  → F X
    return′  :  (a : A)                                     → F X

  record νF (i : Size) : Set where
    coinductive
    constructor delay
    field force : {j : Size< i} → F (νF j)

open IO public

module _  {Iᵢₒ : IOInterface } (let C = Command Iᵢₒ) (let R = Response Iᵢₒ) where

  infixl 2 _>>=_

  exec      :  ∀ {i A}    (c : C) (f : R c → IO Iᵢₒ i A) → IO Iᵢₒ i A
  return  :  ∀ {i A}    (a : A) → IO Iᵢₒ i A
  _>>=_   :  ∀ {i A B}  (m : IO Iᵢₒ i A) (k : A → IO Iᵢₒ i B) → IO Iᵢₒ i B

  force (exec c f)    = exec′ c f
  force (return a)  =  return′ a

  force (_>>=_ {i} m k) {j} with force m {j}
  ... | exec′ c f     =  exec′ c λ r → _>>=_ {j} (f r) k
  ... | return′ a   =  force (k a) {j}

  {-# NON_TERMINATING #-}
  translateIO : ∀{A : Set}
    →  (translateLocal : (c : C) → NativeIO (R c))
    →  IO Iᵢₒ ∞ A
    →  NativeIO A
  translateIO translateLocal m = case (force m) of
    λ{ (exec′ c f)    → (translateLocal c) native>>= λ r →
         translateIO translateLocal (f r)
     ; (return′ a) → nativeReturn a
     }

record IOObject (Iᵢₒ : IOInterface) (I : Interface) (i : Size) : Set where
  coinductive
  field method  :  ∀{j : Size< i} (m : Method I)
                   → IO Iᵢₒ ∞ (Result I m × IOObject Iᵢₒ I j)

open IOObject public

CellC : (i : Size) → Set
CellC = IOObject ConsoleInterface (cellJ String)

simpleCell : ∀{i} (s : String) → CellC i
force (method (simpleCell {i} s) {j} get) =
  exec′ (putStrLn ("getting (" ++ s ++ ")")) λ _ →
  return (s , simpleCell {j} s)
force (method (simpleCell _) (put s)) =
  exec′ (putStrLn ("putting (" ++ s ++ ")")) λ _ →
  return (unit , simpleCell s)

program : ∀{i} → IO ConsoleInterface i Unit
force program =
  let c₁ = simpleCell "Start" in
  exec′ getLine          λ{ nothing → return unit; (just s) →
  method c₁ (put s)    >>= λ{ (_ , c₂) →
  method c₂ get        >>= λ{ (s′ , c₃) →
  exec (putStrLn s′)     λ _ →
  program }}}

main : NativeIO Unit
main = translateIO translateIOConsoleLocal program


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.Algebra.Group.Algebra where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Structure
open import Cubical.Foundations.Function using (_∘_)
open import Cubical.Foundations.GroupoidLaws

open import Cubical.Data.Sigma
open import Cubical.Data.Unit

open import Cubical.Algebra.Group.Base
open import Cubical.Algebra.Group.Properties
open import Cubical.Algebra.Group.Morphism
open import Cubical.Algebra.Group.MorphismProperties

open import Cubical.HITs.PropositionalTruncation hiding (map)

open Iso
open GroupHom

private
  variable
    ℓ ℓ' ℓ₁ ℓ₂ ℓ₃ : Level

------- elementary properties of morphisms --------

module _ (G : Group {ℓ}) (H : Group {ℓ'}) where

  module G = GroupStr (snd G)
  module H = GroupStr (snd H)

  -0≡0 : G.- G.0g ≡ G.0g
  -0≡0 = sym (G.lid _) ∙ G.invr _

  -- ϕ(0) ≡ 0
  morph0→0 : (f : GroupHom G H) → f .fun G.0g ≡ H.0g
  morph0→0 fh@(grouphom f _) =
    f G.0g                         ≡⟨ sym (H.rid _) ⟩
    f G.0g H.+ H.0g                ≡⟨ (λ i → f G.0g H.+ H.invr (f G.0g) (~ i)) ⟩
    f G.0g H.+ (f G.0g H.- f G.0g) ≡⟨ H.assoc _ _ _ ⟩
    (f G.0g H.+ f G.0g) H.- f G.0g ≡⟨ sym (cong (λ x → x H.+ (H.- f G.0g))
                                                (sym (cong f (G.lid _)) ∙ isHom fh G.0g G.0g)) ⟩
    f G.0g H.- f G.0g              ≡⟨ H.invr _ ⟩
    H.0g ∎

  -- ϕ(- x) = - ϕ(x)
  morphMinus : (f : GroupHom G H) → (g : ⟨ G ⟩) → f .fun (G.- g) ≡ H.- (f .fun g)
  morphMinus fc@(grouphom f fh) g =
    f (G.- g)                   ≡⟨ sym (H.rid _) ⟩
    f (G.- g) H.+ H.0g          ≡⟨ cong (f (G.- g) H.+_) (sym (H.invr _)) ⟩
    f (G.- g) H.+ (f g H.- f g) ≡⟨ H.assoc _ _ _ ⟩
    (f (G.- g) H.+ f g) H.- f g ≡⟨ cong (H._+ (H.- f g)) helper ⟩
    H.0g H.- f g                ≡⟨ H.lid _ ⟩
    H.- f g ∎
    where
    helper : f (G.- g) H.+ f g ≡ H.0g
    helper = sym (fh (G.- g) g) ∙∙ cong f (G.invl g) ∙∙ morph0→0 fc

-- ----------- Alternative notions of isomorphisms --------------
record GroupIso {ℓ ℓ'} (G : Group {ℓ}) (H : Group {ℓ'}) : Type (ℓ-max ℓ ℓ') where

  constructor iso
  field
    map : GroupHom G H
    inv : ⟨ H ⟩ → ⟨ G ⟩
    rightInv : section (GroupHom.fun map) inv
    leftInv : retract (GroupHom.fun map) inv

record BijectionIso {ℓ ℓ'} (A : Group {ℓ}) (B : Group {ℓ'}) : Type (ℓ-max ℓ ℓ') where

  constructor bij-iso
  field
    map' : GroupHom A B
    inj : isInjective A B map'
    surj : isSurjective A B map'

-- "Very" short exact sequences
-- i.e. an exact sequence A → B → C → D where A and D are trivial
record vSES {ℓ ℓ' ℓ'' ℓ'''} (A : Group {ℓ}) (B : Group {ℓ'}) (leftGr : Group {ℓ''}) (rightGr : Group {ℓ'''})
           : Type (ℓ-suc (ℓ-max ℓ (ℓ-max ℓ' (ℓ-max ℓ'' ℓ''')))) where

  constructor ses
  field
    isTrivialLeft : isProp ⟨ leftGr ⟩
    isTrivialRight : isProp ⟨ rightGr ⟩

    left : GroupHom leftGr A
    right : GroupHom B rightGr
    ϕ : GroupHom A B

    Ker-ϕ⊂Im-left : (x : ⟨ A ⟩)
                  → isInKer A B ϕ x
                  → isInIm leftGr A left x
    Ker-right⊂Im-ϕ : (x : ⟨ B ⟩)
                   → isInKer B rightGr right x
                   → isInIm A B ϕ x

open BijectionIso
open GroupIso
open vSES

Iso+Hom→GrIso : {G : Group {ℓ}} {H : Group {ℓ₁}} → (e : Iso ⟨ G ⟩ ⟨ H ⟩) → isGroupHom G H (Iso.fun e) → GroupIso G H
fun (map (Iso+Hom→GrIso e hom)) = Iso.fun e
isHom (map (Iso+Hom→GrIso e hom)) = hom
inv (Iso+Hom→GrIso e hom) = Iso.inv e
rightInv (Iso+Hom→GrIso e hom) = Iso.rightInv e
leftInv (Iso+Hom→GrIso e hom) = Iso.leftInv e

compGroupIso : {G : Group {ℓ}} {H : Group {ℓ₁}} {A : Group {ℓ₂}} → GroupIso G H → GroupIso H A → GroupIso G A
map (compGroupIso iso1 iso2) = compGroupHom (map iso1) (map iso2)
inv (compGroupIso iso1 iso2) = inv iso1 ∘ inv iso2
rightInv (compGroupIso iso1 iso2) a = cong (fun (map iso2)) (rightInv iso1 _) ∙ rightInv iso2 a
leftInv (compGroupIso iso1 iso2) a = cong (inv iso1) (leftInv iso2 _) ∙ leftInv iso1 a

isGroupHomInv' : {G : Group {ℓ}} {H : Group {ℓ₁}} (f : GroupIso G H) → isGroupHom H G (inv f)
isGroupHomInv' {G = G} {H = H}  f h h' = isInj-f _ _ (
  f' (g (h ⋆² h')) ≡⟨ (rightInv f) _ ⟩
  (h ⋆² h') ≡⟨ sym (cong₂ _⋆²_ (rightInv f h) (rightInv f h')) ⟩
  (f' (g h) ⋆² f' (g h')) ≡⟨ sym (isHom (map f) _ _) ⟩
  f' (g h ⋆¹ g h') ∎)
  where
  f' = fun (map f)
  _⋆¹_ = GroupStr._+_ (snd G)
  _⋆²_ = GroupStr._+_ (snd H)
  g = inv f

  isInj-f : (x y : ⟨ G ⟩) → f' x ≡ f' y → x ≡ y
  isInj-f x y p = sym (leftInv f _) ∙∙ cong g p ∙∙ leftInv f _

invGroupIso : {G : Group {ℓ}} {H : Group {ℓ₁}} → GroupIso G H → GroupIso H G
fun (map (invGroupIso iso1)) = inv iso1
isHom (map (invGroupIso iso1)) = isGroupHomInv' iso1
inv (invGroupIso iso1) = fun (map iso1)
rightInv (invGroupIso iso1) = leftInv iso1
leftInv (invGroupIso iso1) = rightInv iso1

dirProdGroupIso : {G : Group {ℓ}} {H : Group {ℓ₁}} {A : Group {ℓ₂}} {B : Group {ℓ₃}}
               → GroupIso G H → GroupIso A B → GroupIso (dirProd G A) (dirProd H B)
fun (map (dirProdGroupIso iso1 iso2)) prod = fun (map iso1) (fst prod) , fun (map iso2) (snd prod)
isHom (map (dirProdGroupIso iso1 iso2)) a b = ΣPathP (isHom (map iso1) (fst a) (fst b) , isHom (map iso2) (snd a) (snd b))
inv (dirProdGroupIso iso1 iso2) prod = (inv iso1) (fst prod) , (inv iso2) (snd prod)
rightInv (dirProdGroupIso iso1 iso2) a = ΣPathP (rightInv iso1 (fst a) , (rightInv iso2 (snd a)))
leftInv (dirProdGroupIso iso1 iso2) a = ΣPathP (leftInv iso1 (fst a) , (leftInv iso2 (snd a)))

GrIsoToGrEquiv : {G : Group {ℓ}} {H : Group {ℓ₂}} → GroupIso G H → GroupEquiv G H
GroupEquiv.eq (GrIsoToGrEquiv i) = isoToEquiv (iso (fun (map i)) (inv i) (rightInv i) (leftInv i))
GroupEquiv.isHom (GrIsoToGrEquiv i) = isHom (map i)

--- Proofs that BijectionIso and vSES both induce isomorphisms ---
BijectionIsoToGroupIso : {A : Group {ℓ}} {B : Group {ℓ'}} → BijectionIso A B → GroupIso A B
BijectionIsoToGroupIso {A = A} {B = B} i = grIso
  where
  module A = GroupStr (snd A)
  module B = GroupStr (snd B)
  f = fun (map' i)

  helper : (b : _) → isProp (Σ[ a ∈ ⟨ A ⟩ ] f a ≡ b)
  helper _ a b =
    Σ≡Prop (λ _ → isSetCarrier B _ _)
           (fst a                             ≡⟨ sym (A.rid _) ⟩
            fst a A.+ A.0g                    ≡⟨ cong (fst a A.+_) (sym (A.invl _)) ⟩
            fst a A.+ ((A.- fst b) A.+ fst b) ≡⟨ A.assoc _ _ _ ⟩
            (fst a A.- fst b) A.+ fst b       ≡⟨ cong (A._+ fst b) idHelper ⟩
            A.0g A.+ fst b                    ≡⟨ A.lid _ ⟩
            fst b ∎)
    where
    idHelper : fst a A.- fst b ≡ A.0g
    idHelper =
      inj i _
           (isHom (map' i) (fst a) (A.- (fst b))
         ∙ (cong (f (fst a) B.+_) (morphMinus A B (map' i) (fst b))
         ∙∙ cong (B._+ (B.- f (fst b))) (snd a ∙ sym (snd b))
         ∙∙ B.invr (f (fst b))))

  grIso : GroupIso A B
  map grIso = map' i
  inv grIso b = (rec (helper b) (λ a → a) (surj i b)) .fst
  rightInv grIso b = (rec (helper b) (λ a → a) (surj i b)) .snd
  leftInv grIso b j = rec (helper (f b)) (λ a → a) (propTruncIsProp (surj i (f b)) ∣ b , refl ∣ j) .fst

BijectionIsoToGroupEquiv : {A : Group {ℓ}} {B : Group {ℓ₂}} → BijectionIso A B → GroupEquiv A B
BijectionIsoToGroupEquiv i = GrIsoToGrEquiv (BijectionIsoToGroupIso i)

vSES→GroupIso : ∀ {ℓ ℓ' ℓ'' ℓ'''} {A : Group {ℓ}} {B : Group {ℓ'}} (leftGr : Group {ℓ''}) (rightGr : Group {ℓ'''})
                → vSES A B leftGr rightGr
                → GroupIso A B
vSES→GroupIso {A = A} lGr rGr vses = BijectionIsoToGroupIso theIso
  where
  theIso : BijectionIso _ _
  map' theIso = vSES.ϕ vses
  inj theIso a inker = rec (isSetCarrier A _ _)
                            (λ (a , p) → sym p
                                        ∙∙ cong (fun (left vses)) (isTrivialLeft vses a _)
                                        ∙∙ morph0→0 lGr A (left vses))
                            (Ker-ϕ⊂Im-left vses a inker)
  surj theIso a = Ker-right⊂Im-ϕ vses a (isTrivialRight vses _ _)

vSES→GroupEquiv : {A : Group {ℓ}} {B : Group {ℓ₁}} (leftGr : Group {ℓ₂}) (rightGr : Group {ℓ₃})
        → vSES A B leftGr rightGr
        → GroupEquiv A B
vSES→GroupEquiv {A = A} lGr rGr vses = GrIsoToGrEquiv (vSES→GroupIso lGr rGr vses)

-- The trivial group is a unit.
lUnitGroupIso : ∀ {ℓ} {G : Group {ℓ}} → GroupIso (dirProd trivialGroup G) G
fun (map lUnitGroupIso) = snd
isHom (map lUnitGroupIso) _ _ = refl
inv lUnitGroupIso g = tt , g
rightInv lUnitGroupIso _ = refl
leftInv lUnitGroupIso _ = refl

rUnitGroupIso : ∀ {ℓ} {G : Group {ℓ}} → GroupIso (dirProd G trivialGroup) G
fun (map rUnitGroupIso) = fst
isHom (map rUnitGroupIso) _ _ = refl
inv rUnitGroupIso g = g , tt
rightInv rUnitGroupIso _ = refl
leftInv rUnitGroupIso _ = refl

lUnitGroupEquiv : ∀ {ℓ} {G : Group {ℓ}} → GroupEquiv (dirProd trivialGroup G) G
lUnitGroupEquiv = GrIsoToGrEquiv lUnitGroupIso

rUnitGroupEquiv : ∀ {ℓ} {G : Group {ℓ}} → GroupEquiv (dirProd G trivialGroup) G
rUnitGroupEquiv = GrIsoToGrEquiv rUnitGroupIso

IsoContrGroupTrivialGroup : {G : Group {ℓ}} → isContr ⟨ G ⟩ → GroupIso G trivialGroup
fun (map (IsoContrGroupTrivialGroup contr)) _ = tt
isHom (map (IsoContrGroupTrivialGroup contr)) _ _ = refl
inv (IsoContrGroupTrivialGroup contr) x = fst contr
rightInv (IsoContrGroupTrivialGroup contr) x = refl
leftInv (IsoContrGroupTrivialGroup contr) x = snd contr x

contrGroup≅trivialGroup : {G : Group {ℓ}} → isContr ⟨ G ⟩ → GroupEquiv G trivialGroup
contrGroup≅trivialGroup contr = GrIsoToGrEquiv (IsoContrGroupTrivialGroup contr)

GroupIso→Iso : {A : Group {ℓ}} {B : Group {ℓ₁}} → GroupIso A B → Iso ⟨ A ⟩ ⟨ B ⟩
fun (GroupIso→Iso i) = fun (map i)
inv (GroupIso→Iso i) = inv i
rightInv (GroupIso→Iso i) = rightInv i
leftInv (GroupIso→Iso i) = leftInv i

congIdLeft≡congIdRight : {A : Type ℓ} (_+A_ : A → A → A) (-A_ : A → A)
            (0A : A)
            (rUnitA : (x : A) → x +A 0A ≡ x)
            (lUnitA : (x : A) → 0A +A x ≡ x)
          → (r≡l : rUnitA 0A ≡ lUnitA 0A)
          → (p : 0A ≡ 0A) →
            cong (0A +A_) p ≡ cong (_+A 0A) p
congIdLeft≡congIdRight _+A_ -A_ 0A rUnitA lUnitA r≡l p =
            rUnit (cong (0A +A_) p)
         ∙∙ ((λ i → (λ j → lUnitA 0A (i ∧ j)) ∙∙ cong (λ x → lUnitA x i) p ∙∙ λ j → lUnitA 0A (i ∧ ~ j))
         ∙∙ cong₂ (λ x y → x ∙∙ p ∙∙ y) (sym r≡l) (cong sym (sym r≡l))
         ∙∙ λ i → (λ j → rUnitA 0A (~ i ∧ j)) ∙∙ cong (λ x → rUnitA x (~ i)) p ∙∙ λ j → rUnitA 0A (~ i ∧ ~ j))
         ∙∙ sym (rUnit (cong (_+A 0A) p))


{-

Descriptor language for easily defining relational structures

-}
{-# OPTIONS --cubical --no-import-sorts --no-exact-split --safe #-}
module Cubical.Structures.Relational.Macro where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Structure
open import Cubical.Foundations.RelationalStructure
open import Cubical.Data.Sigma

open import Cubical.Structures.Relational.Constant
open import Cubical.Structures.Relational.Function
open import Cubical.Structures.Relational.Maybe
open import Cubical.Structures.Relational.Parameterized
open import Cubical.Structures.Relational.Pointed
open import Cubical.Structures.Relational.Product

open import Cubical.Structures.Macro
open import Cubical.Structures.Maybe

data PosRelDesc (ℓ : Level) : Typeω where
  -- constant structure: X ↦ A
  constant : ∀ {ℓ'} → hSet ℓ' → PosRelDesc ℓ
  -- pointed structure: X ↦ X
  var : PosRelDesc ℓ
  -- product of structures S,T : X ↦ (S X × T X)
  _,_ : PosRelDesc ℓ  → PosRelDesc ℓ  → PosRelDesc ℓ
  -- Maybe on a structure S: X ↦ Maybe (S X)
  maybe : PosRelDesc ℓ → PosRelDesc ℓ

data RelDesc (ℓ : Level) : Typeω where
  -- constant structure: X ↦ A
  constant : ∀ {ℓ'} → hSet ℓ' → RelDesc ℓ
  -- pointed structure: X ↦ X
  var : RelDesc ℓ
  -- product of structures S,T : X ↦ (S X × T X)
  _,_ : RelDesc ℓ  → RelDesc ℓ  → RelDesc ℓ
  -- structure S parameterized by constant A : X ↦ (A → S X)
  param : ∀ {ℓ'} → (A : Type ℓ') → RelDesc ℓ  → RelDesc ℓ
  -- function from positive structure S to T: X ↦ (S X → T X)
  function+ : PosRelDesc ℓ → RelDesc ℓ → RelDesc ℓ
  -- Maybe on a structure S: X ↦ Maybe (S X)
  maybe : RelDesc ℓ → RelDesc ℓ

infixr 4 _,_

posRelDesc→TranspDesc : ∀ {ℓ} → PosRelDesc ℓ → TranspDesc ℓ
posRelDesc→TranspDesc (constant A) = constant (A .fst)
posRelDesc→TranspDesc var = var
posRelDesc→TranspDesc (d₀ , d₁) = posRelDesc→TranspDesc d₀ , posRelDesc→TranspDesc d₁
posRelDesc→TranspDesc (maybe d) = maybe (posRelDesc→TranspDesc d)

posRelDesc→RelDesc : ∀ {ℓ} → PosRelDesc ℓ → RelDesc ℓ
posRelDesc→RelDesc (constant A) = constant A
posRelDesc→RelDesc var = var
posRelDesc→RelDesc (d₀ , d₁) = posRelDesc→RelDesc d₀ , posRelDesc→RelDesc d₁
posRelDesc→RelDesc (maybe d) = maybe (posRelDesc→RelDesc d)

relDesc→Desc : ∀ {ℓ} → RelDesc ℓ → Desc ℓ
relDesc→Desc (constant A) = constant (A .fst)
relDesc→Desc var = var
relDesc→Desc (d₀ , d₁) = relDesc→Desc d₀ , relDesc→Desc d₁
relDesc→Desc (param A d) = function+ (constant A) (relDesc→Desc d)
relDesc→Desc (function+ d₀ d₁) = function+ (posRelDesc→TranspDesc d₀) (relDesc→Desc d₁)
relDesc→Desc (maybe d) = maybe (relDesc→Desc d)

{- Universe level calculations -}

posRelMacroStrLevel : ∀ {ℓ} → PosRelDesc ℓ → Level
posRelMacroStrLevel d = transpMacroLevel (posRelDesc→TranspDesc d)

relMacroStrLevel : ∀ {ℓ} → RelDesc ℓ → Level
relMacroStrLevel d = macroStrLevel (relDesc→Desc d)

posRelMacroRelLevel : ∀ {ℓ} → PosRelDesc ℓ → Level
posRelMacroRelLevel (constant {ℓ'} A) = ℓ'
posRelMacroRelLevel {ℓ} var = ℓ
posRelMacroRelLevel (d₀ , d₁) = ℓ-max (posRelMacroRelLevel d₀) (posRelMacroRelLevel d₁)
posRelMacroRelLevel (maybe d) = posRelMacroRelLevel d

relMacroRelLevel : ∀ {ℓ} → RelDesc ℓ → Level
relMacroRelLevel (constant {ℓ'} A) = ℓ'
relMacroRelLevel {ℓ} var = ℓ
relMacroRelLevel (d₀ , d₁) = ℓ-max (relMacroRelLevel d₀) (relMacroRelLevel d₁)
relMacroRelLevel (param {ℓ'} A d) = ℓ-max ℓ' (relMacroRelLevel d)
relMacroRelLevel (function+ d₀ d₁) =
  ℓ-max (posRelMacroStrLevel d₀) (ℓ-max (posRelMacroRelLevel d₀) (relMacroRelLevel d₁))
relMacroRelLevel (maybe d) = relMacroRelLevel d

{- Definition of structure -}

PosRelMacroStructure : ∀ {ℓ} (d : PosRelDesc ℓ) → Type ℓ → Type (posRelMacroStrLevel d)
PosRelMacroStructure d = TranspMacroStructure (posRelDesc→TranspDesc d)

RelMacroStructure : ∀ {ℓ} (d : RelDesc ℓ) → Type ℓ → Type (relMacroStrLevel d)
RelMacroStructure d = MacroStructure (relDesc→Desc d)

{- Notion of structured relation defined by a descriptor -}

PosRelMacroRelStr : ∀ {ℓ} (d : PosRelDesc ℓ) → StrRel {ℓ} (PosRelMacroStructure d) (posRelMacroRelLevel d)
PosRelMacroRelStr (constant A) = ConstantRelStr A
PosRelMacroRelStr var = PointedRelStr
PosRelMacroRelStr (d₀ , d₁) = ProductRelStr (PosRelMacroRelStr d₀) (PosRelMacroRelStr d₁)
PosRelMacroRelStr (maybe d) = MaybeRelStr (PosRelMacroRelStr d)

RelMacroRelStr : ∀ {ℓ} (d : RelDesc ℓ) → StrRel {ℓ} (RelMacroStructure d) (relMacroRelLevel d)
RelMacroRelStr (constant A) = ConstantRelStr A
RelMacroRelStr var = PointedRelStr
RelMacroRelStr (d₀ , d₁) = ProductRelStr (RelMacroRelStr d₀) (RelMacroRelStr d₁)
RelMacroRelStr (param A d) = ParamRelStr A (λ _ → RelMacroRelStr d)
RelMacroRelStr (function+ d₀ d₁) =
  FunctionRelStr (PosRelMacroRelStr d₀)  (RelMacroRelStr d₁)
RelMacroRelStr (maybe d) = MaybeRelStr (RelMacroRelStr d)

{- Proof that structure induced by descriptor is suitable or positive -}

posRelMacroSuitableRel : ∀ {ℓ} (d : PosRelDesc ℓ) → SuitableStrRel _ (PosRelMacroRelStr d)
posRelMacroSuitableRel (constant A) = constantSuitableRel A
posRelMacroSuitableRel var = pointedSuitableRel
posRelMacroSuitableRel (d₀ , d₁) =
  productSuitableRel (posRelMacroSuitableRel d₀) (posRelMacroSuitableRel d₁)
posRelMacroSuitableRel (maybe d) = maybeSuitableRel (posRelMacroSuitableRel d)

posRelMacroPositiveRel : ∀ {ℓ} (d : PosRelDesc ℓ) → PositiveStrRel (posRelMacroSuitableRel d)
posRelMacroPositiveRel (constant A) = constantPositiveRel A
posRelMacroPositiveRel var = pointedPositiveRel
posRelMacroPositiveRel (d₀ , d₁) =
  productPositiveRel (posRelMacroPositiveRel d₀) (posRelMacroPositiveRel d₁)
posRelMacroPositiveRel (maybe d) = maybePositiveRel (posRelMacroPositiveRel d)

relMacroSuitableRel : ∀ {ℓ} (d : RelDesc ℓ) → SuitableStrRel _ (RelMacroRelStr d)
relMacroSuitableRel (constant A) = constantSuitableRel A
relMacroSuitableRel var = pointedSuitableRel
relMacroSuitableRel (d₀ , d₁) = productSuitableRel (relMacroSuitableRel d₀) (relMacroSuitableRel d₁)
relMacroSuitableRel (param A d) = paramSuitableRel A (λ _ → relMacroSuitableRel d)
relMacroSuitableRel (function+ d₀ d₁) =
  functionSuitableRel (posRelMacroSuitableRel d₀) (posRelMacroPositiveRel d₀) (relMacroSuitableRel d₁)
relMacroSuitableRel (maybe d) = maybeSuitableRel (relMacroSuitableRel d)

{- Proof that structured relations and equivalences agree -}

posRelMacroMatchesEquiv : ∀ {ℓ} (d : PosRelDesc ℓ)
  → StrRelMatchesEquiv (PosRelMacroRelStr d) (EquivAction→StrEquiv (transpMacroAction (posRelDesc→TranspDesc d)))
posRelMacroMatchesEquiv (constant A) _ _ _ = idEquiv _
posRelMacroMatchesEquiv var _ _ _ = idEquiv _
posRelMacroMatchesEquiv (d₀ , d₁) =
  productRelMatchesTransp
    (PosRelMacroRelStr d₀) (transpMacroAction (posRelDesc→TranspDesc d₀))
    (PosRelMacroRelStr d₁) (transpMacroAction (posRelDesc→TranspDesc d₁))
    (posRelMacroMatchesEquiv d₀) (posRelMacroMatchesEquiv d₁)
posRelMacroMatchesEquiv (maybe d) =
  maybeRelMatchesTransp
    (PosRelMacroRelStr d) (transpMacroAction (posRelDesc→TranspDesc d))
    (posRelMacroMatchesEquiv d)

relMacroMatchesEquiv : ∀ {ℓ} (d : RelDesc ℓ)
  → StrRelMatchesEquiv (RelMacroRelStr d) (MacroEquivStr (relDesc→Desc d))
relMacroMatchesEquiv (constant A) = constantRelMatchesEquiv A
relMacroMatchesEquiv var = pointedRelMatchesEquiv
relMacroMatchesEquiv (d₁ , d₂) =
  productRelMatchesEquiv
    (RelMacroRelStr d₁) (RelMacroRelStr d₂)
    (relMacroMatchesEquiv d₁) (relMacroMatchesEquiv d₂)
relMacroMatchesEquiv (param A d) =
  paramRelMatchesEquiv A (λ _ → RelMacroRelStr d) (λ _ → relMacroMatchesEquiv d)
relMacroMatchesEquiv (function+ d₀ d₁) =
  functionRelMatchesEquiv+
    (PosRelMacroRelStr d₀) (transpMacroAction (posRelDesc→TranspDesc d₀))
    (RelMacroRelStr d₁) (MacroEquivStr (relDesc→Desc d₁))
    (posRelMacroMatchesEquiv d₀) (relMacroMatchesEquiv d₁)
relMacroMatchesEquiv (maybe d) =
  maybeRelMatchesEquiv (RelMacroRelStr d) (relMacroMatchesEquiv d)

-- Module for easy importing
module RelMacro ℓ (d : RelDesc ℓ) where
  relation = RelMacroRelStr d
  suitable = relMacroSuitableRel d
  matches = relMacroMatchesEquiv d
  open Macro ℓ (relDesc→Desc d) public


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

open import Base
open import Homotopy.Pointed
open import Homotopy.Connected

module Homotopy.Cover {i} (A⋆ : pType i)
  (A⋆-is-conn : is-connected⋆ ⟨0⟩ A⋆) where
open pType A⋆ renaming (∣_∣ to A ; ⋆ to a)

open import Homotopy.Cover.Def A

module Reconstruct where
  open import Homotopy.Cover.HomotopyGroupSetIsomorphism A⋆ A⋆-is-conn public

{-
-- The following is still a mess.  Don't read it.
module _ (uc : universal-covering) where
  open covering (π₁ uc)

{-
  path⇒deck-is-equiv : ∀ {a₂} (path : a ≡ a₂) → is-equiv (path⇒deck path)
  path⇒deck-is-equiv path = iso-is-eq
    (transport fiber path)
    (transport fiber (! path))
    (trans-trans-opposite fiber path)
    (trans-opposite-trans fiber path)
-}

  is-nature : (fiber a → fiber a) → Set i
  is-nature f = (p : a ≡ a)
    → ∀ x → transport fiber p (f x) ≡ f (transport fiber p x)

  auto : Set i
  auto = fiber a ≃ fiber a

  -- This Lemma can be made more general as "(m+1)-connected => has-all-paths_m"
  uc-has-all-path₀ : ∀ (p q : Σ A fiber) → p ≡₀ q
  uc-has-all-path₀ p q = inverse τ-path-equiv-path-τ-S
    $ π₂ (π₂ uc) (proj p) ∘ ! (π₂ (π₂ uc) $ proj q)

  nature-auto-eq : ∀ (f₁ f₂ : auto)
    → (f₁-is-nature : is-nature (π₁ f₁))
    → (f₂-is-nature : is-nature (π₁ f₂))
    → Σ (fiber a) (λ x → f₁ ☆ x ≡ f₂ ☆ x)
    → f₁ ≡ f₂
  nature-auto-eq f₁ f₂ f₁-nat f₂-nat (x , path) = equiv-eq $ funext λ y →
    π₀-extend
      ⦃ λ _ → ≡-is-set {x = f₁ ☆ y} {y = f₂ ☆ y} $ fiber-is-set a ⦄
      (λ uc-path →
        f₁ ☆ y
          ≡⟨ ap (π₁ f₁) $ ! $ fiber-path uc-path ⟩
        f₁ ☆ transport fiber (base-path uc-path) x
          ≡⟨ ! $ f₁-nat (base-path uc-path) x ⟩
        transport fiber (base-path uc-path) (f₁ ☆ x)
          ≡⟨ ap (transport fiber $ base-path uc-path) path ⟩
        transport fiber (base-path uc-path) (f₂ ☆ x)
          ≡⟨ f₂-nat (base-path uc-path) x ⟩
        f₂ ☆ transport fiber (base-path uc-path) x
          ≡⟨ ap (π₁ f₂) $ fiber-path uc-path ⟩∎
        f₂ ☆ y
          ∎)
      (uc-has-all-path₀ (a , x) (a , y))
-}


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

open import HoTT
open import homotopy.SuspSmash
open import homotopy.SuspSmashComm

module homotopy.IterSuspSmash where

module _ {i j} (X : Ptd i) (Y : Ptd j) where

  Σ^∧-out : (n : ℕ) → ⊙Susp^ n X ∧ Y → Susp^ n (X ∧ Y)
  Σ^∧-out O = idf _
  Σ^∧-out (S n) = Susp-fmap (Σ^∧-out n) ∘ Σ∧-out (⊙Susp^ n X) Y

  private
    Σ^∧-out-pt : (n : ℕ) → Σ^∧-out n (pt (⊙Susp^ n X ⊙∧ Y)) == pt (⊙Susp^ n (X ⊙∧ Y))
    Σ^∧-out-pt O = idp
    Σ^∧-out-pt (S n) = idp

  ⊙Σ^∧-out : (n : ℕ) → ⊙Susp^ n X ⊙∧ Y ⊙→ ⊙Susp^ n (X ⊙∧ Y)
  ⊙Σ^∧-out n = Σ^∧-out n , Σ^∧-out-pt n

  ∧Σ^-out : (n : ℕ) → X ∧ ⊙Susp^ n Y → Susp^ n (X ∧ Y)
  ∧Σ^-out O = idf _
  ∧Σ^-out (S n) = Susp-fmap (∧Σ^-out n) ∘ ∧Σ-out X (⊙Susp^ n Y)

  private
    ∧Σ^-out-pt : (n : ℕ) → ∧Σ^-out n (pt (X ⊙∧ ⊙Susp^ n Y)) == pt (⊙Susp^ n (X ⊙∧ Y))
    ∧Σ^-out-pt O = idp
    ∧Σ^-out-pt (S n) = idp

  ⊙∧Σ^-out : (n : ℕ) → X ⊙∧ ⊙Susp^ n Y ⊙→ ⊙Susp^ n (X ⊙∧ Y)
  ⊙∧Σ^-out n = ∧Σ^-out n , ∧Σ^-out-pt n

private
  ⊙maybe-Susp^-flip-+ : ∀ {i} {X : Ptd i} (m n : ℕ) (b : Bool)
    → (m == 0 → b == false)
    → ⊙coe (⊙Susp^-+ m n {X}) ◃⊙∘
      ⊙maybe-Susp^-flip {X = ⊙Susp^ n X} m b ◃⊙idf
      =⊙∘
      ⊙maybe-Susp^-flip (m + n) b ◃⊙∘
      ⊙coe (⊙Susp^-+ m n) ◃⊙idf
  ⊙maybe-Susp^-flip-+ O O b h = =⊙∘-in idp
  ⊙maybe-Susp^-flip-+ O (S n) true h = ⊥-elim (Bool-true≠false (h idp))
  ⊙maybe-Susp^-flip-+ O (S n) false h = =⊙∘-in idp
  ⊙maybe-Susp^-flip-+ {X = X} (S m) n true h =
    ⊙coe (ap ⊙Susp (Susp^-+ m n)) ◃⊙∘
    ⊙Susp-flip (⊙Susp^ m (⊙Susp^ n X)) ◃⊙idf
      =⊙∘₁⟨ 0 & 1 & ! p ⟩
    ⊙Susp-fmap (fst (⊙coe (⊙Susp^-+ m n))) ◃⊙∘
    ⊙Susp-flip (⊙Susp^ m (⊙Susp^ n X)) ◃⊙idf
      =⊙∘⟨ =⊙∘-in $ ! $
           ⊙Susp-flip-natural (⊙coe (⊙Susp^-+ m n)) ⟩
    ⊙Susp-flip (⊙Susp^ (m + n) X) ◃⊙∘
    ⊙Susp-fmap (fst (⊙coe (⊙Susp^-+ m n))) ◃⊙idf
      =⊙∘₁⟨ 1 & 1 & p ⟩
    ⊙Susp-flip (⊙Susp^ (m + n) X) ◃⊙∘
    ⊙coe (ap ⊙Susp (Susp^-+ m n)) ◃⊙idf ∎⊙∘
    where
    p : ⊙Susp-fmap (fst (⊙coe (⊙Susp^-+ m n {X}))) ==
        ⊙coe (ap ⊙Susp (Susp^-+ m n))
    p =
      ap (⊙Susp-fmap ∘ coe) (de⊙-⊙Susp^-+ m n {X}) ∙
      ! (⊙transport-⊙Susp (Susp^-+ m n)) ∙
      ⊙transport-⊙coe ⊙Susp (Susp^-+ m n)
  ⊙maybe-Susp^-flip-+ (S m) n false h = =⊙∘-in $ ! $ ⊙λ= $ ⊙∘-unit-l _

  ⊙maybe-Susp^-flip-comm : ∀ {i} {X : Ptd i} (m n : ℕ) (b : Bool)
    → (m == 0 → b == false)
    → (n == 0 → b == false)
    → ⊙coe (⊙Susp^-comm m n {X = X}) ◃⊙∘
      ⊙maybe-Susp^-flip {X = ⊙Susp^ n X} m b ◃⊙idf
      =⊙∘
      ⊙maybe-Susp^-flip {X = ⊙Susp^ m X} n b ◃⊙∘
      ⊙coe (⊙Susp^-comm m n) ◃⊙idf
  ⊙maybe-Susp^-flip-comm O O b h₁ h₂ = =⊙∘-in (! (⊙λ= (⊙∘-unit-l (⊙coe (⊙Susp^-comm 0 0)))))
  ⊙maybe-Susp^-flip-comm O (S _) true  h₁ h₂ = ⊥-elim (Bool-true≠false (h₁ idp))
  ⊙maybe-Susp^-flip-comm O (S _) false h₁ h₂ = =⊙∘-in (! (⊙λ= (⊙∘-unit-l _)))
  ⊙maybe-Susp^-flip-comm (S m) O true  h₁ h₂ = ⊥-elim (Bool-true≠false (h₂ idp))
  ⊙maybe-Susp^-flip-comm (S m) O false h₁ h₂ = =⊙∘-in (! (⊙λ= (⊙∘-unit-l _)))
  ⊙maybe-Susp^-flip-comm {X = X} (S m) (S n) true h₁ h₂ =
    ⊙coe (⊙Susp^-comm (S m) (S n)) ◃⊙∘
    ⊙Susp-flip (⊙Susp^ m (⊙Susp^ (S n) X)) ◃⊙idf
      =⊙∘₁⟨ 0 & 1 & p ⟩
    ⊙Susp-fmap (fst (⊙coe (⊙Susp^-comm m (S n) ∙ ⊙Susp^-comm 1 n))) ◃⊙∘
    ⊙Susp-flip (⊙Susp^ m (⊙Susp^ (S n) X)) ◃⊙idf
      =⊙∘⟨ =⊙∘-in $ ! $
           ⊙Susp-flip-natural (⊙coe (⊙Susp^-comm m (S n) ∙ ⊙Susp^-comm 1 n)) ⟩
    ⊙Susp-flip (⊙Susp^ n (⊙Susp^ (S m) X)) ◃⊙∘
    ⊙Susp-fmap (fst (⊙coe (⊙Susp^-comm m (S n) ∙ ⊙Susp^-comm 1 n))) ◃⊙idf
      =⊙∘₁⟨ 1 & 1 & ! p ⟩
    ⊙Susp-flip (⊙Susp^ n (⊙Susp^ (S m) X)) ◃⊙∘
    ⊙Susp^-swap (S m) (S n) ◃⊙idf ∎⊙∘
    where
    p : ⊙Susp^-swap (S m) (S n) {X} ==
        ⊙Susp-fmap (fst (⊙coe (⊙Susp^-comm m (S n) {X} ∙ ⊙Susp^-comm 1 n {⊙Susp^ m X})))
    p =
      ap ⊙coe (⊙Susp^-comm-S-S m n) ∙
      ! (⊙transport-⊙coe ⊙Susp (Susp^-comm m (S n) ∙ Susp^-comm 1 n)) ∙
      ⊙transport-⊙Susp (Susp^-comm m (S n) ∙ Susp^-comm 1 n) ∙
      ! (ap (⊙Susp-fmap ∘ coe) (ap2 _∙_ (de⊙-⊙Susp^-comm m (S n)) (de⊙-⊙Susp^-comm 1 n))) ∙
      ap (⊙Susp-fmap ∘ coe) (∙-ap de⊙ (⊙Susp^-comm m (S n)) (⊙Susp^-comm 1 n))
  ⊙maybe-Susp^-flip-comm (S m) (S n) false h₁ h₂ = =⊙∘-in (! (⊙λ= (⊙∘-unit-l _)))

⊙∧Σ-Σ^∧-out : ∀ {i} {j} (X : Ptd i) (Y : Ptd j) (m : ℕ)
  → ⊙Susp-fmap (∧Σ^-out X Y m) ◃⊙∘
    ⊙Σ∧-out X (⊙Susp^ m Y) ◃⊙idf
    =⊙∘
    ⊙Susp^-swap m 1 {X = X ⊙∧ Y} ◃⊙∘
    ⊙maybe-Susp^-flip m (odd m) ◃⊙∘
    ⊙Susp^-fmap m (⊙Σ∧-out X Y) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ X)) Y m ◃⊙idf
⊙∧Σ-Σ^∧-out X Y 0 =
  ⊙Susp-fmap (idf (X ∧ Y)) ◃⊙∘
  ⊙Σ∧-out X (⊙Susp^ 0 Y) ◃⊙idf
    =⊙∘⟨ 0 & 1 & ⊙Susp-fmap-idf _ ⟩
  ⊙Σ∧-out X (⊙Susp^ 0 Y) ◃⊙idf
    =⊙∘⟨ 0 & 0 & ⊙contract ⟩
  ⊙idf _ ◃⊙∘
  ⊙Σ∧-out X (⊙Susp^ 0 Y) ◃⊙idf
    =⊙∘₁⟨ 0 & 1 & ap ⊙coe (! (⊙Susp^-comm-0-l 1 (X ⊙∧ Y))) ⟩
  ⊙Susp^-swap 0 1 ◃⊙∘
  ⊙Σ∧-out X (⊙Susp^ 0 Y) ◃⊙idf
    =⊙∘⟨ 1 & 0 & ⊙contract ⟩
  ⊙Susp^-swap 0 1 ◃⊙∘
  ⊙idf _ ◃⊙∘
  ⊙Σ∧-out X (⊙Susp^ 0 Y) ◃⊙idf
    =⊙∘⟨ 3 & 0 & ⊙contract ⟩
  ⊙Susp^-swap 0 1 ◃⊙∘
  ⊙idf _ ◃⊙∘
  ⊙Σ∧-out X (⊙Susp^ 0 Y) ◃⊙∘
  ⊙idf _ ◃⊙idf ∎⊙∘
⊙∧Σ-Σ^∧-out X Y (S m) =
  ⊙Susp-fmap (∧Σ^-out X Y (S m)) ◃⊙∘
  ⊙Σ∧-out X (⊙Susp^ (S m) Y) ◃⊙idf
    =⊙∘⟨ 0 & 1 & ⊙Susp-fmap-seq-∘ (Susp-fmap (∧Σ^-out X Y m) ◃∘
                                   ∧Σ-out X (⊙Susp^ m Y) ◃idf) ⟩
  ⊙Susp^-fmap 2 (⊙∧Σ^-out X Y m) ◃⊙∘
  ⊙Susp-fmap (∧Σ-out X (⊙Susp^ m Y)) ◃⊙∘
  ⊙Σ∧-out X (⊙Susp^ (S m) Y) ◃⊙idf
    =⊙∘⟨ 1 & 2 & ⊙∧Σ-Σ∧-out X (⊙Susp^ m Y) ⟩
  ⊙Susp^-fmap 2 (⊙∧Σ^-out X Y m) ◃⊙∘
  ⊙Susp-flip (⊙Susp (X ∧ ⊙Susp^ m Y)) ◃⊙∘
  ⊙Susp-fmap (Σ∧-out X (⊙Susp^ m Y)) ◃⊙∘
  ⊙∧Σ-out (⊙Susp (de⊙ X)) (⊙Susp^ m Y) ◃⊙idf
    =⊙∘⟨ 0 & 2 & =⊙∘-in {gs = ⊙Susp-flip (⊙Susp (Susp^ m (X ∧ Y))) ◃⊙∘
                              ⊙Susp^-fmap 2 (⊙∧Σ^-out X Y m) ◃⊙idf} $
                 ! (⊙Susp-flip-natural (⊙Susp-fmap (∧Σ^-out X Y m))) ⟩
  ⊙Susp-flip (⊙Susp (Susp^ m (X ∧ Y))) ◃⊙∘
  ⊙Susp^-fmap 2 (⊙∧Σ^-out X Y m) ◃⊙∘
  ⊙Susp-fmap (Σ∧-out X (⊙Susp^ m Y)) ◃⊙∘
  ⊙∧Σ-out (⊙Susp (de⊙ X)) (⊙Susp^ m Y) ◃⊙idf
    =⊙∘⟨ 1 & 2 & ⊙Susp-fmap-seq-=⊙∘ (de⊙-seq-=⊙∘ (⊙∧Σ-Σ^∧-out X Y m)) ⟩
  ⊙Susp-flip (⊙Susp (Susp^ m (X ∧ Y))) ◃⊙∘
  ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm m 1))) ◃⊙∘
  ⊙Susp-fmap (fst (⊙maybe-Susp^-flip m (odd m))) ◃⊙∘
  ⊙Susp^-fmap (S m) (⊙Σ∧-out X Y) ◃⊙∘
  ⊙Susp-fmap (∧Σ^-out (⊙Susp (de⊙ X)) Y m) ◃⊙∘
  ⊙∧Σ-out (⊙Susp (de⊙ X)) (⊙Susp^ m Y) ◃⊙idf
    =⊙∘⟨ 0 & 2 & =⊙∘-in {gs = ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm m 1))) ◃⊙∘
                              ⊙Susp-flip (⊙Susp^ m (⊙Susp (X ∧ Y))) ◃⊙idf} $
                 ⊙Susp-flip-natural (⊙coe (⊙Susp^-comm m 1)) ⟩
  ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm m 1))) ◃⊙∘
  ⊙Susp-flip (⊙Susp^ m (⊙Susp (X ∧ Y))) ◃⊙∘
  ⊙Susp-fmap (fst (⊙maybe-Susp^-flip m (odd m))) ◃⊙∘
  ⊙Susp^-fmap (S m) (⊙Σ∧-out X Y) ◃⊙∘
  ⊙Susp-fmap (∧Σ^-out (⊙Susp (de⊙ X)) Y m) ◃⊙∘
  ⊙∧Σ-out (⊙Susp (de⊙ X)) (⊙Susp^ m Y) ◃⊙idf
    =⊙∘⟨ 4 & 2 & ⊙contract ⟩
  ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm m 1))) ◃⊙∘
  ⊙Susp-flip (⊙Susp^ m (⊙Susp (X ∧ Y))) ◃⊙∘
  ⊙Susp-fmap (fst (⊙maybe-Susp^-flip m (odd m))) ◃⊙∘
  ⊙Susp^-fmap (S m) (⊙Σ∧-out X Y) ◃⊙∘
  ⊙∧Σ^-out (⊙Susp (de⊙ X)) Y (S m) ◃⊙idf
    =⊙∘₁⟨ 0 & 1 & ! (⊙transport-⊙Susp (ap de⊙ (⊙Susp^-comm m 1))) ∙
                  ap (⊙transport ⊙Susp) (de⊙-⊙Susp^-comm m 1) ∙
                  ⊙transport-⊙coe ⊙Susp (Susp^-comm m 1) ∙
                  ap ⊙coe (! (⊙Susp^-comm-S-1 m {X ⊙∧ Y})) ⟩
  ⊙coe (⊙Susp^-comm (S m) 1) ◃⊙∘
  ⊙Susp-flip (⊙Susp^ m (⊙Susp (X ∧ Y))) ◃⊙∘
  ⊙Susp-fmap (fst (⊙maybe-Susp^-flip m (odd m))) ◃⊙∘
  ⊙Susp^-fmap (S m) (⊙Σ∧-out X Y) ◃⊙∘
  ⊙∧Σ^-out (⊙Susp (de⊙ X)) Y (S m) ◃⊙idf
    =⊙∘₁⟨ 2 & 1 & ap ⊙Susp-fmap (de⊙-⊙maybe-Susp^-flip m (odd m)) ⟩
  ⊙coe (⊙Susp^-comm (S m) 1) ◃⊙∘
  ⊙Susp-flip (⊙Susp^ m (⊙Susp (X ∧ Y))) ◃⊙∘
  ⊙Susp-fmap (maybe-Susp^-flip m (odd m)) ◃⊙∘
  ⊙Susp^-fmap (S m) (⊙Σ∧-out X Y) ◃⊙∘
  ⊙∧Σ^-out (⊙Susp (de⊙ X)) Y (S m) ◃⊙idf
    =⊙∘₁⟨ 2 & 1 & ⊙Susp-fmap-maybe-Susp^-flip m (odd m) (ap odd) ⟩
  ⊙coe (⊙Susp^-comm (S m) 1) ◃⊙∘
  ⊙Susp-flip (⊙Susp^ m (⊙Susp (X ∧ Y))) ◃⊙∘
  ⊙maybe-Susp-flip (⊙Susp^ m (⊙Susp (X ∧ Y))) (odd m) ◃⊙∘
  ⊙Susp^-fmap (S m) (⊙Σ∧-out X Y) ◃⊙∘
  ⊙∧Σ^-out (⊙Susp (de⊙ X)) Y (S m) ◃⊙idf
    =⊙∘⟨ 1 & 2 & ⊙maybe-Susp-flip-flip _ true (odd m) ⟩
  ⊙coe (⊙Susp^-comm (S m) 1) ◃⊙∘
  ⊙maybe-Susp-flip (⊙Susp^ m (⊙Susp (X ∧ Y))) (odd (S m)) ◃⊙∘
  ⊙Susp^-fmap (S m) (⊙Σ∧-out X Y) ◃⊙∘
  ⊙∧Σ^-out (⊙Susp (de⊙ X)) Y (S m) ◃⊙idf ∎⊙∘

module _ {i j} (X : Ptd i) (Y : Ptd j) where

  ⊙∧Σ^-Σ^∧-out : ∀ (m n : ℕ)
    → ⊙Susp^-fmap m (⊙∧Σ^-out X Y n) ◃⊙∘
      ⊙Σ^∧-out X (⊙Susp^ n Y) m ◃⊙idf
      =⊙∘
      ⊙coe (⊙Susp^-comm n m) ◃⊙∘
      ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
      ⊙Susp^-fmap n (⊙Σ^∧-out X Y m) ◃⊙∘
      ⊙∧Σ^-out (⊙Susp^ m X) Y n ◃⊙idf
  ⊙∧Σ^-Σ^∧-out O n =
    ⊙∧Σ^-out X Y n ◃⊙∘
    ⊙idf _ ◃⊙idf
      =⊙∘⟨ 1 & 1 & ⊙expand ⊙idf-seq ⟩
    ⊙∧Σ^-out X Y n ◃⊙idf
      =⊙∘⟨ 0 & 0 & !⊙∘ (⊙Susp^-fmap-idf n (X ⊙∧ Y)) ⟩
    ⊙Susp^-fmap n (⊙idf (X ⊙∧ Y)) ◃⊙∘
    ⊙∧Σ^-out X Y n ◃⊙idf
      =⊙∘₁⟨ 0 & 0 & ! (⊙maybe-Susp^-flip-false n) ⟩
    ⊙maybe-Susp^-flip n false ◃⊙∘
    ⊙Susp^-fmap n (⊙idf (X ⊙∧ Y)) ◃⊙∘
    ⊙∧Σ^-out X Y n ◃⊙idf
      =⊙∘₁⟨ 0 & 0 & ap ⊙coe (! (⊙Susp^-comm-0-r n _)) ⟩
    ⊙coe (⊙Susp^-comm n O) ◃⊙∘
    ⊙maybe-Susp^-flip n false ◃⊙∘
    ⊙Susp^-fmap n (⊙idf (X ⊙∧ Y)) ◃⊙∘
    ⊙∧Σ^-out X Y n ◃⊙idf ∎⊙∘
  ⊙∧Σ^-Σ^∧-out (S m) n =
    ⊙Susp^-fmap (S m) (⊙∧Σ^-out X Y n) ◃⊙∘
    ⊙Σ^∧-out X (⊙Susp^ n Y) (S m) ◃⊙idf
      =⊙∘⟨ 1 & 1 & ⊙expand (⊙Susp-fmap (Σ^∧-out X (⊙Susp^ n Y) m) ◃⊙∘
                            ⊙Σ∧-out (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf) ⟩
    ⊙Susp^-fmap (S m) (⊙∧Σ^-out X Y n) ◃⊙∘
    ⊙Susp-fmap (Σ^∧-out X (⊙Susp^ n Y) m) ◃⊙∘
    ⊙Σ∧-out (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf
      =⊙∘⟨ 0 & 2 & ⊙Susp-fmap-seq-=⊙∘ (de⊙-seq-=⊙∘ (⊙∧Σ^-Σ^∧-out m n)) ⟩
    ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm n m))) ◃⊙∘
    ⊙Susp-fmap (fst (⊙maybe-Susp^-flip n (and (odd m) (odd n)))) ◃⊙∘
    ⊙Susp-fmap (Susp^-fmap n (Σ^∧-out X Y m)) ◃⊙∘
    ⊙Susp-fmap (∧Σ^-out (⊙Susp^ m X) Y n) ◃⊙∘
    ⊙Σ∧-out (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf
      =⊙∘⟨ 3 & 2 & ⊙∧Σ-Σ^∧-out (⊙Susp^ m X) Y n ⟩
    ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm n m))) ◃⊙∘
    ⊙Susp-fmap (fst (⊙maybe-Susp^-flip n (and (odd m) (odd n)))) ◃⊙∘
    ⊙Susp-fmap (Susp^-fmap n (Σ^∧-out X Y m)) ◃⊙∘
    ⊙Susp^-swap n 1 ◃⊙∘
    ⊙maybe-Susp^-flip n (odd n) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ∧-out (⊙Susp^ m X) Y) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ (⊙Susp^ m X))) Y n ◃⊙idf
      =⊙∘⟨ 2 & 2 & !⊙∘ $ ⊙Susp^-swap-natural n 1 (⊙Σ^∧-out X Y m) ⟩
    ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm n m))) ◃⊙∘
    ⊙Susp-fmap (fst (⊙maybe-Susp^-flip n (and (odd m) (odd n)))) ◃⊙∘
    ⊙Susp^-swap n 1 ◃⊙∘
    ⊙Susp^-fmap n (⊙Susp-fmap (Σ^∧-out X Y m)) ◃⊙∘
    ⊙maybe-Susp^-flip n (odd n) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ∧-out (⊙Susp^ m X) Y) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ (⊙Susp^ m X))) Y n ◃⊙idf
      =⊙∘⟨ 3 & 2 & ⊙maybe-Susp^-flip-natural-=⊙∘ (⊙Susp-fmap (Σ^∧-out X Y m)) n (odd n) ⟩
    ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm n m))) ◃⊙∘
    ⊙Susp-fmap (fst (⊙maybe-Susp^-flip n (and (odd m) (odd n)))) ◃⊙∘
    ⊙Susp^-swap n 1 ◃⊙∘
    ⊙maybe-Susp^-flip n (odd n) ◃⊙∘
    ⊙Susp^-fmap n (⊙Susp-fmap (Σ^∧-out X Y m)) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ∧-out (⊙Susp^ m X) Y) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ (⊙Susp^ m X))) Y n ◃⊙idf
      =⊙∘₁⟨ 4 & 2 & ! $ ⊙Susp^-fmap-∘ n (⊙Susp-fmap (Σ^∧-out X Y m)) (⊙Σ∧-out (⊙Susp^ m X) Y) ⟩
    ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm n m))) ◃⊙∘
    ⊙Susp-fmap (fst (⊙maybe-Susp^-flip n (and (odd m) (odd n)))) ◃⊙∘
    ⊙Susp^-swap n 1 ◃⊙∘
    ⊙maybe-Susp^-flip n (odd n) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y (S m)) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ (⊙Susp^ m X))) Y n ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙maybe-Susp^-flip-⊙Susp^-comm _ n 1 (and (odd m) (odd n)) ⟩
    ⊙Susp-fmap (coe (ap de⊙ (⊙Susp^-comm n m))) ◃⊙∘
    ⊙coe (⊙Susp^-comm n 1) ◃⊙∘
    ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
    ⊙maybe-Susp^-flip n (odd n) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y (S m)) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ (⊙Susp^ m X))) Y n ◃⊙idf
      =⊙∘₁⟨ 0 & 1 & ! (⊙transport-⊙Susp (ap de⊙ (⊙Susp^-comm n m))) ∙
                    ⊙transport-⊙coe ⊙Susp (ap de⊙ (⊙Susp^-comm n m)) ∙
                    ap (⊙coe ∘ ap ⊙Susp) (de⊙-⊙Susp^-comm n m) ⟩
    ⊙coe (ap ⊙Susp (Susp^-comm n m)) ◃⊙∘
    ⊙coe (⊙Susp^-comm n 1) ◃⊙∘
    ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
    ⊙maybe-Susp^-flip n (odd n) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y (S m)) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ (⊙Susp^ m X))) Y n ◃⊙idf
      =⊙∘₁⟨ 0 & 2 & ! (=⊙∘-out (⊙coe-∙ (⊙Susp^-comm n 1) (ap ⊙Susp (Susp^-comm n m)))) ∙
                    ap ⊙coe (! (=ₛ-out (⊙Susp^-comm-S-r n m))) ⟩
    ⊙coe (⊙Susp^-comm n (S m)) ◃⊙∘
    ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
    ⊙maybe-Susp^-flip n (odd n) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y (S m)) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ (⊙Susp^ m X))) Y n ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙maybe-Susp^-flip-flip n (and (odd m) (odd n)) (odd n) ⟩
    ⊙coe (⊙Susp^-comm n (S m)) ◃⊙∘
    ⊙maybe-Susp^-flip n (xor (and (odd m) (odd n)) (odd n)) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y (S m)) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ (⊙Susp^ m X))) Y n ◃⊙idf
      =⊙∘₁⟨ 1 & 1 & ap (⊙maybe-Susp^-flip n) (table (odd m) (odd n)) ⟩
    ⊙coe (⊙Susp^-comm n (S m)) ◃⊙∘
    ⊙maybe-Susp^-flip n (and (odd (S m)) (odd n)) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y (S m)) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp (de⊙ (⊙Susp^ m X))) Y n ◃⊙idf ∎⊙∘
    where
    table : ∀ (b c : Bool) → xor (and b c) c == and (negate b) c
    table true  true  = idp
    table true  false = idp
    table false true  = idp
    table false false = idp

  ⊙∧Σ^-Σ^∧-out' : ∀ (m n : ℕ)
    → ⊙Susp^-fmap n (⊙Σ^∧-out X Y m) ◃⊙∘
      ⊙∧Σ^-out (⊙Susp^ m X) Y n ◃⊙idf
      =⊙∘
      ⊙coe (⊙Susp^-comm m n) ◃⊙∘
      ⊙maybe-Susp^-flip m (and (odd m) (odd n)) ◃⊙∘
      ⊙Susp^-fmap m (⊙∧Σ^-out X Y n) ◃⊙∘
      ⊙Σ^∧-out X (⊙Susp^ n Y) m ◃⊙idf
  ⊙∧Σ^-Σ^∧-out' m n =
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y m) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp^ m X) Y n ◃⊙idf
      =⊙∘₁⟨ 0 & 0 & ! (⊙maybe-Susp^-flip-false n) ∙
                    ap (⊙maybe-Susp^-flip n) (! (xor-diag (and (odd m) (odd n)))) ⟩
    ⊙maybe-Susp^-flip n (xor (and (odd m) (odd n)) (and (odd m) (odd n))) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y m) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp^ m X) Y n ◃⊙idf
      =⊙∘⟨ 0 & 1 & !⊙∘ $ ⊙maybe-Susp^-flip-flip n (and (odd m) (odd n)) (and (odd m) (odd n)) ⟩
    ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
    ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y m) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp^ m X) Y n ◃⊙idf
      =⊙∘⟨ 1 & 0 & =⊙∘-in {gs = ⊙coe (! (⊙Susp^-comm n m)) ◃⊙∘
                                ⊙coe (⊙Susp^-comm n m) ◃⊙idf} $
           ap ⊙coe (! (!-inv-r (⊙Susp^-comm n m))) ∙
           =⊙∘-out (⊙coe-∙ (⊙Susp^-comm n m) (! (⊙Susp^-comm n m))) ⟩
    ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
    ⊙coe (! (⊙Susp^-comm n m)) ◃⊙∘
    ⊙coe (⊙Susp^-comm n m) ◃⊙∘
    ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
    ⊙Susp^-fmap n (⊙Σ^∧-out X Y m) ◃⊙∘
    ⊙∧Σ^-out (⊙Susp^ m X) Y n ◃⊙idf
      =⊙∘⟨ 2 & 4 & !⊙∘ $ ⊙∧Σ^-Σ^∧-out m n ⟩
    ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
    ⊙coe (! (⊙Susp^-comm n m)) ◃⊙∘
    ⊙Susp^-fmap m (⊙∧Σ^-out X Y n) ◃⊙∘
    ⊙Σ^∧-out X (⊙Susp^ n Y) m ◃⊙idf
      =⊙∘₁⟨ 1 & 1 & ap ⊙coe (⊙Susp^-comm-! n m) ⟩
    ⊙maybe-Susp^-flip n (and (odd m) (odd n)) ◃⊙∘
    ⊙coe (⊙Susp^-comm m n) ◃⊙∘
    ⊙Susp^-fmap m (⊙∧Σ^-out X Y n) ◃⊙∘
    ⊙Σ^∧-out X (⊙Susp^ n Y) m ◃⊙idf
      =⊙∘⟨ 0 & 2 & !⊙∘ $
           ⊙maybe-Susp^-flip-comm m n (and (odd m) (odd n))
             (ap (λ k → and (odd k) (odd n)))
             (λ p → ap (and (odd m) ∘ odd) p ∙ and-false-r (odd m)) ⟩
    ⊙coe (⊙Susp^-comm m n) ◃⊙∘
    ⊙maybe-Susp^-flip m (and (odd m) (odd n)) ◃⊙∘
    ⊙Susp^-fmap m (⊙∧Σ^-out X Y n) ◃⊙∘
    ⊙Σ^∧-out X (⊙Susp^ n Y) m ◃⊙idf ∎⊙∘

  ⊙Σ^∧Σ^-out-seq : ∀ (m n : ℕ) → (⊙Susp^ m X ⊙∧ ⊙Susp^ n Y) ⊙–→ ⊙Susp^ (m + n) (X ⊙∧ Y)
  ⊙Σ^∧Σ^-out-seq m n =
    ⊙coe (⊙Susp^-+ m n {X ⊙∧ Y}) ◃⊙∘
    ⊙Susp^-fmap m (⊙∧Σ^-out X Y n) ◃⊙∘
    ⊙Σ^∧-out X (⊙Susp^ n Y) m ◃⊙idf

  ⊙Σ^∧Σ^-out : ∀ (m n : ℕ) → ⊙Susp^ m X ⊙∧ ⊙Susp^ n Y ⊙→ ⊙Susp^ (m + n) (X ⊙∧ Y)
  ⊙Σ^∧Σ^-out m n = ⊙compose (⊙Σ^∧Σ^-out-seq m n)

  swap-⊙Σ^∧-out : ∀ (m : ℕ)
    → ⊙Susp^-fmap m (⊙∧-swap X Y) ◃⊙∘
      ⊙Σ^∧-out X Y m ◃⊙idf
      =⊙∘
      ⊙∧Σ^-out Y X m ◃⊙∘
      ⊙∧-swap (⊙Susp^ m X) Y ◃⊙idf
  swap-⊙Σ^∧-out O = =⊙∘-in $ ! $ ⊙λ= $ ⊙∘-unit-l (⊙∧-swap X Y)
  swap-⊙Σ^∧-out (S m) =
    ⊙Susp^-fmap (S m) (⊙∧-swap X Y) ◃⊙∘
    ⊙Σ^∧-out X Y (S m) ◃⊙idf
      =⊙∘⟨ 1 & 1 & ⊙expand (⊙Susp-fmap (Σ^∧-out X Y m) ◃⊙∘
                            ⊙Σ∧-out (⊙Susp^ m X) Y ◃⊙idf) ⟩
    ⊙Susp^-fmap (S m) (⊙∧-swap X Y) ◃⊙∘
    ⊙Susp-fmap (Σ^∧-out X Y m) ◃⊙∘
    ⊙Σ∧-out (⊙Susp^ m X) Y ◃⊙idf
      =⊙∘⟨ 0 & 2 &
           ⊙Susp-fmap-seq-=⊙∘ (de⊙-seq-=⊙∘ (swap-⊙Σ^∧-out m)) ⟩
    ⊙Susp-fmap (∧Σ^-out Y X m) ◃⊙∘
    ⊙Susp-fmap (∧-swap (⊙Susp^ m X) Y) ◃⊙∘
    ⊙Σ∧-out (⊙Susp^ m X) Y ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙swap-Σ∧-out (⊙Susp^ m X) Y ⟩
    ⊙Susp-fmap (∧Σ^-out Y X m) ◃⊙∘
    ⊙∧Σ-out Y (⊙Susp^ m X) ◃⊙∘
    ⊙∧-swap (⊙Susp (de⊙ (⊙Susp^ m X))) Y ◃⊙idf
      =⊙∘⟨ 0 & 2 & ⊙contract ⟩
    ⊙∧Σ^-out Y X (S m) ◃⊙∘
    ⊙∧-swap (⊙Susp^ (S m) X) Y ◃⊙idf ∎⊙∘

  swap-⊙∧Σ^-out : ∀ (n : ℕ)
    → ⊙Susp^-fmap n (⊙∧-swap X Y) ◃⊙∘
      ⊙∧Σ^-out X Y n ◃⊙idf
      =⊙∘
      ⊙Σ^∧-out Y X n ◃⊙∘
      ⊙∧-swap X (⊙Susp^ n Y) ◃⊙idf
  swap-⊙∧Σ^-out O = =⊙∘-in $ ! $ ⊙λ= $ ⊙∘-unit-l (⊙∧-swap X Y)
  swap-⊙∧Σ^-out (S n) =
    ⊙Susp^-fmap (S n) (⊙∧-swap X Y) ◃⊙∘
    ⊙∧Σ^-out X Y (S n) ◃⊙idf
      =⊙∘⟨ 1 & 1 & ⊙expand (⊙Susp-fmap (∧Σ^-out X Y n) ◃⊙∘
                            ⊙∧Σ-out X (⊙Susp^ n Y) ◃⊙idf) ⟩
    ⊙Susp^-fmap (S n) (⊙∧-swap X Y) ◃⊙∘
    ⊙Susp-fmap (∧Σ^-out X Y n) ◃⊙∘
    ⊙∧Σ-out X (⊙Susp^ n Y) ◃⊙idf
      =⊙∘⟨ 0 & 2 &
           ⊙Susp-fmap-seq-=⊙∘ (de⊙-seq-=⊙∘ (swap-⊙∧Σ^-out n))⟩
    ⊙Susp-fmap (fst (⊙Σ^∧-out Y X n)) ◃⊙∘
    ⊙Susp-fmap (fst (⊙∧-swap X (⊙Susp^ n Y))) ◃⊙∘
    ⊙∧Σ-out X (⊙Susp^ n Y) ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙swap-∧Σ-out X (⊙Susp^ n Y) ⟩
    ⊙Susp-fmap (fst (⊙Σ^∧-out Y X n)) ◃⊙∘
    ⊙Σ∧-out (⊙Susp^ n Y) X ◃⊙∘
    ⊙∧-swap X (⊙Susp (de⊙ (⊙Susp^ n Y))) ◃⊙idf
      =⊙∘⟨ 0 & 2 & ⊙contract ⟩
    ⊙Σ^∧-out Y X (S n) ◃⊙∘
    ⊙∧-swap X (⊙Susp^ (S n) Y) ◃⊙idf ∎⊙∘

⊙Σ^∧Σ^-out-swap : ∀ {i} {j} (X : Ptd i) (Y : Ptd j) (m n : ℕ)
  → ⊙transport (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃⊙∘
    ⊙Susp^-fmap (m + n) (⊙∧-swap X Y) ◃⊙∘
    ⊙Σ^∧Σ^-out X Y m n ◃⊙idf
    =⊙∘
    ⊙maybe-Susp^-flip (n + m) (and (odd n) (odd m)) ◃⊙∘
    ⊙Σ^∧Σ^-out Y X n m ◃⊙∘
    ⊙∧-swap (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf
⊙Σ^∧Σ^-out-swap X Y m n =
  ⊙transport (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃⊙∘
  ⊙Susp^-fmap (m + n) (⊙∧-swap X Y) ◃⊙∘
  ⊙Σ^∧Σ^-out X Y m n ◃⊙idf
    =⊙∘⟨ 2 & 1 & ⊙expand (⊙Σ^∧Σ^-out-seq X Y m n) ⟩
  ⊙transport (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃⊙∘
  ⊙Susp^-fmap (m + n) (⊙∧-swap X Y) ◃⊙∘
  ⊙coe (⊙Susp^-+ m n) ◃⊙∘
  ⊙Susp^-fmap m (⊙∧Σ^-out X Y n) ◃⊙∘
  ⊙Σ^∧-out X (⊙Susp^ n Y) m ◃⊙idf
    =⊙∘⟨ 1 & 2 & =⊙∘-in {gs = ⊙coe (⊙Susp^-+ m n) ◃⊙∘
                              ⊙Susp^-fmap m (⊙Susp^-fmap n (⊙∧-swap X Y)) ◃⊙idf} $
         ! $ ⊙Susp^-+-natural m n (⊙∧-swap X Y) ⟩
  ⊙transport (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃⊙∘
  ⊙coe (⊙Susp^-+ m n) ◃⊙∘
  ⊙Susp^-fmap m (⊙Susp^-fmap n (⊙∧-swap X Y)) ◃⊙∘
  ⊙Susp^-fmap m (⊙∧Σ^-out X Y n) ◃⊙∘
  ⊙Σ^∧-out X (⊙Susp^ n Y) m ◃⊙idf
    =⊙∘⟨ 2 & 2 & ⊙Susp^-fmap-seq-=⊙∘ m $ swap-⊙∧Σ^-out X Y n ⟩
  ⊙transport (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃⊙∘
  ⊙coe (⊙Susp^-+ m n) ◃⊙∘
  ⊙Susp^-fmap m (⊙Σ^∧-out Y X n) ◃⊙∘
  ⊙Susp^-fmap m (⊙∧-swap X (⊙Susp^ n Y)) ◃⊙∘
  ⊙Σ^∧-out X (⊙Susp^ n Y) m ◃⊙idf
    =⊙∘⟨ 3 & 2 & swap-⊙Σ^∧-out X (⊙Susp^ n Y) m ⟩
  ⊙transport (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃⊙∘
  ⊙coe (⊙Susp^-+ m n) ◃⊙∘
  ⊙Susp^-fmap m (⊙Σ^∧-out Y X n) ◃⊙∘
  ⊙∧Σ^-out (⊙Susp^ n Y) X m ◃⊙∘
  ⊙∧-swap (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf
    =⊙∘⟨ 2 & 2 & ⊙∧Σ^-Σ^∧-out' Y X n m ⟩
  ⊙transport (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃⊙∘
  ⊙coe (⊙Susp^-+ m n) ◃⊙∘
  ⊙coe (⊙Susp^-comm n m) ◃⊙∘
  ⊙maybe-Susp^-flip n (and (odd n) (odd m)) ◃⊙∘
  ⊙Susp^-fmap n (⊙∧Σ^-out Y X m) ◃⊙∘
  ⊙Σ^∧-out Y (⊙Susp^ m X) n ◃⊙∘
  ⊙∧-swap (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf
    =⊙∘₁⟨ 0 & 1 & ⊙transport-⊙coe (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ⟩
  ⊙coe (ap (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n)) ◃⊙∘
  ⊙coe (⊙Susp^-+ m n) ◃⊙∘
  ⊙coe (⊙Susp^-comm n m) ◃⊙∘
  ⊙maybe-Susp^-flip n (and (odd n) (odd m)) ◃⊙∘
  ⊙Susp^-fmap n (⊙∧Σ^-out Y X m) ◃⊙∘
  ⊙Σ^∧-out Y (⊙Susp^ m X) n ◃⊙∘
  ⊙∧-swap (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf
    =⊙∘⟨ 0 & 3 & ⊙coe-seq-=ₛ p ⟩
  ⊙coe (⊙Susp^-+ n m) ◃⊙∘
  ⊙maybe-Susp^-flip n (and (odd n) (odd m)) ◃⊙∘
  ⊙Susp^-fmap n (⊙∧Σ^-out Y X m) ◃⊙∘
  ⊙Σ^∧-out Y (⊙Susp^ m X) n ◃⊙∘
  ⊙∧-swap (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf
    =⊙∘⟨ 0 & 2 & ⊙maybe-Susp^-flip-+ n m (and (odd n) (odd m))
           (ap (λ k → and (odd k) (odd m))) ⟩
  ⊙maybe-Susp^-flip (n + m) (and (odd n) (odd m)) ◃⊙∘
  ⊙coe (⊙Susp^-+ n m) ◃⊙∘
  ⊙Susp^-fmap n (⊙∧Σ^-out Y X m) ◃⊙∘
  ⊙Σ^∧-out Y (⊙Susp^ m X) n ◃⊙∘
  ⊙∧-swap (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf
    =⊙∘⟨ 1 & 3 & ⊙contract ⟩
  ⊙maybe-Susp^-flip (n + m) (and (odd n) (odd m)) ◃⊙∘
  ⊙Σ^∧Σ^-out Y X n m ◃⊙∘
  ⊙∧-swap (⊙Susp^ m X) (⊙Susp^ n Y) ◃⊙idf ∎⊙∘
  where
  p : ⊙Susp^-comm n m ◃∙
      ⊙Susp^-+ m n {Y ⊙∧ X} ◃∙
      ap (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃∎
      =ₛ
      ⊙Susp^-+ n m {Y ⊙∧ X} ◃∎
  p =
    ⊙Susp^-comm n m ◃∙
    ⊙Susp^-+ m n {Y ⊙∧ X} ◃∙
    ap (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃∎
      =ₛ⟨ 0 & 1 & expand (⊙Susp^-comm-seq n m) ⟩
    ⊙Susp^-+ n m ◃∙
    ap (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm n m) ◃∙
    ! (⊙Susp^-+ m n) ◃∙
    ⊙Susp^-+ m n ◃∙
    ap (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃∎
      =ₛ⟨ 2 & 2 & seq-!-inv-l (⊙Susp^-+ m n ◃∎) ⟩
    ⊙Susp^-+ n m ◃∙
    ap (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm n m) ◃∙
    ap (λ k → ⊙Susp^ k (Y ⊙∧ X)) (+-comm m n) ◃∎
      =ₛ⟨ 1 & 2 & ap-seq-=ₛ (λ k → ⊙Susp^ k (Y ⊙∧ X)) $
          =ₛ-in {s = +-comm n m ◃∙ +-comm m n ◃∎} {t = []} $
          set-path ℕ-level _ _ ⟩
    ⊙Susp^-+ n m ◃∎ ∎ₛ


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

open import HoTT
open import homotopy.elims.CofPushoutSection as CofPushoutSection

module homotopy.SmashIsCofiber {i j} (X : Ptd i) (Y : Ptd j) where

{- the proof that our smash matches the more classical definition as a cofiber -}

Smash-equiv-Cof : Smash X Y ≃ Cofiber (∨-to-× {X = X} {Y = Y})
Smash-equiv-Cof = equiv to from to-from from-to where
  module To = SmashRec {C = Cofiber (∨-to-× {X = X} {Y = Y})}
    (curry cfcod) cfbase cfbase
    (λ x → ! (cfglue (winl x)))
    (λ y → ! (cfglue (winr y)))
  to : Smash X Y → Cofiber (∨-to-× {X = X} {Y = Y})
  to = To.f

  module FromGlue = WedgeElim
    {P = λ x∨y → smin (pt X) (pt Y) == uncurry smin (∨-to-× {X = X} {Y = Y} x∨y)}
    (λ x → smgluel (pt X) ∙' ! (smgluel x))
    (λ y → smgluer (pt Y) ∙' ! (smgluer y))
    (↓-cst=app-in $
        ap2 _∙'_ (!-inv'-r (smgluel (pt X)))
          ( ap-∘ (uncurry smin) (∨-to-× {X = X} {Y = Y}) wglue
          ∙ ap (ap (uncurry smin)) (∨-to-×-glue-β {X = X} {Y = Y}))
      ∙ ! (!-inv'-r (smgluer (pt Y))))

  module From = CofiberRec {C = Smash X Y}
    (smin (pt X) (pt Y)) (uncurry smin)
    FromGlue.f

  from : Cofiber (∨-to-× {X = X} {Y = Y}) → Smash X Y
  from = From.f

  abstract
    from-to : ∀ x → from (to x) == x
    from-to = Smash-elim
      (λ _ _ → idp) (smgluel (pt X)) (smgluer (pt Y))
      (λ x → ↓-∘=idf-in' from to $
        ap from (ap to (smgluel x)) ∙' smgluel (pt X)
          =⟨ ap (_∙' smgluel (pt X)) $
              ap from (ap to (smgluel x))
                =⟨ ap (ap from) (To.smgluel-β x) ⟩
              ap from (! (cfglue (winl x)))
                =⟨ ap-! from (cfglue (winl x)) ⟩
              ! (ap from (cfglue (winl x)))
                =⟨ ap ! $ From.glue-β (winl x) ⟩
              ! (smgluel (pt X) ∙' ! (smgluel x))
                =⟨ !-∙' (smgluel (pt X)) (! (smgluel x)) ⟩
              ! (! (smgluel x)) ∙' ! (smgluel (pt X))
                =∎ ⟩
        (! (! (smgluel x)) ∙' ! (smgluel (pt X))) ∙' smgluel (pt X)
          =⟨ ∙'-assoc (! (! (smgluel x))) (! (smgluel (pt X))) (smgluel (pt X)) ⟩
        ! (! (smgluel x)) ∙' ! (smgluel (pt X)) ∙' smgluel (pt X)
          =⟨ ap2 _∙'_ (!-! (smgluel x)) (!-inv'-l (smgluel (pt X))) ⟩
        smgluel x
          =∎)
      (λ y → ↓-∘=idf-in' from to $
        ap from (ap to (smgluer y)) ∙' smgluer (pt Y)
          =⟨ ap (_∙' smgluer (pt Y)) $
              ap from (ap to (smgluer y))
                =⟨ ap (ap from) (To.smgluer-β y) ⟩
              ap from (! (cfglue (winr y)))
                =⟨ ap-! from (cfglue (winr y)) ⟩
              ! (ap from (cfglue (winr y)))
                =⟨ ap ! $ From.glue-β (winr y) ⟩
              ! (smgluer (pt Y) ∙' ! (smgluer y))
                =⟨ !-∙' (smgluer (pt Y)) (! (smgluer y)) ⟩
              ! (! (smgluer y)) ∙' ! (smgluer (pt Y))
                =∎ ⟩
        (! (! (smgluer y)) ∙' ! (smgluer (pt Y))) ∙' smgluer (pt Y)
          =⟨ ∙'-assoc (! (! (smgluer y))) (! (smgluer (pt Y))) (smgluer (pt Y)) ⟩
        ! (! (smgluer y)) ∙' ! (smgluer (pt Y)) ∙' smgluer (pt Y)
          =⟨ ap2 _∙'_ (!-! (smgluer y)) (!-inv'-l (smgluer (pt Y))) ⟩
        smgluer y
          =∎)

    to-from : ∀ x → to (from x) == x
    to-from = CofPushoutSection.elim
      (λ _ → unit) (λ _ → idp)
      (! (cfglue (winr (pt Y)))) (λ _ → idp)
      (λ x → ↓-∘=idf-in' to from $
        ap to (ap from (cfglue (winl x)))
          =⟨ ap (ap to) (From.glue-β (winl x)) ⟩
        ap to (FromGlue.f (winl x))
          =⟨ ap-∙' to (smgluel (pt X)) (! (smgluel x)) ⟩
        ap to (smgluel (pt X)) ∙' ap to (! (smgluel x))
          =⟨ ap2 _∙'_
              (To.smgluel-β (pt X))
              (ap-! to (smgluel x) ∙ ap ! (To.smgluel-β x) ∙ !-! (cfglue (winl x))) ⟩
        ! (cfglue (winl (pt X))) ∙' cfglue (winl x)
          =⟨ ap (λ p → ! p ∙' cfglue (winl x)) $
              ap (cfglue (winl (pt X)) ∙'_)
                ( ap (ap cfcod) (! ∨-to-×-glue-β)
                ∙ ∘-ap cfcod ∨-to-× wglue)
            ∙ ↓-cst=app-out (apd cfglue wglue)
          ⟩
        ! (cfglue (winr (pt Y))) ∙' cfglue (winl x)
          =⟨ ∙'=∙ (! (cfglue (winr (pt Y)))) (cfglue (winl x)) ⟩
        ! (cfglue (winr (pt Y))) ∙ cfglue (winl x)
          =∎)
      (λ y → ↓-∘=idf-in' to from $
        ap to (ap from (cfglue (winr y)))
          =⟨ ap (ap to) (From.glue-β (winr y)) ⟩
        ap to (FromGlue.f (winr y))
          =⟨ ap-∙' to (smgluer (pt Y)) (! (smgluer y)) ⟩
        ap to (smgluer (pt Y)) ∙' ap to (! (smgluer y))
          =⟨ ap2 _∙'_
              (To.smgluer-β (pt Y))
              (ap-! to (smgluer y) ∙ ap ! (To.smgluer-β y) ∙ !-! (cfglue (winr y))) ⟩
        ! (cfglue (winr (pt Y))) ∙' cfglue (winr y)
          =⟨ ∙'=∙ (! (cfglue (winr (pt Y)))) (cfglue (winr y)) ⟩
        ! (cfglue (winr (pt Y))) ∙ cfglue (winr y)
          =∎)

Smash-⊙equiv-Cof : ⊙Smash X Y ⊙≃ ⊙Cofiber (∨-⊙to-× {X = X} {Y = Y})
Smash-⊙equiv-Cof = ≃-to-⊙≃ Smash-equiv-Cof (! (cfglue (winl (pt X))))


module Base.Prelude.Integer where

open import Data.Nat                   using (ℕ; zero; suc)
open import Data.Integer               using (+_; -_) renaming (ℤ to ℤᵖ; _+_ to _+ᵖ_; _-_ to _-ᵖ_; _*_ to _*ᵖ_)
open import Data.Integer.Properties    using (_≤?_; _<?_) renaming (_≟_ to _≟ᵖ_)
open import Relation.Nullary.Decidable using (⌊_⌋)

-- Imports for literals.
open import Agda.Builtin.FromNat
open import Agda.Builtin.FromNeg
open import Agda.Builtin.FromNat       public

open import Base.Free                  using (Free; pure; impure; _>>=_)
open import Base.Partial               using (Partial; error)
open import Base.Prelude.Bool          using (𝔹; not)
open import Base.Prelude.Unit          using (⊤ᵖ)

ℤ : (Shape : Set) → (Shape → Set) → Set
ℤ _ _ = ℤᵖ

_+_ : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (ℤ S P)
mx + my = mx >>= λ x → my >>= λ y → pure (x +ᵖ y)

_-_ : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (ℤ S P)
mx - my = mx >>= λ x → my >>= λ y → pure (x -ᵖ y)

_*_ : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (ℤ S P)
mx * my = mx >>= λ x → my >>= λ y → pure (x *ᵖ y)

_≤_ : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (𝔹 S P)
mx ≤ my = mx >>= λ x → my >>= λ y → pure ⌊ x ≤? y ⌋

_<_ : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (𝔹 S P)
mx < my = mx >>= λ x → my >>= λ y → pure ⌊ x <? y ⌋

_>_ : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (𝔹 S P)
mx > my = my ≤ mx

_≥_ : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (𝔹 S P)
mx ≥ my = my < mx

_≟_ : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (𝔹 S P)
mx ≟ my = mx >>= λ x → my >>= λ y → pure ⌊ x ≟ᵖ y ⌋

_≠_ : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (𝔹 S P)
mx ≠ my = not (mx ≟ my)

instance
  number : Number ℤᵖ
  number = record
    { Constraint = λ _ → ⊤ᵖ
    ; fromNat    = λ n → + n
    }

  negative : Negative ℤᵖ
  negative = record
    { Constraint = λ _ → ⊤ᵖ
    ; fromNeg    = λ n → - (+ n)
    }

_^ᵖ_ : ℤᵖ → ℕ → ℤᵖ
b ^ᵖ 0     = 1
b ^ᵖ suc e = b *ᵖ (b ^ᵖ e)

-- If it encounters a negative exponent the Haskell implementation of @(^)@ raises an exception using @errorWithoutStackTrace@.
_^_ : ∀ {S P} → ⦃ Partial S P ⦄ → Free S P (ℤ S P) → Free S P (ℤ S P) → Free S P (ℤ S P)
mb ^ me = me >>= λ where
  (ℤᵖ.negsuc _) → error "Negative exponent"
  (+ 0)         → pure 1
  (+ (suc e))   → mb >>= λ b → pure (b ^ᵖ suc e)

neg : ∀ {S P} → Free S P (ℤ S P) → Free S P (ℤ S P)
neg x = pure -1 * x


------------------------------------------------------------------------------
-- Properties stated in the Burstall's paper
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module FOTC.Program.SortList.PropertiesATP where

open import FOTC.Base
open import FOTC.Base.List
open import FOTC.Data.Bool.PropertiesATP
open import FOTC.Data.Nat.Inequalities
open import FOTC.Data.Nat.Inequalities.PropertiesATP
open import FOTC.Data.Nat.List.Type
open import FOTC.Data.Nat.Type
open import FOTC.Data.List
open import FOTC.Data.List.PropertiesATP
open import FOTC.Program.SortList.Properties.Totality.BoolATP
open import FOTC.Program.SortList.Properties.Totality.ListN-ATP
open import FOTC.Program.SortList.Properties.Totality.OrdList.FlattenATP
open import FOTC.Program.SortList.Properties.Totality.OrdListATP
open import FOTC.Program.SortList.Properties.Totality.OrdTreeATP
open import FOTC.Program.SortList.Properties.Totality.TreeATP
open import FOTC.Program.SortList.SortList

------------------------------------------------------------------------------
-- Burstall's lemma: If t is ordered then totree(i, t) is ordered.
toTree-OrdTree : ∀ {item t} → N item → Tree t → OrdTree t →
                 OrdTree (toTree · item · t)
toTree-OrdTree {item} Nitem tnil OTt = prf
  where postulate prf : OrdTree (toTree · item · nil)
        {-# ATP prove prf #-}

toTree-OrdTree {item} Nitem (ttip {i} Ni) OTt =
  case prf₁ prf₂ (x>y∨x≤y Ni Nitem)
  where
  postulate prf₁ : i > item → OrdTree (toTree · item · tip i)
  {-# ATP prove prf₁ x≤x x<y→x≤y x>y→x≰y #-}

  postulate prf₂ : i ≤ item → OrdTree (toTree · item · tip i)
  {-# ATP prove prf₂ x≤x #-}

toTree-OrdTree {item} Nitem (tnode {t₁} {i} {t₂} Tt₁ Ni Tt₂) OTtnode =
  case (prf₁ (toTree-OrdTree Nitem Tt₁ (leftSubTree-OrdTree Tt₁ Ni Tt₂ OTtnode))
             (rightSubTree-OrdTree Tt₁ Ni Tt₂ OTtnode))
       (prf₂ (toTree-OrdTree Nitem Tt₂ (rightSubTree-OrdTree Tt₁ Ni Tt₂ OTtnode))
             (leftSubTree-OrdTree Tt₁ Ni Tt₂ OTtnode))
       (x>y∨x≤y Ni Nitem)
  where
  postulate prf₁ : ordTree (toTree · item · t₁) ≡ true →
                   OrdTree t₂ →
                   i > item →
                   OrdTree (toTree · item · node t₁ i t₂)
  {-# ATP prove prf₁ &&-list₄-t x>y→x≰y le-ItemTree-Bool le-TreeItem-Bool ordTree-Bool toTree-OrdTree-helper₁ #-}

  postulate prf₂ : ordTree (toTree · item · t₂) ≡ true →
                   OrdTree t₁ →
                   i ≤ item →
                   OrdTree (toTree · item · node t₁ i t₂)
  {-# ATP prove prf₂ &&-list₄-t  le-ItemTree-Bool le-TreeItem-Bool ordTree-Bool toTree-OrdTree-helper₂ #-}

------------------------------------------------------------------------------
-- Burstall's lemma: ord(maketree(is)).

-- makeTree-TreeOrd : ∀ {is} → ListN is → OrdTree (makeTree is)
-- makeTree-TreeOrd LNis =
--   ind-lit OrdTree toTree nil LNis ordTree-nil
--           (λ Nx y TOy → toTree-OrdTree Nx {!!} TOy)

makeTree-OrdTree : ∀ {is} → ListN is → OrdTree (makeTree is)
makeTree-OrdTree lnnil = prf
  where postulate prf : OrdTree (makeTree [])
        {-# ATP prove prf #-}

makeTree-OrdTree (lncons {i} {is} Ni Lis) = prf (makeTree-OrdTree Lis)
  where postulate prf : OrdTree (makeTree is) → OrdTree (makeTree (i ∷ is))
        {-# ATP prove prf makeTree-Tree toTree-OrdTree #-}

------------------------------------------------------------------------------
-- Burstall's lemma: If ord(is1) and ord(is2) and is1 ≤ is2 then
-- ord(concat(is1, is2)).
++-OrdList : ∀ {is js} → ListN is → ListN js → OrdList is → OrdList js →
             ≤-Lists is js → OrdList (is ++ js)

++-OrdList {js = js} lnnil LNjs LOis LOjs is≤js =
  subst OrdList (sym (++-leftIdentity js)) LOjs

++-OrdList {js = js} (lncons {i} {is} Ni LNis) LNjs OLi∷is OLjs i∷is≤js =
  subst OrdList
        (sym (++-∷ i is js))
        (lemma (++-OrdList LNis LNjs
                           (subList-OrdList Ni LNis OLi∷is)
                           OLjs
                           (&&-list₂-t₂ (le-ItemList-Bool Ni LNjs)
                                        (le-Lists-Bool LNis LNjs)
                                        (trans (sym (le-Lists-∷ i is js)) i∷is≤js))))
  where postulate lemma : OrdList (is ++ js) → OrdList (i ∷ is ++ js)
        {-# ATP prove lemma &&-list₂-t ++-OrdList-helper le-ItemList-Bool le-Lists-Bool ordList-Bool #-}

------------------------------------------------------------------------------
-- Burstall's lemma: If t is ordered then (flatten t) is ordered.
flatten-OrdList : ∀ {t} → Tree t → OrdTree t → OrdList (flatten t)
flatten-OrdList tnil OTt =
  subst OrdList (sym flatten-nil) ordList-[]

flatten-OrdList (ttip {i} Ni) OTt = prf
  where postulate prf : OrdList (flatten (tip i))
        {-# ATP prove prf #-}

flatten-OrdList (tnode {t₁} {i} {t₂} Tt₁ Ni Tt₂) OTt
  = prf (++-OrdList (flatten-ListN Tt₁)
                    (flatten-ListN Tt₂)
                    (flatten-OrdList Tt₁ (leftSubTree-OrdTree Tt₁ Ni Tt₂ OTt))
                    (flatten-OrdList Tt₂ (rightSubTree-OrdTree Tt₁ Ni Tt₂ OTt))
                    (flatten-OrdList-helper Tt₁ Ni Tt₂ OTt))
  where postulate prf : OrdList (flatten t₁ ++ flatten t₂) → -- Indirect IH.
                        OrdList (flatten (node t₁ i t₂))
        {-# ATP prove prf #-}


{-# OPTIONS --safe #-}

open import Definition.Typed.EqualityRelation

module Definition.LogicalRelation.Properties.Reflexivity {{eqrel : EqRelSet}} where

open import Definition.Untyped
open import Definition.Typed
open import Definition.LogicalRelation

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

import Data.Fin as Fin
import Data.Nat as Nat

-- Reflexivity of reducible types.
reflEq : ∀ {l Γ A r} ([A] : Γ ⊩⟨ l ⟩ A ^ r) → Γ ⊩⟨ l ⟩ A ≡ A ^ r / [A]
reflEq (Uᵣ′ _ _ _ _ l< PE.refl D) = red D
reflEq (ℕᵣ D) = red D
reflEq (Emptyᵣ D) = red D
reflEq (ne′ K [[ ⊢A , ⊢B , D ]] neK K≡K) =
  ne₌ _ [[ ⊢A , ⊢B , D ]] neK K≡K
reflEq (Πᵣ′ rF lF lG _ _ F G [[ ⊢A , ⊢B , D ]] ⊢F ⊢G A≡A [F] [G] G-ext) =
  Π₌ _ _ D A≡A
     (λ ρ ⊢Δ → reflEq ([F] ρ ⊢Δ))
     (λ ρ ⊢Δ [a] → reflEq ([G] ρ ⊢Δ [a]))
reflEq (∃ᵣ′ F G [[ ⊢A , ⊢B , D ]] ⊢F ⊢G A≡A [F] [G] G-ext) =
  ∃₌ _ _ D A≡A
    (λ ρ ⊢Δ → reflEq ([F] ρ ⊢Δ))
    (λ ρ ⊢Δ [a] → reflEq ([G] ρ ⊢Δ [a]))
reflEq {ι ¹} (emb X [A]) = reflEq [A]
reflEq {∞} (emb X [A]) = reflEq [A]

reflNatural-prop : ∀ {Γ n}
                 → Natural-prop Γ n
                 → [Natural]-prop Γ n n
reflNatural-prop (sucᵣ (ℕₜ n d t≡t prop)) =
  sucᵣ (ℕₜ₌ n n d d t≡t
            (reflNatural-prop prop))
reflNatural-prop zeroᵣ = zeroᵣ
reflNatural-prop (ne (neNfₜ neK ⊢k k≡k)) = ne (neNfₜ₌ neK neK k≡k)

reflEmpty-prop : ∀ {Γ n l}
                 → Empty-prop Γ n l
                 → [Empty]-prop Γ n n l
reflEmpty-prop (ne x) = ne x x

-- Reflexivity of reducible terms.
-- We proceed in a layered way because Agda does not understand our
-- recursions are well founded

reflEqTerm⁰ : ∀ {Γ A t r} ([A] : Γ ⊩⟨ ι ⁰ ⟩ A ^ r)
           → Γ ⊩⟨ ι ⁰ ⟩ t ∷ A ^ r / [A]
           → Γ ⊩⟨ ι ⁰ ⟩ t ≡ t ∷ A ^ r / [A]
reflEqTerm⁰ (ℕᵣ D) (ℕₜ n [[ ⊢t , ⊢u , d ]] t≡t prop) =
  ℕₜ₌ n n [[ ⊢t , ⊢u , d ]] [[ ⊢t , ⊢u , d ]] t≡t
      (reflNatural-prop prop)
reflEqTerm⁰ (Emptyᵣ D) (Emptyₜ (ne x)) = Emptyₜ₌ (ne x x)
reflEqTerm⁰ {r = [ ! , l ]} (ne′ K D neK K≡K) (neₜ k d (neNfₜ neK₁ ⊢k k≡k)) =
  neₜ₌ k k d d (neNfₜ₌ neK₁ neK₁ k≡k)
reflEqTerm⁰ {r = [ % , l ]} (ne′ K D neK K≡K) (neₜ d) = neₜ₌ d d
reflEqTerm⁰ {r = [ ! , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext) (Πₜ f d funcF f≡f [f] [f]₁) =
  Πₜ₌ f f d d funcF funcF f≡f
      (Πₜ f d funcF f≡f [f] [f]₁)
      (Πₜ f d funcF f≡f [f] [f]₁)
      (λ ρ ⊢Δ [a] → [f] ρ ⊢Δ [a] [a] (reflEqTerm⁰ ([F] ρ ⊢Δ) [a]))
reflEqTerm⁰ {r = [ % , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext) X = X , X
reflEqTerm⁰ (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) X = X , X

reflEqTerm¹ : ∀ {Γ A t r} ([A] : Γ ⊩⟨ ι ¹ ⟩ A ^ r)
           → Γ ⊩⟨ ι ¹ ⟩ t ∷ A ^ r / [A]
           → Γ ⊩⟨ ι ¹ ⟩ t ≡ t ∷ A ^ r / [A]
reflEqTerm¹ (Uᵣ (Uᵣ r ⁰ X PE.refl D)) (Uₜ A d typeA A≡A [A]) =
  Uₜ₌ (Uₜ A d typeA A≡A [A]) (Uₜ A d typeA A≡A [A])
    A≡A (λ [ρ] ⊢Δ → reflEq ([A] [ρ] ⊢Δ))
reflEqTerm¹ (Uᵣ (Uᵣ r ¹ () PE.refl D)) (Uₜ A d typeA A≡A [A])
reflEqTerm¹ (ℕᵣ D) (ℕₜ n [[ ⊢t , ⊢u , d ]] t≡t prop) =
  ℕₜ₌ n n [[ ⊢t , ⊢u , d ]] [[ ⊢t , ⊢u , d ]] t≡t
      (reflNatural-prop prop)
reflEqTerm¹ (Emptyᵣ D) (Emptyₜ (ne x)) = Emptyₜ₌ (ne x x)
reflEqTerm¹ {r = [ ! , l ]} (ne′ K D neK K≡K) (neₜ k d (neNfₜ neK₁ ⊢k k≡k)) =
  neₜ₌ k k d d (neNfₜ₌ neK₁ neK₁ k≡k)
reflEqTerm¹ {r = [ % , l ]} (ne′ K D neK K≡K) (neₜ d) = neₜ₌ d d
reflEqTerm¹ {r = [ ! , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext) (Πₜ f d funcF f≡f [f] [f]₁) =
  Πₜ₌ f f d d funcF funcF f≡f
      (Πₜ f d funcF f≡f [f] [f]₁)
      (Πₜ f d funcF f≡f [f] [f]₁)
      (λ ρ ⊢Δ [a] → [f] ρ ⊢Δ [a] [a] (reflEqTerm¹ ([F] ρ ⊢Δ) [a]))
reflEqTerm¹ {r = [ % , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext) X = X , X
reflEqTerm¹ (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) X = X , X
reflEqTerm¹ (emb X [A]) = reflEqTerm⁰ [A]

reflEqTerm∞ : ∀ {Γ A t r} ([A] : Γ ⊩⟨ ∞ ⟩ A ^ r)
           → Γ ⊩⟨ ∞ ⟩ t ∷ A ^ r / [A]
           → Γ ⊩⟨ ∞ ⟩ t ≡ t ∷ A ^ r / [A]
reflEqTerm∞ (Uᵣ (Uᵣ r ⁰ X eq D)) (Uₜ A d typeA A≡A [A]) =
  Uₜ₌ (Uₜ A d typeA A≡A [A]) (Uₜ A d typeA A≡A [A]) A≡A (λ [ρ] ⊢Δ → reflEq ([A] [ρ] ⊢Δ))
reflEqTerm∞ (Uᵣ (Uᵣ r ¹ X eq D)) (Uₜ A d typeA A≡A [A]) =
  Uₜ₌ (Uₜ A d typeA A≡A [A]) (Uₜ A d typeA A≡A [A]) A≡A (λ [ρ] ⊢Δ → reflEq ([A] [ρ] ⊢Δ))
reflEqTerm∞ (ℕᵣ D) (ℕₜ n [[ ⊢t , ⊢u , d ]] t≡t prop) =
  ℕₜ₌ n n [[ ⊢t , ⊢u , d ]] [[ ⊢t , ⊢u , d ]] t≡t
      (reflNatural-prop prop)
reflEqTerm∞ (Emptyᵣ D) (Emptyₜ (ne x)) = Emptyₜ₌ (ne x x)
reflEqTerm∞ {r = [ ! , l ]} (ne′ K D neK K≡K) (neₜ k d (neNfₜ neK₁ ⊢k k≡k)) =
  neₜ₌ k k d d (neNfₜ₌ neK₁ neK₁ k≡k)
reflEqTerm∞ {r = [ % , l ]} (ne′ K D neK K≡K) (neₜ d) = neₜ₌ d d
reflEqTerm∞ {r = [ ! , l ]} (Πᵣ′ rF lF lG _ _ F G D ⊢F ⊢G A≡A [F] [G] G-ext) (Πₜ f d funcF f≡f [f] [f]₁) =
  Πₜ₌ f f d d funcF funcF f≡f
      (Πₜ f d funcF f≡f [f] [f]₁)
      (Πₜ f d funcF f≡f [f] [f]₁)
      (λ ρ ⊢Δ [a] → [f] ρ ⊢Δ [a] [a] (reflEqTerm∞ ([F] ρ ⊢Δ) [a]))
reflEqTerm∞ {r = [ % , l ]} (Πᵣ′ rF lF lG F G _ _ D ⊢F ⊢G A≡A [F] [G] G-ext) X = X , X
reflEqTerm∞ (∃ᵣ′ F G D ⊢F ⊢G A≡A [F] [G] G-ext) X = X , X
reflEqTerm∞ (emb X [A]) = reflEqTerm¹ [A]

reflEqTerm : ∀ {l Γ A t r} ([A] : Γ ⊩⟨ l ⟩ A ^ r)
           → Γ ⊩⟨ l ⟩ t ∷ A ^ r / [A]
           → Γ ⊩⟨ l ⟩ t ≡ t ∷ A ^ r / [A]
reflEqTerm {l = ι ⁰} [A] [t] = reflEqTerm⁰ [A] [t]
reflEqTerm {l = ι ¹} [A] [t] = reflEqTerm¹ [A] [t]
reflEqTerm {l = ∞} [A] [t] = reflEqTerm∞ [A] [t]


------------------------------------------------------------------------
-- The Agda standard library
--
-- Names used in the reflection machinery
------------------------------------------------------------------------

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

module Reflection.Name where

open import Data.List.Base
import Data.Product.Properties as Prodₚ
import Data.Word.Properties as Wₚ
open import Function
open import Relation.Nullary.Decidable using (map′)
open import Relation.Binary
import Relation.Binary.Construct.On as On
open import Relation.Binary.PropositionalEquality

----------------------------------------------------------------------
-- Re-export built-ins

open import Agda.Builtin.Reflection public
  using (Name) renaming (primQNameToWord64s to toWords)

open import Agda.Builtin.Reflection.Properties public
  renaming (primQNameToWord64sInjective to toWords-injective)

----------------------------------------------------------------------
-- More definitions
----------------------------------------------------------------------

Names : Set
Names = List Name

----------------------------------------------------------------------
-- Decidable equality for names
----------------------------------------------------------------------

_≈_ : Rel Name _
_≈_ = _≡_ on toWords

infix 4 _≈?_ _≟_

_≈?_ : Decidable _≈_
_≈?_ = On.decidable toWords _≡_ (Prodₚ.≡-dec Wₚ._≟_ Wₚ._≟_)

_≟_ : DecidableEquality Name
m ≟ n = map′ (toWords-injective _ _) (cong toWords) (m ≈? n)