------------------------------------------------------------------------
-- Bi-invertibility
------------------------------------------------------------------------

-- The development is based on the presentation of bi-invertibility
-- (for types and functions) and related things in the HoTT book.

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

open import Equality
open import Prelude hiding (id; _∘_)

-- The code is parametrised by something like a "raw" category.

module Bi-invertibility
  {e⁺}
  (eq : ∀ {a p} → Equality-with-J a p e⁺)
  {o h}
  (Obj : Type o)
  (Hom : Obj → Obj → Type h)
  (id : {A : Obj} → Hom A A)
  (_∘′_ : {A B C : Obj} → Hom B C → Hom A B → Hom A C)
  where

open Derived-definitions-and-properties eq
open import Equivalence eq as Eq using (_≃_; Is-equivalence)
open import Function-universe eq as F hiding (id; _∘_)
open import Logical-equivalence using (_⇔_)
open import H-level eq
open import H-level.Closure eq
open import Preimage eq
open import Surjection eq using (_↠_)

private
  variable
    A B : Obj
    f   : Hom A B

  infixr 9 _∘_

  _∘_ : {A B C : Obj} → Hom B C → Hom A B → Hom A C
  _∘_ = _∘′_

-- Has-left-inverse f means that f has a left inverse.

Has-left-inverse : Hom A B → Type h
Has-left-inverse f = ∃ λ f⁻¹ → f⁻¹ ∘ f ≡ id

-- Has-right-inverse f means that f has a right inverse.

Has-right-inverse : Hom A B → Type h
Has-right-inverse f = ∃ λ f⁻¹ → f ∘ f⁻¹ ≡ id

-- Is-bi-invertible f means that f has a left inverse and a (possibly
-- distinct) right inverse.

Is-bi-invertible : Hom A B → Type h
Is-bi-invertible f =
  Has-left-inverse f × Has-right-inverse f

-- Has-quasi-inverse f means that f has a left inverse that is also a
-- right inverse.

Has-quasi-inverse : Hom A B → Type h
Has-quasi-inverse f =
  ∃ λ f⁻¹ → f ∘ f⁻¹ ≡ id × f⁻¹ ∘ f ≡ id

-- Some notions of isomorphism or equivalence.

infix 4 _≊_ _≅_

_≊_ : Obj → Obj → Type h
A ≊ B = ∃ λ (f : Hom A B) → Is-bi-invertible f

_≅_ : Obj → Obj → Type h
A ≅ B = ∃ λ (f : Hom A B) → Has-quasi-inverse f

-- Morphisms with quasi-inverses are bi-invertible.

Has-quasi-inverse→Is-bi-invertible :
  (f : Hom A B) → Has-quasi-inverse f → Is-bi-invertible f
Has-quasi-inverse→Is-bi-invertible _ (f⁻¹ , f∘f⁻¹≡id , f⁻¹∘f≡id) =
    (f⁻¹ , f⁻¹∘f≡id)
  , (f⁻¹ , f∘f⁻¹≡id)

≅→≊ : A ≅ B → A ≊ B
≅→≊ = ∃-cong Has-quasi-inverse→Is-bi-invertible

-- The remaining code relies on some further assumptions, similar to
-- those of a precategory. However, note that Hom A B is not required
-- to be a set (some properties require Hom A A to be a set for some
-- A).

module More
  (left-identity  : {A B : Obj} (f : Hom A B) → id ∘ f ≡ f)
  (right-identity : {A B : Obj} (f : Hom A B) → f ∘ id ≡ f)
  (associativity  : {A B C D : Obj}
                    (f : Hom C D) (g : Hom B C) (h : Hom A B) →
                    f ∘ (g ∘ h) ≡ (f ∘ g) ∘ h)
  where

  -- Bi-invertible morphisms have quasi-inverses.

  Is-bi-invertible→Has-quasi-inverse :
    Is-bi-invertible f → Has-quasi-inverse f
  Is-bi-invertible→Has-quasi-inverse
    {f = f} ((f⁻¹₁ , f⁻¹₁∘f≡id) , (f⁻¹₂ , f∘f⁻¹₂≡id)) =
      (f⁻¹₁ ∘ f ∘ f⁻¹₂)
    , (f ∘ f⁻¹₁ ∘ f ∘ f⁻¹₂    ≡⟨ cong (f ∘_) $ associativity _ _ _ ⟩
       f ∘ (f⁻¹₁ ∘ f) ∘ f⁻¹₂  ≡⟨ cong (λ f′ → f ∘ f′ ∘ f⁻¹₂) f⁻¹₁∘f≡id ⟩
       f ∘ id ∘ f⁻¹₂          ≡⟨ cong (f ∘_) $ left-identity _ ⟩
       f ∘ f⁻¹₂               ≡⟨ f∘f⁻¹₂≡id ⟩∎
       id                     ∎)
    , ((f⁻¹₁ ∘ f ∘ f⁻¹₂) ∘ f  ≡⟨ cong (λ f′ → (f⁻¹₁ ∘ f′) ∘ f) f∘f⁻¹₂≡id ⟩
       (f⁻¹₁ ∘ id) ∘ f        ≡⟨ cong (_∘ f) $ right-identity _ ⟩
       f⁻¹₁ ∘ f               ≡⟨ f⁻¹₁∘f≡id ⟩∎
       id                     ∎)

  -- Has-left-inverse f is contractible if f has a quasi-inverse.

  Has-left-inverse-contractible :
    Has-quasi-inverse f → Contractible (Has-left-inverse f)
  Has-left-inverse-contractible
    {f = f} (f⁻¹ , f∘f⁻¹≡id , f⁻¹∘f≡id) =
    bijection⁻¹-contractible (record
      { surjection = record
        { logical-equivalence = record
          { to   = _∘ f
          ; from = _∘ f⁻¹
          }
        ; right-inverse-of = λ g →
            (g ∘ f⁻¹) ∘ f  ≡⟨ sym $ associativity _ _ _ ⟩
            g ∘ f⁻¹ ∘ f    ≡⟨ cong (g ∘_) f⁻¹∘f≡id ⟩
            g ∘ id         ≡⟨ right-identity _ ⟩∎
            g              ∎
        }
      ; left-inverse-of = λ g →
          (g ∘ f) ∘ f⁻¹  ≡⟨ sym $ associativity _ _ _ ⟩
          g ∘ f ∘ f⁻¹    ≡⟨ cong (g ∘_) f∘f⁻¹≡id ⟩
          g ∘ id         ≡⟨ right-identity _ ⟩∎
          g              ∎
      })
      id

  -- Has-right-inverse f is contractible if f has a quasi-inverse.

  Has-right-inverse-contractible :
    Has-quasi-inverse f → Contractible (Has-right-inverse f)
  Has-right-inverse-contractible
    {f = f} (f⁻¹ , f∘f⁻¹≡id , f⁻¹∘f≡id) =
    bijection⁻¹-contractible (record
      { surjection = record
        { logical-equivalence = record
          { to   = f ∘_
          ; from = f⁻¹ ∘_
          }
        ; right-inverse-of = λ g →
            f ∘ f⁻¹ ∘ g    ≡⟨ associativity _ _ _ ⟩
            (f ∘ f⁻¹) ∘ g  ≡⟨ cong (_∘ g) f∘f⁻¹≡id ⟩
            id ∘ g         ≡⟨ left-identity _ ⟩∎
            g              ∎
        }
      ; left-inverse-of = λ g →
          f⁻¹ ∘ f ∘ g    ≡⟨ associativity _ _ _ ⟩
          (f⁻¹ ∘ f) ∘ g  ≡⟨ cong (_∘ g) f⁻¹∘f≡id ⟩
          id ∘ g         ≡⟨ left-identity _ ⟩∎
          g              ∎
      })
      id

  -- Is-bi-invertible f is a proposition.

  Is-bi-invertible-propositional :
    (f : Hom A B) → Is-proposition (Is-bi-invertible f)
  Is-bi-invertible-propositional f =
    [inhabited⇒+]⇒+ 0 λ b →
      let q = Is-bi-invertible→Has-quasi-inverse b in
      mono₁ 0 $
      ×-closure 0
        (Has-left-inverse-contractible  q)
        (Has-right-inverse-contractible q)

  -- If Hom A A is a set, where A is the domain of f, then
  -- Has-quasi-inverse f is a proposition.

  Has-quasi-inverse-propositional-domain :
    {f : Hom A B} →
    Is-set (Hom A A) →
    Is-proposition (Has-quasi-inverse f)
  Has-quasi-inverse-propositional-domain {f = f} s =              $⟨ (λ inv → Σ-closure 1
                                                                                (mono₁ 0 $ Has-right-inverse-contractible inv)
                                                                                (λ _ → s)) ⟩
    (Has-quasi-inverse f →
     Is-proposition (∃ λ ((f⁻¹ , _) : Has-right-inverse f) →
                       f⁻¹ ∘ f ≡ id))                             ↝⟨ (∀-cong _ λ _ → H-level-cong _ 1 (inverse Σ-assoc)) ⟩

    (Has-quasi-inverse f → Is-proposition (Has-quasi-inverse f))  ↝⟨ [inhabited⇒+]⇒+ 0 ⟩□

    Is-proposition (Has-quasi-inverse f)                          □

  -- If Hom B B is a set, where B is the codomain of f, then
  -- Has-quasi-inverse f is a proposition.

  Has-quasi-inverse-propositional-codomain :
    {f : Hom A B} →
    Is-set (Hom B B) →
    Is-proposition (Has-quasi-inverse f)
  Has-quasi-inverse-propositional-codomain {f = f} s =            $⟨ (λ inv → Σ-closure 1
                                                                                (mono₁ 0 $ Has-left-inverse-contractible inv)
                                                                                (λ _ → s)) ⟩
    (Has-quasi-inverse f →
     Is-proposition (∃ λ ((f⁻¹ , _) : Has-left-inverse f) →
                       f ∘ f⁻¹ ≡ id))                             ↝⟨ (∀-cong _ λ _ → H-level-cong _ 1 lemma) ⟩

    (Has-quasi-inverse f → Is-proposition (Has-quasi-inverse f))  ↝⟨ [inhabited⇒+]⇒+ 0 ⟩□

    Is-proposition (Has-quasi-inverse f)                          □
    where
    lemma =
      (∃ λ ((f⁻¹ , _) : Has-left-inverse f) → f ∘ f⁻¹ ≡ id)      ↔⟨⟩
      (∃ λ ((f⁻¹ , _) : ∃ λ f⁻¹ → f⁻¹ ∘ f ≡ id) → f ∘ f⁻¹ ≡ id)  ↝⟨ inverse Σ-assoc ⟩
      (∃ λ f⁻¹ → f⁻¹ ∘ f ≡ id × f ∘ f⁻¹ ≡ id)                    ↝⟨ (∃-cong λ _ → ×-comm) ⟩
      (∃ λ f⁻¹ → f ∘ f⁻¹ ≡ id × f⁻¹ ∘ f ≡ id)                    ↔⟨⟩
      Has-quasi-inverse f                                        □

  -- There is a split surjection from Has-quasi-inverse f to
  -- Is-bi-invertible f.

  Has-quasi-inverse↠Is-bi-invertible :
    Has-quasi-inverse f ↠ Is-bi-invertible f
  Has-quasi-inverse↠Is-bi-invertible = record
    { logical-equivalence = record
      { to   = Has-quasi-inverse→Is-bi-invertible _
      ; from = Is-bi-invertible→Has-quasi-inverse
      }
    ; right-inverse-of = λ _ → Is-bi-invertible-propositional _ _ _
    }

  -- There is a split surjection from A ≅ B to A ≊ B.

  ≅↠≊ : (A ≅ B) ↠ (A ≊ B)
  ≅↠≊ = ∃-cong λ _ → Has-quasi-inverse↠Is-bi-invertible

  -- Is-bi-invertible and Has-quasi-inverse are equivalent for
  -- morphisms with domain A for which Hom A A is a set.

  Is-bi-invertible≃Has-quasi-inverse-domain :
    {f : Hom A B} →
    Is-set (Hom A A) →
    Is-bi-invertible f ≃ Has-quasi-inverse f
  Is-bi-invertible≃Has-quasi-inverse-domain s =
    inverse $ Eq.↔⇒≃ (record
      { surjection = Has-quasi-inverse↠Is-bi-invertible
      ; left-inverse-of = λ _ →
          Has-quasi-inverse-propositional-domain s _ _
      })

  -- Is-bi-invertible and Has-quasi-inverse are equivalent for
  -- morphisms with codomain B for which Hom B B is a set.

  Is-bi-invertible≃Has-quasi-inverse-codomain :
    {f : Hom A B} →
    Is-set (Hom B B) →
    Is-bi-invertible f ≃ Has-quasi-inverse f
  Is-bi-invertible≃Has-quasi-inverse-codomain s =
    inverse $ Eq.↔⇒≃ (record
      { surjection = Has-quasi-inverse↠Is-bi-invertible
      ; left-inverse-of = λ _ →
          Has-quasi-inverse-propositional-codomain s _ _
      })

  -- A ≊ B and A ≅ B are equivalent if Hom A A is a set.

  ≊≃≅-domain :
    Is-set (Hom A A) →
    (A ≊ B) ≃ (A ≅ B)
  ≊≃≅-domain s =
    ∃-cong λ _ → Is-bi-invertible≃Has-quasi-inverse-domain s

  -- A ≊ B and A ≅ B are equivalent if Hom B B is a set.

  ≊≃≅-codomain :
    Is-set (Hom B B) →
    (A ≊ B) ≃ (A ≅ B)
  ≊≃≅-codomain s =
    ∃-cong λ _ → Is-bi-invertible≃Has-quasi-inverse-codomain s

  -- An equality characterisation lemma for _≊_.

  equality-characterisation-≊ :
    (f g : A ≊ B) → (f ≡ g) ≃ (proj₁ f ≡ proj₁ g)
  equality-characterisation-≊ _ _ =
    Eq.↔⇒≃ $ inverse $ ignore-propositional-component $
      Is-bi-invertible-propositional _

  -- Two equality characterisation lemmas for _≅_.

  equality-characterisation-≅-domain :
    Is-set (Hom A A) →
    (f g : A ≅ B) → (f ≡ g) ≃ (proj₁ f ≡ proj₁ g)
  equality-characterisation-≅-domain s _ _ =
    Eq.↔⇒≃ $ inverse $ ignore-propositional-component $
      Has-quasi-inverse-propositional-domain s

  equality-characterisation-≅-codomain :
    Is-set (Hom B B) →
    (f g : A ≅ B) → (f ≡ g) ≃ (proj₁ f ≡ proj₁ g)
  equality-characterisation-≅-codomain s _ _ =
    Eq.↔⇒≃ $ inverse $ ignore-propositional-component $
      Has-quasi-inverse-propositional-codomain s

  -- If f : Hom A B has a quasi-inverse, then Has-quasi-inverse f is
  -- equivalent to id ≡ id, where id stands for either the identity at
  -- A or at B.

  Has-quasi-inverse≃id≡id-domain :
    {f : Hom A B} →
    Has-quasi-inverse f →
    Has-quasi-inverse f ≃ (id ≡ id {A = A})
  Has-quasi-inverse≃id≡id-domain {f = f} q-inv@(f⁻¹ , _ , f⁻¹∘f≡id) =
    Has-quasi-inverse f                                     ↔⟨ Σ-assoc ⟩
    (∃ λ ((f⁻¹ , _) : Has-right-inverse f) → f⁻¹ ∘ f ≡ id)  ↔⟨ drop-⊤-left-Σ (_⇔_.to contractible⇔↔⊤ $ Has-right-inverse-contractible q-inv) ⟩
    (f⁻¹ ∘ id) ∘ f ≡ id                                     ↝⟨ ≡⇒↝ _ $ cong (λ f′ → f′ ∘ _ ≡ _) $ right-identity _ ⟩
    f⁻¹ ∘ f ≡ id                                            ↝⟨ ≡⇒↝ _ $ cong (_≡ _) f⁻¹∘f≡id ⟩□
    id ≡ id                                                 □

  Has-quasi-inverse≃id≡id-codomain :
    {f : Hom A B} →
    Has-quasi-inverse f →
    Has-quasi-inverse f ≃ (id ≡ id {A = B})
  Has-quasi-inverse≃id≡id-codomain {f = f} q-inv@(f⁻¹ , f∘f⁻¹≡id , _) =
    Has-quasi-inverse f                                    ↔⟨ Σ-assoc F.∘ (∃-cong λ _ → ×-comm) ⟩
    (∃ λ ((f⁻¹ , _) : Has-left-inverse f) → f ∘ f⁻¹ ≡ id)  ↔⟨ drop-⊤-left-Σ (_⇔_.to contractible⇔↔⊤ $ Has-left-inverse-contractible q-inv) ⟩
    f ∘ id ∘ f⁻¹ ≡ id                                      ↝⟨ ≡⇒↝ _ $ cong (λ f′ → _ ∘ f′ ≡ _) $ left-identity _ ⟩
    f ∘ f⁻¹ ≡ id                                           ↝⟨ ≡⇒↝ _ $ cong (_≡ _) f∘f⁻¹≡id ⟩□
    id ≡ id                                                □

  ----------------------------------------------------------------------
  -- Univalence

  -- The relation _≅_ is reflexive.

  id-≅ : A ≅ A
  id-≅ = id , id , left-identity id , right-identity id

  -- Equal objects are related by _≅_.

  ≡→≅ : A ≡ B → A ≅ B
  ≡→≅ = elim (λ {A B} _ → A ≅ B) (λ _ → id-≅)

  -- A "computation" rule for ≡→≅.

  ≡→≅-refl : ≡→≅ (refl A) ≡ id-≅
  ≡→≅-refl = elim-refl _ _

  -- A notion of univalence, defined using _≅_.

  Univalence-≅ : Type (o ⊔ h)
  Univalence-≅ = {A B : Obj} → Is-equivalence (≡→≅ {A = A} {B = B})

  -- If equality is equivalent to _≅_, then univalence holds.

  ≡≃≅→Univalence-≅ :
    (∀ {A B} → (A ≡ B) ≃ (A ≅ B)) →
    Univalence-≅
  ≡≃≅→Univalence-≅ ≡≃≅ =
    Eq.≡≃→≡→→Is-equivalence (_ ≅_) ≡≃≅ ≡→≅

  -- The relation _≊_ is reflexive.

  id-≊ : A ≊ A
  id-≊ = id , (id , left-identity id) , (id , right-identity id)

  -- Equal objects are related by _≊_.

  ≡→≊ : A ≡ B → A ≊ B
  ≡→≊ = elim (λ {A B} _ → A ≊ B) (λ _ → id-≊)

  -- A "computation" rule for ≡→≊.

  ≡→≊-refl : ≡→≊ (refl A) ≡ id-≊
  ≡→≊-refl = elim-refl _ _

  -- A notion of univalence, defined using _≊_.

  Univalence-≊ : Type (o ⊔ h)
  Univalence-≊ = {A B : Obj} → Is-equivalence (≡→≊ {A = A} {B = B})

  -- If equality is equivalent to _≊_, then univalence holds.

  ≡≃≊→Univalence-≊ :
    (∀ {A B} → (A ≡ B) ≃ (A ≊ B)) →
    Univalence-≊
  ≡≃≊→Univalence-≊ ≡≃≊ =
    Eq.≡≃→≡→→Is-equivalence (_ ≊_) ≡≃≊ ≡→≊


module SafetyProof where

open import Algebra
import Algebra.Properties.CommutativeSemigroup as CommutativeSemigroupProperties
open import Data.List
open import Data.List.Relation.Unary.All using (All; []; _∷_)
open import Data.Fin.Patterns using (0F; 1F)
open import Data.Nat using (z≤n; s≤s)
open import Data.Integer using (+≤+; +<+; +_)
open import Data.Vec using (_∷_)
import Data.Vec.Functional as Vector
open import Data.Product using (_×_; _,_; uncurry)
open import Data.Sum
open import Level using (0ℓ)
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary using (yes; no)

open import Vehicle.Data.Tensor
open import AbstractRationals
import WindControllerSpec as Vehicle

open ≤-Reasoning

------------------------------------------------------------------------
-- Setup

toTensor : ℚ → ℚ → Tensor ℚ (2 ∷ [])
toTensor x y = Vector.fromList (x ∷ y ∷ [])

roadWidth : ℚ
roadWidth = + 3 / 1

maxWindShift : ℚ
maxWindShift = 1ℚ

maxSensorError : ℚ
maxSensorError = + 1 / 4

roadWidth≥0 : roadWidth ≥ 0ℚ
roadWidth≥0 = *≤* (+≤+ z≤n)

maxWindShift≥0 : maxWindShift ≥ 0ℚ
maxWindShift≥0 = *≤* (+≤+ z≤n)

maxSensorError≥0 : maxSensorError ≥ 0ℚ
maxSensorError≥0 = *≤* (+≤+ z≤n)

------------------------------------------------------------------------
-- Model data

record State : Set where
  constructor state
  field
    windSpeed : ℚ
    position  : ℚ
    velocity  : ℚ
    sensor    : ℚ

open State

record Observation : Set where
  constructor observe
  field
    windShift   : ℚ
    sensorError : ℚ

open Observation

------------------------------------------------------------------------
-- Model transitions

initialState : State
initialState = record
  { windSpeed = 0ℚ
  ; position  = 0ℚ
  ; velocity  = 0ℚ
  ; sensor    = 0ℚ
  }

controller : ℚ → ℚ → ℚ
controller x y = Vehicle.controller (toTensor x y)

nextState : Observation → State → State
nextState o s = record
  { windSpeed = newWindSpeed
  ; position  = newPosition
  ; velocity  = newVelocity
  ; sensor    = newSensor
  }
  where
  newWindSpeed = windSpeed s + windShift o
  newPosition  = position s + velocity s + newWindSpeed
  newSensor    = newPosition + sensorError o
  newVelocity  = velocity s + controller newSensor (sensor s)

finalState : List Observation → State
finalState xs = foldr nextState initialState xs

------------------------------------------------------------------------
-- Definition of correctness

nextPosition-windShift : State → ℚ
nextPosition-windShift s = position s + velocity s + windSpeed s

-- The vehicle is on the road if its position is less than the
-- width of the road.
OnRoad : State → Set
OnRoad s = ∣ position s ∣ ≤ roadWidth

-- The vehicle is in a "safe" state if it's more than the maxWindShift away
-- from the edge of the road.
SafeDistanceFromEdge : State → Set
SafeDistanceFromEdge s = ∣ nextPosition-windShift s ∣ < roadWidth - maxWindShift

-- The vehicle's previous sensor reading is accurate if it is no more than the
-- maximum error away from it's true location.
AccurateSensorReading : State → Set
AccurateSensorReading s = ∣ position s - sensor s ∣ ≤ maxSensorError

-- The vehicle's previous sensor reading was not off the road
SensorReadingNotOffRoad : State → Set
SensorReadingNotOffRoad s = ∣ sensor s ∣ ≤ roadWidth + maxSensorError

-- A state is safe if it both a safe distance from the edge and it's sensor
-- reading is accurate.
SafeState : State → Set
SafeState s = SafeDistanceFromEdge s
            × AccurateSensorReading s
            × SensorReadingNotOffRoad s

-- An observation is valid if the observed sensor error and the wind shift
-- are less than the expected maximum shifts.
ValidObservation : Observation → Set
ValidObservation o = ∣ sensorError o ∣ ≤ maxSensorError
                   × ∣ windShift   o ∣ ≤ maxWindShift

------------------------------------------------------------------------
-- Proof of correctness

-- Initial state is both a safe distance from the edge and on the road

initialState-onRoad : OnRoad initialState
initialState-onRoad = roadWidth≥0

initialState-safe : SafeState initialState
initialState-safe rewrite +-eq | *-eq | neg-eq =
  *<* (+<+ (s≤s z≤n)) ,
  *≤* (+≤+ z≤n) ,
  *≤* (+≤+ z≤n)

-- Transitions are well-behaved

controller-lem : ∀ x y →
                 ∣ x ∣ ≤ roadWidth + maxSensorError →
                 ∣ y ∣ ≤ roadWidth + maxSensorError →
                 ∣ controller x y + 2ℚ * x - y ∣ < roadWidth - maxWindShift - 3ℚ * maxSensorError
controller-lem x y ∣x∣≤rw+εₘₐₓ ∣y∣≤rw+εₘₐₓ rewrite +-eq | *-eq | neg-eq =
  uncurry -p<q<p⇒∣q∣<p (Vehicle.safe (toTensor x y) (λ
    { 0F → ∣p∣≤q⇒-q≤p≤q x ∣x∣≤rw+εₘₐₓ
    ; 1F → ∣p∣≤q⇒-q≤p≤q y ∣y∣≤rw+εₘₐₓ
    }))

valid⇒nextState-accurateSensor : ∀ o → ValidObservation o → ∀ s →
                                 AccurateSensorReading (nextState o s)
valid⇒nextState-accurateSensor o (ε≤εₘₐₓ , _) s = let s' = nextState o s in begin
  ∣ position s' - sensor s' ∣                     ≡⟨⟩
  ∣ position s' - (position s' + sensorError o) ∣ ≡⟨ cong ∣_∣ (p-[p+q]≡q (position s') (sensorError o)) ⟩
  ∣ sensorError o ∣                               ≤⟨ ε≤εₘₐₓ ⟩
  maxSensorError                                  ∎

valid+safe⇒nextState-onRoad : ∀ o → ValidObservation o →
                              ∀ s → SafeState s →
                              OnRoad (nextState o s)
valid+safe⇒nextState-onRoad o (_ , δw≤δwₘₐₓ) s (safeDist , _ , _) = begin
  ∣ position s + velocity s + (windSpeed s + windShift o) ∣ ≡˘⟨ cong ∣_∣ (+-assoc (position s + velocity s) (windSpeed s) (windShift o)) ⟩
  ∣ position s + velocity s + windSpeed s + windShift o   ∣ ≤⟨ ∣p+q∣≤∣p∣+∣q∣ _ (windShift o) ⟩
  ∣ nextPosition-windShift s ∣ + ∣ windShift o ∣            ≤⟨ +-mono-≤ (<⇒≤ safeDist) δw≤δwₘₐₓ ⟩
  roadWidth - maxWindShift + maxWindShift                   ≡⟨ p-q+q≡p roadWidth maxWindShift ⟩
  roadWidth                                                 ∎

valid+safe⇒nextState-sensorReadingNotOffRoad : ∀ s → SafeState s → ∀ o → ValidObservation o →
                                               SensorReadingNotOffRoad (nextState o s)
valid+safe⇒nextState-sensorReadingNotOffRoad s safe o valid@(ε≤εₘₐₓ , _) = begin
  ∣ position (nextState o s) + sensorError o ∣       ≤⟨ ∣p+q∣≤∣p∣+∣q∣ (position s + velocity s + (windSpeed s + windShift o)) (sensorError o) ⟩
  ∣ position (nextState o s) ∣ + ∣ sensorError o ∣   ≤⟨ +-mono-≤ (valid+safe⇒nextState-onRoad o valid s safe) ε≤εₘₐₓ ⟩
  roadWidth + maxSensorError                         ∎

valid+safe⇒nextState-safeDistanceFromEdge : ∀ o → ValidObservation o →
                                            ∀ s → SafeState s →
                                            SafeDistanceFromEdge (nextState o s)
valid+safe⇒nextState-safeDistanceFromEdge o valid@(ε-accurate , _) s safe@(safeDist , ε'-accurate , s-notOffRoad) = let
    s' = nextState o s
    y  = position  s; y' = position  s'
    v  = velocity  s; v' = velocity  s'
    w  = windSpeed s; w' = windSpeed s'
    p  = sensor    s; p' = sensor    s'
    ε  = y - p;       ε' = sensorError o
    dw = windShift   o
    dv = controller p' p
    ∣dv+2p'-p∣ = ∣ dv + 2ℚ * p' - p ∣
    s'-notOffRoad = valid+safe⇒nextState-sensorReadingNotOffRoad s safe o valid
  in begin-strict
  ∣ y' + v' + w' ∣                                                  ≡⟨ cong ∣_∣ (sym (p+q-q≡p (y' + v' + w') _)) ⟩
  ∣ y' + (v + dv) + w' + (y + ε' + ε' - p) - (y + ε' + ε' - p) ∣    ≡⟨ cong ∣_∣ (lem1 y' v dv w' y ε' p) ⟩
  ∣ dv + (y' + ε' + (y + v + w' + ε')) - p - (ε' + ε' + (y - p)) ∣  ≡⟨⟩
  ∣ dv + (p' + p') - p - (ε' + ε' + (y - p)) ∣                      ≡⟨ cong ∣_∣ (cong₂ (λ a b → dv + a - p - (b + (y - p))) (sym (2*p≡p+p p')) (sym (2*p≡p+p ε'))) ⟩
  ∣ dv + 2ℚ * p' - p - (2ℚ * ε' + (y - p)) ∣                        ≤⟨ ∣p-q∣≤∣p∣+∣q∣ (dv + 2ℚ * p' - p) (2ℚ * ε' + (y - p)) ⟩
  ∣ dv + 2ℚ * p' - p ∣ + ∣ 2ℚ * ε' + (y - p) ∣                      ≤⟨ +-monoʳ-≤ _ (∣p+q∣≤∣p∣+∣q∣ (2ℚ * ε') (y - p)) ⟩
  ∣ dv + 2ℚ * p' - p ∣ + (∣ 2ℚ * ε' ∣ + ∣ y - p ∣)                  ≡⟨ cong (λ v → ∣dv+2p'-p∣ + v) (cong (_+ _) (∣p*q∣≡∣p∣*∣q∣ 2ℚ ε')) ⟩
  ∣ dv + 2ℚ * p' - p ∣ + (2ℚ * ∣ ε' ∣ + ∣ ε ∣)                      ≤⟨ +-monoʳ-≤ _ (+-mono-≤ (*-monoʳ-≤ 2ℚ _ ε-accurate) ε'-accurate) ⟩
  ∣ dv + 2ℚ * p' - p ∣ + (2ℚ * maxSensorError + maxSensorError)     ≡⟨ cong (λ v → ∣dv+2p'-p∣ + v) (2p+p≡3p maxSensorError) ⟩
  ∣ dv + 2ℚ * p' - p ∣ + 3ℚ * maxSensorError                        <⟨ p<r-q⇒p+q<r _ _ _ (controller-lem p' p s'-notOffRoad s-notOffRoad) ⟩
  roadWidth - maxWindShift                                          ∎

safe⇒nextState-safe : ∀ s → SafeState s → ∀ o → ValidObservation o → SafeState (nextState o s)
safe⇒nextState-safe s safe o valid =
  valid+safe⇒nextState-safeDistanceFromEdge o valid s safe ,
  valid⇒nextState-accurateSensor o valid s ,
  valid+safe⇒nextState-sensorReadingNotOffRoad s safe o valid

finalState-safe : ∀ xs → All ValidObservation xs → SafeState (finalState xs)
finalState-safe []       []         = initialState-safe
finalState-safe (x ∷ xs) (px ∷ pxs) = safe⇒nextState-safe (finalState xs) (finalState-safe xs pxs) x px

finalState-onRoad : ∀ xs → All ValidObservation xs → OnRoad (finalState xs)
finalState-onRoad []       []         = initialState-onRoad
finalState-onRoad (x ∷ xs) (px ∷ pxs) = valid+safe⇒nextState-onRoad x px (finalState xs) (finalState-safe xs pxs)


open import Nat
open import Prelude
open import core
open import contexts

module typ-dec where
  lemma-arr-l : ∀{t1 t2 t4} → t1 ==> t2 == t1 ==> t4 → t2 == t4
  lemma-arr-l refl = refl

  lemma-arr-r : ∀{t1 t2 t3} → t1 ==> t2 == t3 ==> t2 → t1 == t3
  lemma-arr-r refl = refl

  lemma-arr-b : ∀{t1 t2 t3 t4} → t1 ==> t2 == t3 ==> t4 → t1 == t3
  lemma-arr-b refl = refl

  lemma-prod-l : ∀{t1 t2 t4} → t1 ⊗ t2 == t1 ⊗ t4 → t2 == t4
  lemma-prod-l refl = refl

  lemma-prod-r : ∀{t1 t2 t3} → t1 ⊗ t2 == t3 ⊗ t2 → t1 == t3
  lemma-prod-r refl = refl

  lemma-prod-b : ∀{t1 t2 t3 t4} → t1 ⊗ t2 == t3 ⊗ t4 → t1 == t3
  lemma-prod-b refl = refl

  -- types are decidable
  typ-dec : dec typ
  typ-dec b b = Inl refl
  typ-dec b ⦇·⦈ = Inr (λ ())
  typ-dec b (t2 ==> t3) = Inr (λ ())
  typ-dec ⦇·⦈ b = Inr (λ ())
  typ-dec ⦇·⦈ ⦇·⦈ = Inl refl
  typ-dec ⦇·⦈ (t2 ==> t3) = Inr (λ ())
  typ-dec (t1 ==> t2) b = Inr (λ ())
  typ-dec (t1 ==> t2) ⦇·⦈ = Inr (λ ())
  typ-dec (t1 ==> t2) (t3 ==> t4) with typ-dec t1 t3 | typ-dec t2 t4
  typ-dec (t1 ==> t2) (.t1 ==> .t2) | Inl refl | Inl refl = Inl refl
  typ-dec (t1 ==> t2) (.t1 ==> t4)  | Inl refl | Inr x₁   = Inr (λ x → x₁ (lemma-arr-l x))
  typ-dec (t1 ==> t2) (t3 ==> .t2)  | Inr x    | Inl refl = Inr (λ x₁ → x (lemma-arr-r x₁))
  typ-dec (t1 ==> t2) (t3 ==> t4)   | Inr x    | Inr x₁   = Inr (λ x₂ → x (lemma-arr-b x₂))
  typ-dec b (t2 ⊗ t3) = Inr (λ ())
  typ-dec ⦇·⦈ (t2 ⊗ t3) = Inr (λ ())
  typ-dec (t1 ==> t2) (t3 ⊗ t4) = Inr (λ ())
  typ-dec (t1 ⊗ t2) b = Inr (λ ())
  typ-dec (t1 ⊗ t2) ⦇·⦈ = Inr (λ ())
  typ-dec (t1 ⊗ t2) (t3 ==> t4) = Inr (λ ())
  typ-dec (t1 ⊗ t2) (t3 ⊗ t4) with typ-dec t1 t3 | typ-dec t2 t4
  typ-dec (t1 ⊗ t2) (.t1 ⊗ .t2) | Inl refl | Inl refl = Inl refl
  typ-dec (t1 ⊗ t2) (.t1 ⊗ t4)  | Inl refl | Inr x₁   = Inr (λ x → x₁ (lemma-prod-l x))
  typ-dec (t1 ⊗ t2) (t3 ⊗ .t2)  | Inr x    | Inl refl = Inr (λ x' → x (lemma-prod-r x'))
  typ-dec (t1 ⊗ t2) (t3 ⊗ t4)   | Inr x    | Inr x₁   = Inr (λ x' → x (lemma-prod-b x'))

  -- if an arrow is disequal, it disagrees in the first or second argument
  ne-factor : ∀{τ1 τ2 τ3 τ4} → (τ1 ==> τ2) ≠ (τ3 ==> τ4) → (τ1 ≠ τ3) + (τ2 ≠ τ4)
  ne-factor {τ1} {τ2} {τ3} {τ4} ne with typ-dec τ1 τ3 | typ-dec τ2 τ4
  ne-factor ne | Inl refl | Inl refl = Inl (λ x → ne refl)
  ne-factor ne | Inl x | Inr x₁ = Inr x₁
  ne-factor ne | Inr x | Inl x₁ = Inl x
  ne-factor ne | Inr x | Inr x₁ = Inl x


module Functor where

open import Category as Cat

record Functor (ℂ ⅅ : Cat) : Set1 where
  field
    F          : Cat.Obj ℂ -> Cat.Obj ⅅ
    map        : {A B : Cat.Obj ℂ} -> Cat._─→_ ℂ A B -> Cat._─→_ ⅅ (F A) (F B)
    mapEq      : {A B : Cat.Obj ℂ}{f g : Cat._─→_ ℂ A B} -> Category._==_ ℂ f g ->
                 Category._==_ ⅅ (map f) (map g)
    mapId      : {A : Cat.Obj ℂ} -> Category._==_ ⅅ (map (Cat.id ℂ {A})) (Cat.id ⅅ)
    mapCompose : {A B C : Cat.Obj ℂ}{f : Cat._─→_ ℂ B C}{g : Cat._─→_ ℂ A B} ->
                 Category._==_ ⅅ (map (Cat._∘_ ℂ f g)) (Cat._∘_ ⅅ (map f) (map g))


module x06-747Connectives-hc where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl)
open Eq.≡-Reasoning
open import Data.Nat using (ℕ)
open import Function using (_∘_)

------------------------------------------------------------------------------
-- BEGIN : Copied from 747Isomorphism.

postulate
  extensionality : ∀ {A B : Set} {f g : A → B}
    → (∀ (x : A) → f x ≡ g x)
      -----------------------
    → f ≡ g

infix 0 _≃_
record _≃_ (A B : Set) : Set where
  constructor mk-≃  -- This has been added, not in PLFA
  field
    to   : A → B
    from : B → A
    from∘to : ∀ (x : A) → from (to x) ≡ x
    to∘from : ∀ (y : B) → to (from y) ≡ y
open _≃_

infix 0 _≲_
record _≲_ (A B : Set) : Set where
  field
    to      : A → B
    from    : B → A
    from∘to : ∀ (x : A) → from (to x) ≡ x
open _≲_

record _⇔_ (A B : Set) : Set where
  field
    to   : A → B
    from : B → A
open _⇔_

-- You may copy over the various reasoning modules if you wish.

-- END : Copied from 747Isomorphism.
------------------------------------------------------------------------------

------------------------------------------------------------------------------
-- logical AND is Cartesian product.

data _×_ (A : Set) (B : Set) : Set where

  ⟨_,_⟩ :
      A
    → B
      -----
    → A × B

-- destructors (aka eliminators) for ×.

proj₁ : ∀ {A B : Set}
  → A × B
    -----
  → A

proj₁ ⟨ a , _ ⟩ = a

proj₂ : ∀ {A B : Set}
  → A × B
    -----
  →     B

proj₂ ⟨ _ , b ⟩ = b

-- equivalent construction using records
record _×′_ (A B : Set) : Set where
  field
    proj₁′ : A
    proj₂′ : B
open _×′_

-- eta-equivalence relates constructors and destructors
η-× : ∀ {A B : Set} (w : A × B) → ⟨ proj₁ w , proj₂ w ⟩ ≡ w
η-× ⟨ a , b ⟩ = refl

-- type with two members
infixr 2 _×_
data Bool : Set where
  true  : Bool
  false : Bool

-- type with three members (used in examples)
data Tri : Set where
  aa : Tri
  bb : Tri
  cc : Tri

-- Bool × Tri has six members
×-count : Bool × Tri → ℕ
×-count ⟨ true  , aa ⟩  =  1
×-count ⟨ true  , bb ⟩  =  2
×-count ⟨ true  , cc ⟩  =  3
×-count ⟨ false , aa ⟩  =  4
×-count ⟨ false , bb ⟩  =  5
×-count ⟨ false , cc ⟩  =  6

-- Cartesian product is commutative and associative up to isomorphism.

swap : ∀ {A B : Set} → A × B → B × A
swap ⟨ a , b ⟩ = ⟨ b , a ⟩

×-comm : ∀ {A B : Set} → A × B ≃ B × A
to      ×-comm = swap
from    ×-comm = swap
from∘to ×-comm ⟨ _ , _ ⟩ = refl
to∘from ×-comm ⟨ _ , _ ⟩ = refl

×-assoc : ∀ {A B C : Set} → (A × B) × C ≃ A × (B × C)
to      ×-assoc ⟨ ⟨ a ,   b ⟩ , c ⟩   = ⟨   a , ⟨ b ,   c ⟩ ⟩
from    ×-assoc ⟨   a , ⟨ b ,   c ⟩ ⟩ = ⟨ ⟨ a ,   b ⟩ , c ⟩
from∘to ×-assoc ⟨ ⟨ _ ,   _ ⟩ , _ ⟩   = refl
to∘from ×-assoc ⟨   _ , ⟨ _ ,   _ ⟩ ⟩ = refl

-- A ⇔ B is isomorphic to (A → B) × (B → A)
iff-iso-if-onlyif : ∀ {A B : Set} → A ⇔ B ≃ (A → B) × (B → A)
_≃_.to         iff-iso-if-onlyif record { to = to ; from = from } = ⟨ to , from ⟩
to   (_≃_.from iff-iso-if-onlyif            ⟨ A→B , B→A ⟩)        = A→B
from (_≃_.from iff-iso-if-onlyif            ⟨ A→B , B→A ⟩)        = B→A
from∘to        iff-iso-if-onlyif record { to = to ; from = from } = refl
to∘from        iff-iso-if-onlyif            ⟨ A→B , B→A ⟩         = refl

------------------------------------------------------------------------------
-- logical True is a type with one member (unit)
data ⊤ : Set where
  tt :
    --
    ⊤

η-⊤ : ∀ (w : ⊤) → tt ≡ w
η-⊤ tt = refl

⊤-count : ⊤ → ℕ
⊤-count tt = 1

-- Unit is the left and right identity of product.

⊤-identityˡ : ∀ {A : Set} → ⊤ × A ≃ A
to      ⊤-identityˡ ⟨ ⊤  , a ⟩ =        a
from    ⊤-identityˡ        a   = ⟨ tt , a ⟩
from∘to ⊤-identityˡ ⟨ tt , a ⟩ = refl
to∘from ⊤-identityˡ        a   = refl

⊤-identityʳ : ∀ {A : Set} → (A × ⊤) ≃ A
to      ⊤-identityʳ ⟨ a , ⊤  ⟩ =   a
from    ⊤-identityʳ   a        = ⟨ a , tt ⟩
from∘to ⊤-identityʳ ⟨ a , tt ⟩ = refl
to∘from ⊤-identityʳ   a        = refl

------------------------------------------------------------------------------
-- logical OR (disjunction) is sum (disjoint union)

data _⊎_ : Set → Set → Set where

  inj₁ : ∀ {A B : Set}
    → A
      -----
    → A ⊎ B

  inj₂ : ∀ {A B : Set}
    →     B
      -----
    → A ⊎ B

-- a way to eliminate a sum (but easier to use pattern-matching to eliminate sums)
case-⊎ : ∀ {A B C : Set}
  → (A → C)
  → (B → C)
  →  A ⊎ B
    -----------
  →      C
case-⊎ f g (inj₁ x) = f x
case-⊎ f g (inj₂ x) = g x

-- eta equivalence for sums
η-⊎ : ∀ {A B : Set} (w : A ⊎ B) → case-⊎ inj₁ inj₂ w ≡ w
η-⊎ (inj₁ _) = refl
η-⊎ (inj₂ _) = refl

-- generalization
uniq-⊎ : ∀ {A B C : Set}
       → (h : A ⊎ B → C) → (w : A ⊎ B)
       → case-⊎ (h ∘ inj₁) (h ∘ inj₂) w ≡ h w
uniq-⊎ h (inj₁ _) = refl
uniq-⊎ h (inj₂ _) = refl

infix 1 _⊎_

-- Bool ⊎ Tri has five members
⊎-count : Bool ⊎ Tri → ℕ
⊎-count (inj₁ true)   =  1
⊎-count (inj₁ false)  =  2
⊎-count (inj₂ aa)     =  3
⊎-count (inj₂ bb)     =  4
⊎-count (inj₂ cc)     =  5

-- Sum is commutative up to isomorphism.
⊎-comm : ∀ {A B : Set} → A ⊎ B ≃ B ⊎ A
to      ⊎-comm (inj₁ a) = inj₂ a
to      ⊎-comm (inj₂ b) = inj₁ b
from    ⊎-comm (inj₁ b) = inj₂ b
from    ⊎-comm (inj₂ a) = inj₁ a
from∘to ⊎-comm (inj₁ _) = refl
from∘to ⊎-comm (inj₂ _) = refl
to∘from ⊎-comm (inj₁ _) = refl
to∘from ⊎-comm (inj₂ _) = refl

-- Sum is associative up to isomorphism.
⊎-assoc : ∀ {A B C : Set} → (A ⊎ B) ⊎ C ≃ A ⊎ (B ⊎ C)
to      ⊎-assoc (inj₁ (inj₁ a)) = inj₁ a
to      ⊎-assoc (inj₁ (inj₂ b)) = inj₂ (inj₁ b)
to      ⊎-assoc (inj₂ c)        = inj₂ (inj₂ c)
from    ⊎-assoc (inj₁ a)        = inj₁ (inj₁ a)
from    ⊎-assoc (inj₂ (inj₁ b)) = inj₁ (inj₂ b)
from    ⊎-assoc (inj₂ (inj₂ c)) = inj₂ c
from∘to ⊎-assoc (inj₁ (inj₁ _)) = refl
from∘to ⊎-assoc (inj₁ (inj₂ _)) = refl
from∘to ⊎-assoc (inj₂ _)        = refl
to∘from ⊎-assoc (inj₁ _)        = refl
to∘from ⊎-assoc (inj₂ (inj₁ _)) = refl
to∘from ⊎-assoc (inj₂ (inj₂ _)) = refl

------------------------------------------------------------------------------
-- logical False is the empty type ("bottom", "empty")

data ⊥ : Set where
  -- no clauses

-- Ex falso quodlibet "from falsehood, anything follows".
⊥-elim : ∀ {A : Set}
  → ⊥
    --
  → A

⊥-elim ()

uniq-⊥ : ∀ {C : Set}
       → (h : ⊥ → C) → (w : ⊥)
       → ⊥-elim w ≡ h w
uniq-⊥ h ()

⊥-count : ⊥ → ℕ
⊥-count ()

-- Empty is the left unit of sum up to isomorphism.
⊎-identityˡ : ∀ {A : Set} → ⊥ ⊎ A ≃ A
to      ⊎-identityˡ (inj₂ a) = a
from    ⊎-identityˡ       a  = inj₂ a
from∘to ⊎-identityˡ (inj₂ _) = refl
to∘from ⊎-identityˡ       _  = refl

-- Empty is the right unit of sum up to isomorphism.
⊎-identityʳ : ∀ {A : Set} → A ⊎ ⊥ ≃ A
to      ⊎-identityʳ (inj₁ a) = a
from    ⊎-identityʳ       a  = inj₁ a
from∘to ⊎-identityʳ (inj₁ _) = refl
to∘from ⊎-identityʳ       _  = refl

------------------------------------------------------------------------------
-- logical implication (if-then) is the function TYPE constructor.
-- Eliminating an if-then (modus ponens) is function application.

→-elim : ∀ {A B : Set}
  → (A → B)
  →  A
    -------
  →      B

→-elim L M = L M

-- →-elim works because eta-reduction for → is built in

η-→ : ∀ {A B : Set}
    → (f : A → B)
    → (λ (x : A) → f x) ≡ f
η-→ f = refl

-- The function space A → B is called the exponential Bᴬ.
-- Bool → Tri has 3² (9) members.
→-count : (Bool → Tri) → ℕ
→-count f with f true | f false
...          | aa     | aa      =   1
...          | aa     | bb      =   2
...          | aa     | cc      =   3
...          | bb     | aa      =   4
...          | bb     | bb      =   5
...          | bb     | cc      =   6
...          | cc     | aa      =   7
...          | cc     | bb      =   8
...          | cc     | cc      =   9

------------------------------------------------------------------------------
-- "currying" : functions of multiple parameters
-- math  : (p ^ n) ^ m = p ^ (n * m)
-- types : (A ^ B) ^ C ≃ A ^ (B × C)
currying : ∀ {A B C : Set} → (A → B → C) ≃ (A × B → C)
to      currying f = λ { ⟨ a , b ⟩ → f a b }
from    currying f = λ a b →  f ⟨ a , b ⟩
from∘to currying _ = refl
to∘from currying f = extensionality λ { ⟨ x , y ⟩ → refl }

-- math, : p ^ (n + m) =  (p ^ n) * (p ^ m)
-- types : (A ⊎ B → C) ≃ ((A → C) × (B → C))
→-distrib-⊎ : ∀ {A B C : Set} → (A ⊎ B → C) ≃ ((A → C) × (B → C))
to      →-distrib-⊎ f             = ⟨ (λ a → f (inj₁ a)) , (λ b → f (inj₂ b)) ⟩
from    →-distrib-⊎ ⟨ A→C , B→C ⟩ = λ { (inj₁ a) → A→C a ; (inj₂ b) → B→C b }
from∘to →-distrib-⊎ _             = extensionality λ { (inj₁ x) → refl ; (inj₂ x) → refl }
to∘from →-distrib-⊎               = λ{ ⟨ g , h ⟩ → refl }

-- math  : (p * n) ^ m = (p ^ m) * (n ^ m)
-- types : (A → B × C) ≃ (A → B) × (A → C)
→-distrib-× : ∀ {A B C : Set} → (A → B × C) ≃ (A → B) × (A → C)
to      →-distrib-× f = ⟨ (λ a → proj₁ (f a)) , (λ a → proj₂ (f a)) ⟩
from    →-distrib-× f = λ a → ⟨ proj₁ f a , proj₂ f a ⟩
from∘to →-distrib-×   = λ a→b×c → extensionality λ a → η-× (a→b×c a)
to∘from →-distrib-×   = λ { ⟨ _ , _ ⟩ → refl }

×-distrib-⊎ : ∀ {A B C : Set} → (A ⊎ B) × C ≃ (A × C) ⊎ (B × C)
to      ×-distrib-⊎ ⟨ inj₁ a , c ⟩   = inj₁ ⟨ a , c ⟩
to      ×-distrib-⊎ ⟨ inj₂ b , c ⟩   = inj₂ ⟨ b , c ⟩
from    ×-distrib-⊎ (inj₁ ⟨ a , c ⟩) = ⟨ inj₁ a , c ⟩
from    ×-distrib-⊎ (inj₂ ⟨ b , c ⟩) = ⟨ inj₂ b , c ⟩
from∘to ×-distrib-⊎                  = λ { ⟨ inj₁   a , c ⟩  → refl ; ⟨ inj₂   b , c ⟩  → refl }
to∘from ×-distrib-⊎                  = λ { ( inj₁ ⟨ a , c ⟩) → refl ; ( inj₂ ⟨ b , c ⟩) → refl }

-- Think of a counterexample to show this is not an isomorphism. TODO
⊎-distrib-× : ∀ {A B C : Set} → (A × B) ⊎ C ≲ (A ⊎ C) × (B ⊎ C)
to      ⊎-distrib-× (inj₁ ⟨ a , b ⟩)    = ⟨ inj₁ a , inj₁ b ⟩
to      ⊎-distrib-× (inj₂ c)            = ⟨ inj₂ c , inj₂ c ⟩
from    ⊎-distrib-× ⟨ inj₁ a , inj₁ b ⟩ = inj₁ ⟨ a , b ⟩
from    ⊎-distrib-× ⟨ inj₁ a , inj₂ c ⟩ = inj₂ c
from    ⊎-distrib-× ⟨ inj₂ c , inj₁ b ⟩ = inj₂ c
from    ⊎-distrib-× ⟨ inj₂ c , inj₂ C ⟩ = inj₂ c -- inj₂ C also valid (loses info)
from∘to ⊎-distrib-×                     = λ { (inj₁ ⟨ a , b ⟩) → refl ; (inj₂ c) → refl }

×-distrib-→ : ∀ {A B C : Set} → (C → (A × B)) ≃ (C → A) × (C → B)
to      ×-distrib-→ f = ⟨ (λ c →   proj₁ (f c)) , (λ c → proj₂ (f c)) ⟩
from    ×-distrib-→ f =    λ c → ⟨ proj₁  f c   ,        proj₂  f c   ⟩
from∘to ×-distrib-→   =    λ { c→a×b → extensionality λ c → η-× (c→a×b c) }
to∘from ×-distrib-→   =    λ { ⟨ c→a , c→b ⟩ → refl }

⊎-distrib-→ : ∀ {A B C : Set} → ((A ⊎ B) → C) ≃ (A → C) × (B → C)
to      ⊎-distrib-→ f = ⟨ (λ a →  f (inj₁ a)) , (λ b → f (inj₂ b)) ⟩
from    ⊎-distrib-→ f = λ { (inj₁ a) → (proj₁ f a) ; (inj₂ b) → (proj₂ f b) }
--from∘to ⊎-distrib-→ f = extensionality λ a⊎b → {!!} -- TODO
from∘to ⊎-distrib-→   = λ { a⊎b→c → extensionality λ { (inj₁ a) → refl ; (inj₂ b) → refl } }
to∘from ⊎-distrib-→   = λ { ⟨ a→c , b→c ⟩ → refl }

-- PLFA exercise: a weak distributive law.
⊎-weak-× : ∀ {A B C : Set} → (A ⊎ B) × C → A ⊎ (B × C)
⊎-weak-× ⟨ inj₁ a , c ⟩ = inj₁ a
⊎-weak-× ⟨ inj₂ b , c ⟩ = inj₂ ⟨ b , c ⟩
-- State and prove the strong law, and explain the relationship. TODO

-- SumOfProdImpProdOfSum : disjunct of conjuncts implies a conjunct of disjuncts.
⊎×-implies-×⊎ : ∀ {A B C D : Set}
              → (A × B) ⊎ (C × D)
              → (A ⊎ C) × (B ⊎ D)
⊎×-implies-×⊎ (inj₁ ⟨ a , b ⟩) = ⟨ inj₁ a , inj₁ b ⟩
⊎×-implies-×⊎ (inj₂ ⟨ c , d ⟩) = ⟨ inj₂ c , inj₂ d ⟩

-- the converse is NOT true
×⊎-implies-⊎×-not : ∀ {A B C D : Set}
                  → (A ⊎ C) × (B ⊎ D)
                  → (A × B) ⊎ (C × D)
×⊎-implies-⊎×-not ⟨ inj₁ a , inj₁ b ⟩ = inj₁ ⟨ a , b ⟩
×⊎-implies-⊎×-not ⟨ inj₁ a , inj₂ d ⟩ = {!!} -- A × B ⊎ C × D -- not possible
×⊎-implies-⊎×-not ⟨ inj₂ c , inj₁ b ⟩ = {!!} -- A × B ⊎ C × D -- not possible
×⊎-implies-⊎×-not ⟨ inj₂ c , inj₂ d ⟩ = inj₂ ⟨ c , d ⟩


------------------------------------------------------------------------
-- The Agda standard library
--
-- Several kinds of "relatedness" for containers such as equivalences,
-- surjections and bijections
------------------------------------------------------------------------

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

module Data.Container.Related where

open import Level using (_⊔_)
open import Data.Container.Core
import Function.Related as Related
open import Relation.Binary
open import Data.Container.Membership

open Related public
  using (Kind; Symmetric-kind)
  renaming ( implication         to subset
           ; reverse-implication to superset
           ; equivalence         to set
           ; injection           to subbag
           ; reverse-injection   to superbag
           ; bijection           to bag
           )

[_]-Order : ∀ {s p ℓ} → Kind → Container s p → Set ℓ →
            Preorder (s ⊔ p ⊔ ℓ) (s ⊔ p ⊔ ℓ) (p ⊔ ℓ)
[ k ]-Order C X = Related.InducedPreorder₂ k (_∈_ {C = C} {X = X})

[_]-Equality : ∀ {s p ℓ} → Symmetric-kind → Container s p → Set ℓ →
               Setoid (s ⊔ p ⊔ ℓ) (p ⊔ ℓ)
[ k ]-Equality C X = Related.InducedEquivalence₂ k (_∈_ {C = C} {X = X})

infix 4 _∼[_]_
_∼[_]_ : ∀ {s p x} {C : Container s p} {X : Set x} →
         ⟦ C ⟧ X → Kind → ⟦ C ⟧ X → Set (p ⊔ x)
_∼[_]_ {C = C} {X} xs k ys = Preorder._∼_ ([ k ]-Order C X) xs ys


-- Example usage of solver

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

open import Categories.Category
open import Categories.Category.Cartesian

module Experiment.Categories.Solver.Category.Cartesian.Example
  {o ℓ e} {𝒞 : Category o ℓ e} (cartesian : Cartesian 𝒞) where

open import Experiment.Categories.Solver.Category.Cartesian cartesian

open Category 𝒞
open Cartesian cartesian
open HomReasoning

private
  variable
    A B C D E F : Obj

module _ {f : D ⇒ E} {g : C ⇒ D} {h : B ⇒ C} {i : A ⇒ B} where
  _ : (f ∘ g) ∘ id ∘ h ∘ i ≈ f ∘ (g ∘ h) ∘ i
  _ = solve ((∥-∥ :∘ ∥-∥) :∘ :id :∘ ∥-∥ :∘ ∥-∥) (∥-∥ :∘ (∥-∥ :∘ ∥-∥) :∘ ∥-∥) refl

swap∘swap≈id :
  ∀ {A B} → swap {A}{B} ∘ swap {B}{A} ≈ id
swap∘swap≈id {A} {B} =
  solve (:swap {∥ A ∥} {∥ B ∥} :∘ :swap)
        :id
        refl

assocʳ∘assocˡ≈id : ∀ {A B C} → assocʳ {A}{B}{C} ∘ assocˡ {A}{B}{C} ≈ id
assocʳ∘assocˡ≈id = solve (:assocʳ {∥ _ ∥} {∥ _ ∥} {∥ _ ∥} :∘ :assocˡ) :id refl

module _ {f : B ⇒ C} (f′ : A ⇒ B) {g : E ⇒ F} {g′ : D ⇒ E} where
  ⁂-∘ : (f ⁂ g) ∘ (f′ ⁂ g′) ≈ (f ∘ f′) ⁂ (g ∘ g′)
  ⁂-∘ = solve lhs rhs refl where
    lhs = (∥ f ∥ :⁂ ∥ g ∥) :∘ (∥ f′ ∥ :⁂ ∥ g′ ∥)
    rhs = (∥ f ∥ :∘ ∥ f′ ∥) :⁂ (∥ g ∥ :∘ ∥ g′ ∥)

module _ {A B C D} where
  pentagon′ : (id ⁂ assocˡ) ∘ assocˡ ∘ (assocˡ ⁂ id) ≈
              assocˡ ∘ assocˡ {A × B} {C} {D}
  pentagon′ = solve lhs rhs refl where
    lhs = (:id :⁂ :assocˡ) :∘ :assocˡ :∘ (:assocˡ :⁂ :id)
    rhs = :assocˡ :∘ :assocˡ {∥ A ∥ :× ∥ B ∥} {∥ C ∥} {∥ D ∥}

module _ {A B C} where
  hexagon₁′ : (id ⁂ swap) ∘ assocˡ ∘ (swap ⁂ id) ≈
              assocˡ ∘ swap ∘ assocˡ {A}{B}{C}
  hexagon₁′ = solve lhs rhs refl where
    lhs = (:id :⁂ :swap) :∘ :assocˡ :∘ (:swap :⁂ :id)
    rhs = :assocˡ :∘ :swap :∘ :assocˡ {∥ A ∥}{∥ B ∥}{∥ C ∥}

module _ {f : A ⇒ B} where
  commute : ⟨ ! , id ⟩ ∘ f ≈ ⟨ id ∘ π₁ , f ∘ π₂ ⟩ ∘ ⟨ ! , id ⟩
  commute = solve (:⟨ :! , :id ⟩ :∘ ∥ f ∥)
                  (:⟨ :id :∘ :π₁ , ∥ f ∥ :∘ :π₂ ⟩ :∘ :⟨ :! , :id ⟩)
                  refl

_ : id {⊤} ≈ !
_ = solve (∥ id !∥) :! refl

_ : π₁ {⊤} {⊤} ≈ π₂
_ = solve (:π₁ {:⊤} {:⊤}) :π₂ refl

module _ {f : A ⇒ B} {g : C ⇒ D} where
  first↔second′ : first f ∘ second g ≈ second g ∘ first f
  first↔second′ = solve (:first ∥ f ∥ :∘ :second ∥ g ∥)
                        (:second ∥ g ∥ :∘ :first ∥ f ∥)
                        refl


{-# OPTIONS -v treeless.opt:20 #-}
module _ where

open import Agda.Builtin.Nat renaming (_<_ to _<?_)
open import Common.Prelude
open import Common.Equality

data _<_ (a b : Nat) : Set where
  diff : (k : Nat) → b ≡ suc k + a → a < b

data Comparison {a} {A : Set a} (_<_ : A → A → Set a) (x y : A) : Set a where
  less    : x < y → Comparison _<_ x y
  equal   : x ≡ y → Comparison _<_ x y
  greater : y < x → Comparison _<_ x y

compare : (a b : Nat) → Comparison _<_ a b
compare a b with a <? b
... | true  = less (diff (b ∸ suc a) primTrustMe)
... | false with b <? a
...   | true  = greater (diff (a ∸ suc b) primTrustMe)
...   | false = equal primTrustMe

{-# INLINE compare #-}

-- This should compile to two calls of _<?_ and only the possible cases.
compare-lots : (a b : Nat) → String
compare-lots a b with compare a b | compare (suc a) (suc b)
compare-lots a b | less (diff k eq) | less (diff k₁ eq₁) = "less-less"
compare-lots a .a | less (diff k eq) | equal refl = "less-equal"
compare-lots a _ | less (diff k refl) | greater (diff j eq) = "less-greater"
compare-lots a .a | equal refl | less (diff k eq₁) = "equal-less"
compare-lots a b | equal eq | equal eq₁ = "equal-equal"
compare-lots a .a | equal refl | greater (diff k eq₁) = "equal-greater"
compare-lots _ b | greater (diff k refl) | less (diff j eq) = "greater-less"
compare-lots _ b | greater (diff k refl) | equal eq = "greater-equal"
compare-lots a b | greater (diff k eq) | greater (diff k₁ eq₁) = "greater-greater"

main : IO Unit
main = putStrLn (compare-lots 1500 2000) ,,
       putStrLn (compare-lots 2000 1500) ,,
       putStrLn (compare-lots 2000 2000)


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

open import lib.Basics
open import lib.NType2
open import lib.NConnected
open import lib.types.Nat
open import lib.types.Subtype
open import lib.types.Truncation

-- classifying types of automorphism groups of types
module lib.types.BAut where

BAut : ∀ {i} → Type i → Type (lsucc i)
BAut {i} A = Σ (Type i) λ X → Trunc -1 (A == X)

BAut-prop : ∀ {i} (A : Type i) → SubtypeProp (Type i) (lsucc i)
BAut-prop A = ((λ X → Trunc -1 (A == X)) , (λ X → Trunc-level))

pBAut : ∀ {i} → Type i → Ptd (lsucc i)
de⊙ (pBAut A) = BAut A
pt (pBAut A) = A , [ idp ]

BAut-trunc-path : ∀ {i} (A X : Type i) → (tp : Trunc -1 (A == X))
                  → Trunc -1 ((A , [ idp ]) == (X , tp) :> BAut A)
BAut-trunc-path {i} A X = Trunc-elim λ p → [ pair= p prop-has-all-paths-↓ ]

BAut-conn : ∀ {i} (A : Type i) → is-connected 0 (BAut A)
fst (has-level-apply (BAut-conn A)) = [ pt (pBAut A) ]
snd (has-level-apply (BAut-conn A)) = Trunc-elim (λ { (X , tp) → <– (=ₜ-equiv [ A , [ idp ] ] [ X , tp ])
                                                                    (BAut-trunc-path A X tp) })


-----------------------------------------------------------------------------------
-- This file contains facts about equational reasoning pertaining to categories. --
-----------------------------------------------------------------------------------
module Category.CatEq where

open import Level

open import Category.Category
open import Setoid.Total

-- The following models the communative square:
--
--  A ---f₁---> C
--  |           |
--  f₃          f₂
--  |           |
--  V           V
--  D ---f₄---> D
--
-- However, as an equation.
comm-square : {l : Level}{ℂ : Cat {l}}{A B D C : Obj ℂ}
            → el (Hom ℂ A B)
            → el (Hom ℂ B C)
            → el (Hom ℂ A D)
            → el (Hom ℂ D C)
            → Set l
comm-square {ℂ = ℂ}{A}{C = C} f₁ f₂ f₃ f₄ = ⟨ Hom ℂ A C ⟩[ f₁ ○[ comp ℂ ] f₂ ≡ f₃ ○[ comp ℂ ] f₄ ]


------------------------------------------------------------------------------
-- Agda-Prop Library.
-- Theorems of ⇔ connective.
------------------------------------------------------------------------------

open import Data.Nat using ( ℕ )

module Data.PropFormula.Theorems.Biimplication ( n : ℕ ) where

------------------------------------------------------------------------------

open import Data.PropFormula.Theorems.Classical n
open import Data.PropFormula.Theorems.Implication n
  using ( ⊃-to-¬∨; ⊃⊃-to-∧⊃; ∧⊃-to-⊃⊃ )
open import Data.PropFormula.Theorems.Negation n
  using ( ¬-equiv₁ ; ¬-equiv₂; ¬∨-to-⊃; ¬¬-equiv₁; ¬¬-equiv₂ )
open import Data.PropFormula.Syntax n

open import Function using ( _$_ )

------------------------------------------------------------------------------

-- Theorem.
⇔-equiv₁
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ φ ⇔ ψ
  → Γ ⊢ (φ ⊃ ψ) ∧ (ψ ⊃ φ)

-- Proof.
⇔-equiv₁ {Γ}{φ}{ψ} Γ⊢φ⇔ψ =
  ∧-intro
    (⊃-intro
      (⇔-elim₁
        (assume φ)
        (weaken φ Γ⊢φ⇔ψ)))
    (⊃-intro
      (⇔-elim₂
        (assume ψ)
         (weaken ψ Γ⊢φ⇔ψ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
⇔-equiv₂
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ (φ ⊃ ψ) ∧ (ψ ⊃ φ)
  → Γ ⊢ φ ⇔ ψ

-- Proof.
⇔-equiv₂ {φ = φ}{ψ} Γ⊢⟪φ⊃ψ⟫∧⟪ψ⊃φ⟫ =
  ⇔-intro
    (⊃-elim
      (weaken φ (∧-proj₁ Γ⊢⟪φ⊃ψ⟫∧⟪ψ⊃φ⟫))
      (assume φ))
    (⊃-elim
      (weaken ψ (∧-proj₂ Γ⊢⟪φ⊃ψ⟫∧⟪ψ⊃φ⟫))
      (assume ψ))
-------------------------------------------------------------------------- ∎

-- Theorem.
⇔-equiv
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ (φ ⇔ ψ) ⇔ ((φ ⊃ ψ) ∧ (ψ ⊃ φ))

-- Proof.
⇔-equiv {Γ}{φ}{ψ} =
  ⇔-intro
    (⇔-equiv₁
      (assume (φ ⇔ ψ)))
    (⇔-equiv₂
      (assume ((φ ⊃ ψ) ∧ (ψ ⊃ φ))))
-------------------------------------------------------------------------- ∎

-- Theorem.
⇔-assoc₁
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ φ ⇔ (ψ ⇔ γ)
  → Γ ⊢ (φ ⇔ ψ) ⇔ γ

-- Proof.
⇔-assoc₁ {Γ}{φ = φ}{ψ}{γ} thm =
  ⇔-intro
    (RAA
      (¬-elim
        (¬-intro
          (¬-elim
            (¬-intro
              (¬-elim
                (weaken φ $ weaken (ψ ⇔ γ) $ assume {Γ = Γ , φ ⇔ ψ} (¬ γ))
                (⇔-elim₁
                  (⇔-elim₁
                    (assume {Γ = Γ , φ ⇔ ψ , ¬ γ , ψ ⇔ γ} φ)
                    (weaken φ $ weaken (ψ ⇔ γ)
                        (weaken (¬ γ) $ assume (φ ⇔ ψ))))
                  (⇔-elim₁
                    (assume {Γ = Γ , φ ⇔ ψ , ¬ γ , ψ ⇔ γ} φ)
                    (weaken φ $ weaken (ψ ⇔ γ) $ weaken (¬ γ) $
                      weaken (φ ⇔ ψ) thm)))))
            (⇔-elim₂
              (assume {Γ = Γ , φ ⇔ ψ , ¬ γ} (ψ ⇔ γ))
              (weaken (ψ ⇔ γ) $ weaken (¬ γ) $ weaken (φ ⇔ ψ) thm))))
        (⇔-intro
          (⊥-elim γ
            (¬-elim
              (⊥-elim (¬ ψ)
                (¬-elim
                  (¬-intro -- ¬ φ
                    (¬-elim
                      (weaken φ $ weaken ψ $ assume {Γ = Γ , φ ⇔ ψ} (¬ γ))
                      (⇔-elim₁
                        (⇔-elim₁
                          (assume {Γ = Γ , φ ⇔ ψ , ¬ γ , ψ} φ)
                          (weaken φ
                            (weaken ψ
                              (weaken (¬ γ)
                                (assume (φ ⇔ ψ))))))
                        (⇔-elim₁
                          (assume {Γ = Γ , φ ⇔ ψ , ¬ γ , ψ} φ)
                          (weaken φ $
                            weaken ψ $ weaken (¬ γ) $ weaken (φ ⇔ ψ) thm)))))
                  (⇔-elim₂ -- φ
                    (assume {Γ = Γ , φ ⇔ ψ , ¬  γ} ψ)
                    (weaken ψ (weaken (¬ γ) (assume (φ ⇔ ψ)))))))
              (assume {Γ = Γ , φ ⇔ ψ , ¬ γ} ψ)))
          (⊥-elim ψ
            (¬-elim
              (weaken γ $ assume {Γ = Γ , φ ⇔ ψ} (¬ γ))
              (assume {Γ = Γ , φ ⇔ ψ , ¬ γ} γ))))))
    (⇔-intro
      (⇔-elim₂
        (weaken φ $ assume γ)
        (⇔-elim₁
          (assume {Γ = Γ , γ} φ)
          (weaken φ $ weaken γ thm)))
      (⇔-elim₂
        (⇔-intro
          (weaken ψ $ weaken ψ $ assume γ)
          (weaken γ $ assume {Γ = Γ , γ} ψ))
        (weaken ψ $ weaken γ thm)))
-------------------------------------------------------------------------- ∎

-- Theorem.
⇔-assoc₂
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ⇔ ψ) ⇔ γ
  → Γ ⊢ φ ⇔ (ψ ⇔ γ)

-- Proof.
⇔-assoc₂ {Γ}{φ}{ψ}{γ} thm =
  ⇔-intro
    (⇔-intro
      (⇔-elim₁
        (⇔-intro
          (weaken φ (assume {Γ = Γ , φ} ψ))
          (weaken ψ (weaken ψ (assume φ))))
        (weaken ψ (weaken φ thm)))
      (⇔-elim₁
        (weaken γ (assume φ))
          (⇔-elim₂
            (assume {Γ = Γ , φ} γ)
            (weaken γ (weaken φ thm)))))
    (RAA
      (¬-elim
        (¬-intro
          (¬-elim
            (¬-intro
              (¬-elim
                (weaken γ $ weaken (φ ⇔ ψ) $ assume {Γ = Γ , ψ ⇔ γ} (¬ φ))
                (⇔-elim₂
                  (⇔-elim₂
                    (assume {Γ = Γ , ψ ⇔ γ , ¬ φ , (φ ⇔ ψ)} γ)
                    (weaken γ $ weaken (φ ⇔ ψ) $ weaken (¬ φ) $
                      assume (ψ ⇔ γ)))
                  (⇔-elim₂
                    (assume {Γ = Γ , ψ ⇔ γ , ¬ φ , (φ ⇔ ψ)} γ)
                    (weaken γ $ weaken (φ ⇔ ψ) $ weaken (¬ φ) $
                      weaken (ψ ⇔ γ) thm)))))
            (⇔-elim₁
              (assume {Γ = Γ , ψ ⇔ γ , ¬ φ} (φ ⇔ ψ))
              (weaken (φ ⇔ ψ) (weaken (¬ φ) (weaken (ψ ⇔ γ) thm))))))
        (⇔-intro
          (⊥-elim ψ
            (¬-elim
              (weaken φ $ assume {Γ = Γ , ψ ⇔ γ} (¬ φ))
              (assume {Γ = Γ , ψ ⇔ γ , ¬ φ} φ)))
          (⊥-elim φ
            (¬-elim
              (⊥-elim (¬ ψ)
                (¬-elim
                  (¬-intro
                    (¬-elim
                      (weaken γ $ weaken ψ $ assume {Γ = Γ , ψ ⇔ γ} (¬ φ))
                      (⇔-elim₂
                        (weaken γ $ assume {Γ = Γ , ψ ⇔ γ , ¬ φ} ψ)
                        (⇔-elim₂
                          (assume {Γ = Γ , ψ ⇔ γ , ¬ φ , ψ} γ)
                          (weaken γ $ weaken ψ $ weaken (¬ φ) $
                            weaken (ψ ⇔ γ) thm)))))
                  (⇔-elim₁
                    (assume {Γ = Γ , ψ ⇔ γ , ¬ φ} ψ)
                    (weaken ψ (weaken (¬ φ) (assume (ψ ⇔ γ)))))))
              (assume {Γ = Γ , ψ ⇔ γ , ¬ φ} ψ))))))
-------------------------------------------------------------------------- ∎

-- Theorem.
⇔-assoc
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ⇔ (ψ ⇔ γ)) ⇔ ((φ ⇔ ψ) ⇔ γ)

-- Proof.
⇔-assoc {φ = φ}{ψ}{γ} =
  ⇔-intro
    (⇔-assoc₁ (assume (φ ⇔ ψ ⇔ γ)))
    (⇔-assoc₂ (assume ((φ ⇔ ψ) ⇔ γ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
⇔-comm
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ φ ⇔ ψ
  → Γ ⊢ ψ ⇔ φ

-- Proof.
⇔-comm {Γ}{φ}{ψ} Γ⊢φ⇔ψ =
  ⇔-intro
    (⇔-elim₂ (assume ψ) (weaken ψ Γ⊢φ⇔ψ))
    (⇔-elim₁ (assume φ) (weaken φ Γ⊢φ⇔ψ))
-------------------------------------------------------------------------- ∎

-- Theorem.
⊃-⇔-¬∨
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ (φ ⊃ ψ) ⇔ (¬ φ ∨ ψ)

-- Proof.

⊃-⇔-¬∨ {Γ}{φ}{ψ} =
  ⇔-intro
    (⊃-to-¬∨ (assume (φ ⊃ ψ)))
    (¬∨-to-⊃ (assume (¬ φ ∨ ψ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
bicon₀-thm
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ φ ⇔ ψ → Γ ⊢ ¬ φ
  → Γ ⊢ ¬ ψ
⇔-¬-to-¬ = bicon₀-thm

-- Proof.
bicon₀-thm {Γ}{φ}{ψ} Γ⊢φ⇔ψ Γ⊢¬ψ =
  ¬-equiv₂
    (⊃-intro
      (¬-elim
        (weaken ψ Γ⊢¬ψ)
      (⇔-elim₂
        (assume ψ)
        (weaken ψ Γ⊢φ⇔ψ))))
-------------------------------------------------------------------------- ∎

-- Theorem.
bicon₁-thm
  : ∀ {Γ} {φ ψ}
  → Γ ⊢ ¬ φ ⇔ ψ → Γ ⊢ φ
  → Γ ⊢ ¬ ψ
¬⇔-to-¬ = bicon₁-thm

-- Proof.
bicon₁-thm {Γ}{φ}{ψ} Γ⊢¬φ⇔ψ Γ⊢φ =
  ¬-equiv₂
    (⊃-intro
      (¬-elim
        (⇔-elim₂
          (assume ψ)
          (weaken ψ Γ⊢¬φ⇔ψ))
        (weaken ψ Γ⊢φ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
¬-equiv
  : ∀ {Γ} {φ}
  → Γ ⊢ ¬ φ ⇔ (φ ⊃ ⊥)

-- Proof.
¬-equiv {Γ}{φ} =
  ⇔-intro
    (¬-equiv₁ (assume (¬ φ)))
    (¬-equiv₂ (assume (φ ⊃ ⊥)))
-------------------------------------------------------------------------- ∎

-- Theorem.
¬¬-equiv
  : ∀ {Γ} {φ}
  → Γ ⊢ ¬ (¬ φ) ⇔ φ

-- Proof.
¬¬-equiv {Γ}{φ} =
  ⇔-intro
    (¬¬-equiv₁ (assume (¬ (¬ φ))))
    (¬¬-equiv₂ (assume φ))
-------------------------------------------------------------------------- ∎

-- Theorem.
⊃⊃-⇔-∧⊃
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ (φ ⊃ (ψ ⊃ γ)) ⇔ ((φ ∧ ψ) ⊃ γ)

-- Proof.
⊃⊃-⇔-∧⊃ {φ = φ}{ψ}{γ} =
  ⇔-intro
    (⊃⊃-to-∧⊃ (assume (φ ⊃ ψ ⊃ γ)))
    (∧⊃-to-⊃⊃ (assume (φ ∧ ψ ⊃ γ)))
-------------------------------------------------------------------------- ∎

-- Theorem.
⇔-trans
  : ∀ {Γ} {φ ψ γ}
  → Γ ⊢ γ ⇔ φ
  → Γ ⊢ φ ⇔ ψ
  → Γ ⊢ γ ⇔ ψ
subst⊢⇔₁ = ⇔-trans

-- Proof.
⇔-trans {φ = φ}{ψ}{γ} Γ⊢γ⇔φ Γ⊢φ⇔ψ =
  ⇔-intro
    (⇔-elim₁
      (⇔-elim₁
        (assume γ)
        (weaken γ Γ⊢γ⇔φ))
      (weaken γ Γ⊢φ⇔ψ))
    (⇔-elim₂
      (⇔-elim₂
        (assume ψ)
        (weaken ψ Γ⊢φ⇔ψ))
      (weaken ψ Γ⊢γ⇔φ))
-------------------------------------------------------------------------- ∎


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

module LogicalFramework.AdequacyTheorems where

module Example5 where

  -- First-order logic with equality.
  open import Common.FOL.FOL-Eq public

  postulate
    A B C : Set
    f₁    : A → C
    f₂    : B → C

  g : (A → C) → (B → C) → A ∨ B → C
  g f₁ f₂ (inj₁ a) = f₁ a
  g f₁ f₂ (inj₂ b) = f₂ b

  g' : (A → C) → (B → C) → A ∨ B → C
  g' f₁ f₂ x = case f₁ f₂ x

module Example7 where

  -- First-order logic with equality.
  open import Common.FOL.FOL-Eq public

  postulate
    C  : D → D → Set
    d  : ∀ {a} → C a a

  g : ∀ {a b} → a ≡ b → C a b
  g refl = d

  g' : ∀ {a b} → a ≡ b → C a b
  g' {a} h = subst (C a) h d

module Example10 where

  -- First-order logic with equality.
  open import Common.FOL.FOL-Eq public

  postulate
    A B C : Set
    f₁ : A → C
    f₂ : B → C

  f : A ∨ B → C
  f (inj₁ a) = f₁ a
  f (inj₂ b) = f₂ b

  f' : A ∨ B → C
  f' = case f₁ f₂

module Example20 where

  -- First-order logic with equality.
  open import Common.FOL.FOL-Eq public

  f : {A : D → Set}{t t' : D} → t ≡ t' → A t → A t'
  f {A} {t} {.t} refl At = d At
    where
    postulate d : A t → A t

  f' : {A : D → Set}{t t' : D} → t ≡ t' → A t → A t'
  f' {A} h At = subst A h At

module Example30 where

  -- First-order logic with equality.
  open import Common.FOL.FOL-Eq public

  postulate
    A B C E : Set
    f₁ : A → E
    f₂ : B → E
    f₃ : C → E

  g : (A ∨ B) ∨ C → E
  g (inj₁ (inj₁ a)) = f₁ a
  g (inj₁ (inj₂ b)) = f₂ b
  g (inj₂ c)        = f₃ c

  g' : (A ∨ B) ∨ C → E
  g' = case (case f₁ f₂) f₃

module Example40 where

  infixl 9  _+_ _+'_
  infix  7  _≡_

  data ℕ : Set where
    zero :     ℕ
    succ : ℕ → ℕ

  ℕ-ind : (A : ℕ → Set) → A zero → (∀ n → A n → A (succ n)) → ∀ n → A n
  ℕ-ind A A0 h zero     = A0
  ℕ-ind A A0 h (succ n) = h n (ℕ-ind A A0 h n)

  data _≡_ (x : ℕ) : ℕ → Set where
    refl : x ≡ x

  subst : (A : ℕ → Set) → ∀ {x y} → x ≡ y → A x → A y
  subst A refl Ax = Ax

  _+_ : ℕ → ℕ → ℕ
  zero   + n = n
  succ m + n = succ (m + n)

  _+'_ : ℕ → ℕ → ℕ
  m +' n = ℕ-ind (λ _ → ℕ) n (λ x y → succ y) m

  -- Properties using pattern matching.
  succCong : ∀ {m n} → m ≡ n → succ m ≡ succ n
  succCong refl = refl

  +-rightIdentity : ∀ n → n + zero ≡ n
  +-rightIdentity zero     = refl
  +-rightIdentity (succ n) = succCong (+-rightIdentity n)

  -- Properties using the basic inductive constants.
  succCong' : ∀ {m n} → m ≡ n → succ m ≡ succ n
  succCong' {m} h = subst (λ x → succ m ≡ succ x) h refl

  +'-leftIdentity : ∀ n → zero +' n ≡ n
  +'-leftIdentity n = refl

  +'-rightIdentity : ∀ n → n +' zero ≡ n
  +'-rightIdentity = ℕ-ind A A0 is
    where
    A : ℕ → Set
    A n = n +' zero ≡ n

    A0 : A zero
    A0 = refl

    is : ∀ n → A n → A (succ n)
    is n ih = succCong' ih

module Example50 where

  infixl 10 _*_
  infixl 9  _+_
  infix  7  _≡_

  data ℕ : Set where
    zero :     ℕ
    succ : ℕ → ℕ

  ℕ-ind : (A : ℕ → Set) → A zero → (∀ n → A n → A (succ n)) → ∀ n → A n
  ℕ-ind A A0 h zero     = A0
  ℕ-ind A A0 h (succ n) = h n (ℕ-ind A A0 h n)

  data _≡_ (x : ℕ) : ℕ → Set where
    refl : x ≡ x

  ℕ-rec : {A : Set} → A → (ℕ → A → A) → ℕ → A
  ℕ-rec {A} = ℕ-ind (λ _ → A)

  _+_ : ℕ → ℕ → ℕ
  m + n = ℕ-rec n (λ _ x → succ x) m

  +-0x : ∀ n → zero + n ≡ n
  +-0x n = refl

  +-Sx : ∀ m n → succ m + n ≡ succ (m + n)
  +-Sx m n = refl

  _*_ : ℕ → ℕ → ℕ
  m * n = ℕ-rec zero (λ _ x → n + x) m

  *-0x : ∀ n → zero * n ≡ zero
  *-0x n = refl

  *-Sx : ∀ m n → succ m * n ≡ n + m * n
  *-Sx m n = refl


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

open import HoTT

{- Move (parts of) faces of a cube around -}

module lib.cubical.elims.CubeMove where

square-push-rb : ∀ {i} {A : Type i} {a₀₀ a₀₁ a₁₀ a₁₁ b : A}
  {p₀₋ : a₀₀ == a₀₁} {p₋₀ : a₀₀ == a₁₀}
  {p₋₁ : a₀₁ == a₁₁} (p₁₋ : a₁₀ == b) (q : b == a₁₁)
  → Square p₀₋ p₋₀ p₋₁ (p₁₋ ∙ q)
  → Square p₀₋ p₋₀ (p₋₁ ∙' ! q) p₁₋
square-push-rb idp idp sq = sq

private
  right-from-front-lemma : ∀ {i} {A : Type i}
    {a₀₀₀ a₀₁₀ a₁₀₀ a₁₁₀ a₀₀₁ a₀₁₁ a₁₀₁ a₁₁₁ : A}
    {p₀₋₀ : a₀₀₀ == a₀₁₀} {p₋₀₀ : a₀₀₀ == a₁₀₀}
    {p₋₁₀ : a₀₁₀ == a₁₁₀} {p₁₋₀ : a₁₀₀ == a₁₁₀}
    {sq₋₋₀ : Square p₀₋₀ p₋₀₀ p₋₁₀ p₁₋₀} -- left

    {p₀₋₁ : a₀₀₁ == a₀₁₁} {p₋₀₁ : a₀₀₁ == a₁₀₁}
    {p₋₁₁ : a₀₁₁ == a₁₁₁} {p₁₋₁ : a₁₀₁ == a₁₁₁}
    (sq₋₋₁ : Square p₀₋₁ p₋₀₁ p₋₁₁ p₁₋₁) -- right

    {p₀₀₋ : a₀₀₀ == a₀₀₁} {p₀₁₋ : a₀₁₀ == a₀₁₁}
    {p₁₀₋ : a₁₀₀ == a₁₀₁} {p₁₁₋ : a₁₁₀ == a₁₁₁}
    {sq₀₋₋ : Square p₀₋₀ p₀₀₋ p₀₁₋ p₀₋₁} -- back
    {sq₋₀₋ : Square p₋₀₀ p₀₀₋ p₁₀₋ (p₋₀₁ ∙ idp)} -- top
    {sq₋₁₋ : Square p₋₁₀ p₀₁₋ p₁₁₋ (p₋₁₁ ∙ idp)} -- bottom
    {sq₁₋₋ : Square p₁₋₀ p₁₀₋ p₁₁₋ p₁₋₁} -- front
    → Cube sq₋₋₀ sq₋₋₁ sq₀₋₋ (square-push-rb p₋₀₁ idp sq₋₀₋)
           (square-push-rb p₋₁₁ idp sq₋₁₋) (sq₁₋₋ ⊡h' !□h hid-square)
    → Cube sq₋₋₀ (sq₋₋₁ ⊡h hid-square) sq₀₋₋ sq₋₀₋ sq₋₁₋ sq₁₋₋
  right-from-front-lemma ids cu = cu

cube-right-from-front : ∀ {i} {A : Type i}
  {a₀₀₀ a₀₁₀ a₁₀₀ a₁₁₀ a₀₀₁ a₀₁₁ a₁₀₁ a₁₁₁ b₀ b₁ : A}
  {p₀₋₀ : a₀₀₀ == a₀₁₀} {p₋₀₀ : a₀₀₀ == a₁₀₀}
  {p₋₁₀ : a₀₁₀ == a₁₁₀} {p₁₋₀ : a₁₀₀ == a₁₁₀}
  {sq₋₋₀ : Square p₀₋₀ p₋₀₀ p₋₁₀ p₁₋₀} -- left

  {p₀₋₁ : a₀₀₁ == a₀₁₁}
  {p₋₀₁ : a₀₀₁ == b₀} {q₋₀₁ : b₀ == a₁₀₁}
  {r : b₀ == b₁}
  {p₋₁₁ : a₀₁₁ == b₁} {q₋₁₁ : b₁ == a₁₁₁}
  {p₁₋₁ : a₁₀₁ == a₁₁₁}
  (sq₋₋₁ : Square p₀₋₁ p₋₀₁ p₋₁₁ r) -- right

  {p₀₀₋ : a₀₀₀ == a₀₀₁} {p₀₁₋ : a₀₁₀ == a₀₁₁}
  {p₁₀₋ : a₁₀₀ == a₁₀₁} {p₁₁₋ : a₁₁₀ == a₁₁₁}
  {sq₀₋₋ : Square p₀₋₀ p₀₀₋ p₀₁₋ p₀₋₁} -- back
  {sq₋₀₋ : Square p₋₀₀ p₀₀₋ p₁₀₋ (p₋₀₁ ∙ q₋₀₁)} -- top
  {sq₋₁₋ : Square p₋₁₀ p₀₁₋ p₁₁₋ (p₋₁₁ ∙ q₋₁₁)} -- bottom
  {sq₁₋₋ : Square p₁₋₀ p₁₀₋ p₁₁₋ p₁₋₁} -- front
  (sq' : Square r q₋₀₁ q₋₁₁ p₁₋₁)
  → Cube sq₋₋₀ sq₋₋₁ sq₀₋₋ (square-push-rb p₋₀₁ q₋₀₁ sq₋₀₋)
         (square-push-rb p₋₁₁ q₋₁₁ sq₋₁₋) (sq₁₋₋ ⊡h' !□h sq')
  → Cube sq₋₋₀ (sq₋₋₁ ⊡h sq') sq₀₋₋ sq₋₀₋ sq₋₁₋ sq₁₋₋
cube-right-from-front sq₋₋₁ ids cu = right-from-front-lemma sq₋₋₁ cu


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

open import level using (Level; _⊔_)
open import function.core

infixr 4 _,_
infixr 2 _×_
infixr 1 _⊎_

record Σ {a b} (A : Set a) (B : A → Set b) : Set (a ⊔ b) where
  constructor _,_
  field
    proj₁ : A
    proj₂ : B proj₁

open Σ public

_×_ : {l k : Level} (A : Set l) (B : Set k) → Set (l ⊔ k)
A × B = Σ A λ _ → B

uncurry : ∀ {a b c} {A : Set a} {B : A → Set b} {C : (x : A) → (B x) → Set c}
        → ((x : A) → (y : B x) → C x y) → ((xy : Σ A B) → C (proj₁ xy) (proj₂ xy))
uncurry f (a , b) = f a b

uncurry' : ∀ {i j k}{X : Set i}{Y : Set j}{Z : Set k}
        → (X → Y → Z)
        → (X × Y) → Z
uncurry' f (x , y) = f x y

data _⊎_ {a b} (A : Set a) (B : Set b) : Set (a ⊔ b) where
  inj₁ : (x : A) → A ⊎ B
  inj₂ : (y : B) → A ⊎ B

[_,⊎_] : ∀ {i i' j}{A : Set i}{A' : Set i'}{B : Set j}
      → (A → B) → (A' → B)
      → A ⊎ A' → B
[ f ,⊎ f' ] (inj₁ a) = f a
[ f ,⊎ f' ] (inj₂ a') = f' a'

map-⊎ : ∀ {i i' j j'}{A : Set i}{A' : Set i'}
      → {B : Set j}{B' : Set j'}
      → (A → B) → (A' → B')
      → A ⊎ A' → B ⊎ B'
map-⊎ g g' = [ inj₁ ∘' g ,⊎ inj₂ ∘' g' ]


-- Category of sets and functions

module Control.Category.SetsAndFunctions where

open import Level using (zero; suc; _⊔_)
open import Relation.Binary.PropositionalEquality
open import Relation.Binary
open import Data.Product

open import Axiom.FunctionExtensionality

open import Control.Category
open import Control.Category.Product

-- Category SET

Functions : Set → Set → Setoid _ _
Functions A B = setoid (A → B)

setIsCategory : IsCategory Functions
setIsCategory = record
  { ops = record
    { id  = λ x → x
    ; _⟫_ = λ f g x → g (f x)
    }
  ; laws = record
    { id-first = refl
    ; id-last  = refl
    ; ∘-assoc  = λ f → refl
    ; ∘-cong   = λ{ refl refl → refl }
    }
  }

SET : Category _ _ _
SET = record { Hom = Functions; isCategory = setIsCategory }

{-
timesProductStructure : (A B : Set) → ProductStructure SET A B
timesProductStructure A B = record
  { A×B = A × B
  ; fst = proj₁
  ; snd = proj₂
  }

⟨_,_⟩ :  {A B C : Set} (f : C → A) (g : C → B) → C → A × B
⟨ f , g ⟩ = λ x → f x , g x

pairIsPair : {A B C : Set} (f : C → A) (g : C → B) →
  IsPair (timesProductStructure A B) f g ⟨ f , g ⟩
pairIsPair f g = record
  { β-fst = refl
  ; β-snd = refl
  }

open IsPair

pairPair : {A B C : Set} (f : C → A) (g : C → B) →
  Pair (timesProductStructure A B) f g
pairPair f g = record
  { ⟨f,g⟩  = ⟨ f , g ⟩
  ; isPair = pairIsPair f g
  ; unique =  λ p → fun-ext (λ x → cong₂ (λ f g → f x , g x) (β-fst p) (β-snd p))
  }

timesIsProduct : (A B : Set) → Product SET A B
timesIsProduct A B = record
  { productStructure = timesProductStructure A B
  ; isProduct = record { pair = pairPair }
  }

-- -}


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

open import lib.Basics

module lib.types.FreeGroup where

module _ {i} where

  private
    data #FreeGroup-aux (A : Type i) : Type i where
      #fg-nil : #FreeGroup-aux A
      _#fg::_ : A → #FreeGroup-aux A → #FreeGroup-aux A
      _#fg-inv::_ : A → #FreeGroup-aux A → #FreeGroup-aux A

    {- Is this okay?  The template is to have 'data' here,
     - but then the β rules for paths will not typecheck.
     -}
    record #FreeGroup (A : Type i) : Type i where
      constructor
        #freegroup
      field
        #freegroup-proj : #FreeGroup-aux A
        #freegroup-unit : Unit → Unit

  FreeGroup : (A : Type i) → Type i
  FreeGroup A = #FreeGroup A

  module _ {A : Type i} where

    fg-nil : FreeGroup A
    fg-nil = #freegroup #fg-nil _

    infixr 60 _fg::_
    _fg::_ : A → FreeGroup A → FreeGroup A
    a fg:: (#freegroup fg _) = #freegroup (a #fg:: fg) _

    infixr 60 _fg-inv::_
    _fg-inv::_ : A → FreeGroup A → FreeGroup A
    a fg-inv:: (#freegroup fg _) = #freegroup (a #fg-inv:: fg) _

    postulate  -- HIT
      fg-inv-l : ∀ (a : A) → (fg : FreeGroup A) → a fg-inv:: a     fg:: fg == fg
      fg-inv-r : ∀ (a : A) → (fg : FreeGroup A) → a fg::     a fg-inv:: fg == fg

    postulate  -- HIT
      FreeGroup-level : is-set (FreeGroup A)

    FreeGroup-is-set = FreeGroup-level

    module FreeGroupElim {j} {P : FreeGroup A → Type j}
      (p : (x : FreeGroup A) → is-set (P x)) (nil* : P fg-nil)
      (_fg::*_ : ∀ a {fg} (fg* : P fg) → P (a fg:: fg))
      (_fg-inv::*_ : ∀ a {fg} (fg* : P fg) → P (a fg-inv:: fg))
      (fg-inv-l* : ∀ a {fg} (fg* : P fg) → (a fg-inv::* (a     fg::* fg*)) == fg* [ P ↓ fg-inv-l a fg ] )
      (fg-inv-r* : ∀ a {fg} (fg* : P fg) → (a fg::*     (a fg-inv::* fg*)) == fg* [ P ↓ fg-inv-r a fg ]) where

      f : Π (FreeGroup A) P
      f = f-aux phantom phantom phantom where

        f-aux* : Π (#FreeGroup-aux A) (P ∘ (λ fg → #freegroup fg _))
        f-aux* #fg-nil = nil*
        f-aux* (a #fg:: fg) = a fg::* f-aux* fg
        f-aux* (a #fg-inv:: fg) = a fg-inv::* f-aux* fg

        f-aux : Phantom p → Phantom fg-inv-l* → Phantom fg-inv-r*
          → Π (FreeGroup A) P
        f-aux phantom phantom phantom (#freegroup fg _) = f-aux* fg

      postulate  -- HIT
        fg-inv-l-β : ∀ a fg → apd f (fg-inv-l a fg) == fg-inv-l* a (f fg)
        fg-inv-r-β : ∀ a fg → apd f (fg-inv-r a fg) == fg-inv-r* a (f fg)

open FreeGroupElim public renaming (f to FreeGroup-elim)

module FreeGroupRec {i} {A : Type i} {j} {B : Type j} (p : is-set B)
  (nil* : B) (_fg::*[_]_ : A → FreeGroup A → B → B) (_fg-inv::*[_]_ : A → FreeGroup A → B → B)
  (fg-inv-l* : ∀ a {fg} fg* → (a fg-inv::*[ a     fg:: fg ] (a     fg::*[ fg ] fg*)) == fg*)
  (fg-inv-r* : ∀ a {fg} fg* → (a     fg::*[ a fg-inv:: fg ] (a fg-inv::*[ fg ] fg*)) == fg*) where

  private
    module M = FreeGroupElim (λ x → p) nil*
      (λ a {fg} fg* → a     fg::*[ fg ] fg*)
      (λ a {fg} fg* → a fg-inv::*[ fg ] fg*)
      (λ a {fg} fg* → ↓-cst-in (fg-inv-l* a {fg} fg*))
      (λ a {fg} fg* → ↓-cst-in (fg-inv-r* a {fg} fg*))

  f : FreeGroup A → B
  f = M.f

open FreeGroupRec public renaming (f to FreeGroup-rec)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Integers
------------------------------------------------------------------------

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

module Data.Integer where

import Data.Nat.Show as ℕ
open import Data.Sign as Sign using (Sign)
open import Data.String.Base using (String; _++_)

------------------------------------------------------------------------
-- Integers, basic types and operations

open import Data.Integer.Base public

------------------------------------------------------------------------
-- Re-export queries from the properties modules

open import Data.Integer.Properties public
  using (_≟_; _≤?_)

------------------------------------------------------------------------
-- Conversions

show : ℤ → String
show i = showSign (sign i) ++ ℕ.show ∣ i ∣
  where
  showSign : Sign → String
  showSign Sign.- = "-"
  showSign Sign.+ = ""

------------------------------------------------------------------------
-- Deprecated

-- Version 0.17

open import Data.Integer.Properties public
  using (◃-cong; drop‿+≤+; drop‿-≤-)
  renaming (◃-inverse to ◃-left-inverse)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Finite sets, based on AVL trees
------------------------------------------------------------------------

open import Relation.Binary
open import Relation.Binary.PropositionalEquality using (_≡_)

module Data.AVL.Sets
  {k ℓ} {Key : Set k} {_<_ : Rel Key ℓ}
  (isStrictTotalOrder : IsStrictTotalOrder _≡_ _<_)
  where

import Data.AVL as AVL
open import Data.Bool
open import Data.List as List using (List)
open import Data.Maybe as Maybe
open import Data.Product as Prod using (_×_; _,_; proj₁)
open import Data.Unit
open import Function
open import Level

-- The set type. (Note that Set is a reserved word.)

private
  open module S = AVL (const ⊤) isStrictTotalOrder
    public using () renaming (Tree to ⟨Set⟩)

-- Repackaged functions.

empty : ⟨Set⟩
empty = S.empty

singleton : Key → ⟨Set⟩
singleton k = S.singleton k _

insert : Key → ⟨Set⟩ → ⟨Set⟩
insert k = S.insert k _

delete : Key → ⟨Set⟩ → ⟨Set⟩
delete = S.delete

_∈?_ : Key → ⟨Set⟩ → Bool
_∈?_ = S._∈?_

headTail : ⟨Set⟩ → Maybe (Key × ⟨Set⟩)
headTail s = Maybe.map (Prod.map proj₁ id) (S.headTail s)

initLast : ⟨Set⟩ → Maybe (⟨Set⟩ × Key)
initLast s = Maybe.map (Prod.map id proj₁) (S.initLast s)

fromList : List Key → ⟨Set⟩
fromList = S.fromList ∘ List.map (λ k → (k , _))

toList : ⟨Set⟩ → List Key
toList = List.map proj₁ ∘ S.toList


------------------------------------------------------------------------
-- Coinductive lists
------------------------------------------------------------------------

module Data.Colist where

open import Coinduction
open import Data.Bool          using (Bool; true; false)
open import Data.Maybe         using (Maybe; nothing; just)
open import Data.Nat           using (ℕ; zero; suc)
open import Data.Conat
open import Data.List          using (List; []; _∷_)
open import Data.List.NonEmpty using (List⁺; _∷_)
                               renaming ([_] to [_]⁺)
open import Data.BoundedVec.Inefficient as BVec
  using (BoundedVec; []; _∷_)
open import Data.Product using (_,_)
open import Data.Function
open import Relation.Binary

------------------------------------------------------------------------
-- The type

infixr 5 _∷_

data Colist (A : Set) : Set where
  []  : Colist A
  _∷_ : (x : A) (xs : ∞ (Colist A)) → Colist A

data Any {A} (P : A → Set) : Colist A → Set where
  here  : ∀ {x xs} (px  : P x)          → Any P (x ∷ xs)
  there : ∀ {x xs} (pxs : Any P (♭ xs)) → Any P (x ∷ xs)

data All {A} (P : A → Set) : Colist A → Set where
  []  : All P []
  _∷_ : ∀ {x xs} (px : P x) (pxs : ∞ (All P (♭ xs))) → All P (x ∷ xs)

------------------------------------------------------------------------
-- Some operations

null : ∀ {A} → Colist A → Bool
null []      = true
null (_ ∷ _) = false

length : ∀ {A} → Colist A → Coℕ
length []       = zero
length (x ∷ xs) = suc (♯ length (♭ xs))

map : ∀ {A B} → (A → B) → Colist A → Colist B
map f []       = []
map f (x ∷ xs) = f x ∷ ♯ map f (♭ xs)

fromList : ∀ {A} → List A → Colist A
fromList []       = []
fromList (x ∷ xs) = x ∷ ♯ fromList xs

take : ∀ {A} (n : ℕ) → Colist A → BoundedVec A n
take zero    xs       = []
take (suc n) []       = []
take (suc n) (x ∷ xs) = x ∷ take n (♭ xs)

replicate : ∀ {A} → Coℕ → A → Colist A
replicate zero    x = []
replicate (suc n) x = x ∷ ♯ replicate (♭ n) x

lookup : ∀ {A} → ℕ → Colist A → Maybe A
lookup n       []       = nothing
lookup zero    (x ∷ xs) = just x
lookup (suc n) (x ∷ xs) = lookup n (♭ xs)

infixr 5 _++_

_++_ : ∀ {A} → Colist A → Colist A → Colist A
[]       ++ ys = ys
(x ∷ xs) ++ ys = x ∷ ♯ (♭ xs ++ ys)

concat : ∀ {A} → Colist (List⁺ A) → Colist A
concat []               = []
concat ([ x ]⁺   ∷ xss) = x ∷ ♯ concat (♭ xss)
concat ((x ∷ xs) ∷ xss) = x ∷ ♯ concat (xs ∷ xss)

[_] : ∀ {A} → A → Colist A
[ x ] = x ∷ ♯ []

------------------------------------------------------------------------
-- Equality and other relations

-- xs ≈ ys means that xs and ys are equal.

infix 4 _≈_

data _≈_ {A} : (xs ys : Colist A) → Set where
  []  :                                       []     ≈ []
  _∷_ : ∀ x {xs ys} (xs≈ : ∞ (♭ xs ≈ ♭ ys)) → x ∷ xs ≈ x ∷ ys

-- x ∈ xs means that x is a member of xs.

infix 4 _∈_

data _∈_ {A : Set} : A → Colist A → Set where
  here  : ∀ {x   xs}                   → x ∈ x ∷ xs
  there : ∀ {x y xs} (x∈xs : x ∈ ♭ xs) → x ∈ y ∷ xs

-- xs ⊑ ys means that xs is a prefix of ys.

infix 4 _⊑_

data _⊑_ {A : Set} : Colist A → Colist A → Set where
  []  : ∀ {ys}                            → []     ⊑ ys
  _∷_ : ∀ x {xs ys} (p : ∞ (♭ xs ⊑ ♭ ys)) → x ∷ xs ⊑ x ∷ ys

------------------------------------------------------------------------
-- Some proofs

setoid : Set → Setoid
setoid A = record
  { carrier       = Colist A
  ; _≈_           = _≈_
  ; isEquivalence = record
    { refl  = refl
    ; sym   = sym
    ; trans = trans
    }
  }
  where
  refl : Reflexive _≈_
  refl {[]}     = []
  refl {x ∷ xs} = x ∷ ♯ refl

  sym : Symmetric _≈_
  sym []        = []
  sym (x ∷ xs≈) = x ∷ ♯ sym (♭ xs≈)

  trans : Transitive _≈_
  trans []        []         = []
  trans (x ∷ xs≈) (.x ∷ ys≈) = x ∷ ♯ trans (♭ xs≈) (♭ ys≈)

poset : Set → Poset
poset A = record
  { carrier        = Colist A
  ; _≈_            = _≈_
  ; _≤_            = _⊑_
  ; isPartialOrder = record
    { isPreorder = record
      { isEquivalence = Setoid.isEquivalence (setoid A)
      ; reflexive     = reflexive
      ; trans         = trans
      ; ∼-resp-≈      = ((λ {_} → ⊑-resp-≈ˡ) , λ {_} → ⊑-resp-≈ʳ)
      }
    ; antisym  = antisym
    }
  }
  where
  reflexive : _≈_ ⇒ _⊑_
  reflexive []        = []
  reflexive (x ∷ xs≈) = x ∷ ♯ reflexive (♭ xs≈)

  trans : Transitive _⊑_
  trans []        _          = []
  trans (x ∷ xs≈) (.x ∷ ys≈) = x ∷ ♯ trans (♭ xs≈) (♭ ys≈)

  ⊑-resp-≈ˡ : {xs : Colist A} →  (λ ys → xs ⊑ ys) Respects _≈_
  ⊑-resp-≈ˡ _         []       = []
  ⊑-resp-≈ˡ (x ∷ xs≈) (.x ∷ p) = x ∷ ♯ ⊑-resp-≈ˡ (♭ xs≈) (♭ p)

  ⊑-resp-≈ʳ : {ys : Colist A} →  (λ xs → xs ⊑ ys) Respects _≈_
  ⊑-resp-≈ʳ []        _        = []
  ⊑-resp-≈ʳ (x ∷ xs≈) (.x ∷ p) = x ∷ ♯ ⊑-resp-≈ʳ (♭ xs≈) (♭ p)

  antisym : Antisymmetric _≈_ _⊑_
  antisym []       []        = []
  antisym (x ∷ p₁) (.x ∷ p₂) = x ∷ ♯ antisym (♭ p₁) (♭ p₂)

map-cong : ∀ {A B} (f : A → B) → _≈_ =[ map f ]⇒ _≈_
map-cong f []        = []
map-cong f (x ∷ xs≈) = f x ∷ ♯ map-cong f (♭ xs≈)

take-⊑ : ∀ {A} n (xs : Colist A) →
         let toColist = fromList ∘ BVec.toList in
         toColist (take n xs) ⊑ xs
take-⊑ zero    xs       = []
take-⊑ (suc n) []       = []
take-⊑ (suc n) (x ∷ xs) = x ∷ ♯ take-⊑ n (♭ xs)


open import Agda.Primitive
open import Agda.Builtin.Equality   renaming (_≡_ to _==_) --(( If I want to rename the built-in equality ))

-- What follows is an attempt to formalize multicategories. For this purpose, we also need to define lists, since the source of a multimap in a multicategory is a list.
-- For the moment, I do not try to prove that the lists an the associated concatenation form a monoid (because I do not know if this is useful or not).

module MultiCategories where

  -- ** Function extensionality **
  postulate
    funext : ∀ {X : Set} {Y : X → Set} {f g : ∀ (x : X) → (Y x)} → (∀ (x : X) → ((f x) == (g x))) → (f == g)

  -- ** Lists **

  -- We first define lists
  data List {l : Level} (A : Set l) : Set l where
    [] : List A
    _::_ : A → List A → List A

  infixr 30 _::_

  open List

  -- We define equality on lists, that extends the built-in equality (since for the moment, we dont need other definitions of equality → but maybe we could do something more general by asking the "equality" as a parameter ?)
  data _≡ᴸ_ {l : Level} {A : Set l} : (u v : List {l} A) → Set (lsuc l) where
    eq-nil : [] ≡ᴸ []
    eq-cons : ∀ (x y : A) (su sv : List A) → (x == y) → (su ≡ᴸ sv) → ( (x :: su) ≡ᴸ (y :: sv))

  -- We define the mapping of lists
  list-map : ∀ {l : Level} {A B : Set l} → (f : A → B) → List A → List B
  list-map f [] = []
  list-map f (x :: u) = ((f x) :: (list-map f u))

  -- We define the concatenation of lists
  _++_ : ∀ {l}{A : Set l} → List A → List A → List A
  [] ++ ys = ys
  (x :: xs) ++ ys = x :: (xs ++ ys)

  -- We define a fold function
  fold : ∀ {l : Level} {A B : Set l} → (A → B → B) → B → List A → B
  fold _⊗_ e [] =  e
  fold _⊗_ e (x :: xs)  =  x ⊗ (fold _⊗_ e xs)

  -- We define a flatten function
  flatten : ∀ {l : Level} {A : Set l} → (u : List ( List A)) → (List A)
  flatten [] = []
  flatten (x :: xs) = x ++ (flatten xs)
  --  (I wanted to do this with the above fold function, which would be more elegant, but I don't know why, I miserably failed at it - must be tired)

  -- We define a function that takes a list of functions and a list of arguments and apply the functions point to point
  list-apply : ∀ {l : Level} {A B : Set l} → (list-f : List (A → B)) → (list-arg : List A) → List B
  list-apply [] [] = []
  list-apply (f :: fs) [] = []
  list-apply [] (x :: xs) = []
  list-apply (f :: fs) (x :: xs) = (f x) :: (list-apply fs xs)
  -- The two cases in the middle should be forbidden, but I don't know how to do this

  -- ** Multicategories **

  -- -- We first define the multimaps on a set
  -- data Multimap {l : Level} {O : Setl l} : Set l where
  --   map : ()

  record MultiCategory {l : Level} : Set (lsuc l) where
    field
      -- Objects and maps
      object : Set l
      multimap : Set l
      sources : ∀ (m : multimap) → List object
      target : ∀ (m : multimap) → object
      -- Composition and associated equations / conditions
      _∘_ : ∀ (f : multimap) → (list : List multimap) → multimap
      plug : ∀ {g : multimap} → {f : multimap} → {list : List multimap} → {g == (f ∘ list)} →  (sources f) ≡ᴸ (list-map target list)
      dom-comp : ∀ {f : multimap} → {list : List multimap} → ((flatten (list-map sources list)) ≡ᴸ (sources (f ∘ list)))
      comp-codom : ∀ (f : multimap) → (list : List multimap) → (target f == target (f ∘ list))
      -- Identities and associated equations / conditions
      id : ∀ (o : object) → multimap
      id-dom-codom : ∀ (o : object) → (sources (id o) == o :: [] )
      id-codom :  ∀ (o : object) → (target (id o) == o)
      id-left : ∀ {o : object} {f : multimap} → {f == id o} → (list : List multimap) → ( (f ∘ list) == f)
      id-rigth : ∀ {f : multimap} {list : List multimap} → {list ≡ᴸ (list-map id (sources f))} → ((f ∘ list) == f)
      -- Associativity
      assoc : ∀ {f : multimap} {list-g : List multimap} {list-h : List (List multimap)} → (f ∘ (list-apply (list-map _∘_ list-g) list-h)) == ( (f ∘ list-g) ∘ (flatten list-h))

  open MultiCategory

  -- List over a list
  data ListOver {l : Level} {A : Set l} (B : A → Set l) : List A → Set l where
    [[]] : ListOver B []
    _:::_ : ∀ {x xs} → (y : B x) → (ys : ListOver B xs) → ListOver B (x :: xs)

  infixr 25 _:::_

  over-map : ∀ {l : Level} {A : Set l} {B : A → Set l} {xs} {C : Set l} → (∀ {x} → B x → C) → ListOver B xs → List C
  over-map f [[]] = []
  over-map f (y ::: ys) = f y :: over-map f ys

  over-over-map : ∀ {l : Level} {A : Set l} {B : A → Set l} {xs} {C : A → Set l} → (∀ {x} → B x → C x) → ListOver B xs → ListOver C xs
  over-over-map f [[]] = [[]]
  over-over-map f (y ::: ys) = f y ::: over-over-map f ys

  over-lift : ∀ {l : Level} {A : Set l} (list : List A) → ListOver (λ x → A) list
  over-lift [] = [[]]
  over-lift (y :: ys) = y ::: (over-lift ys)

  over-flatten : ∀ {l : Level} {A B : Set l} {list : List A} (list-ov : ListOver (λ x → List B) list) → List B
  over-flatten [[]] = []
  over-flatten (x ::: xs) = x ++ (over-flatten xs)

  -- Dependent sum
  record Σ {l} (A : Set l) (B : A → Set l) : Set l where
    constructor ⟨_,_⟩
    field
      π₁ : A
      π₂ : B π₁

  open Σ

  -- Shortcuts to map the projections on lists when the dependent sum is a "product"
  list-π₁ : ∀ {l : Level} {A : Set l} {B} (list : List ( Σ {l} A B)) → List A
  list-π₁ list = list-map π₁ list

  list-π₂ : ∀ {l : Level} {A C : Set l} (list : List ( Σ {l} A ( λ x → C))) → List C
  list-π₂ list = list-map π₂ list

  -- A more dependent attempt at multicategories
  record MultiCategory2 {l : Level} : Set (lsuc l) where
    field
      object : Set l
      multimap : List object → object → Set l
      𝟙 : ∀ {x} → multimap (x :: []) x
      _•_ : ∀ {ys x} → multimap ys x → ∀ (gs : ListOver (λ y → Σ (List object) (λ zs → multimap zs y)) ys) →
            multimap (flatten (over-map π₁ gs)) x
      -- Another attempt to define multimaps, "putting the dependance elsewhere"
      _●_ : ∀ {x : object} {ys : List (Σ (List object) (λ x → object))} → multimap (list-π₂ ys) x → ListOver (λ y → multimap (π₁ y) (π₂ y)) ys → multimap (flatten (list-π₁ ys)) x
      -- here complications start
      -- 𝟙-left : ∀ {ys x} → (f : multimap ys x) → 𝟙 • (⟨ ys , f ⟩ ::: [[]]) == f
      -- 𝟙-right : ∀ {ys x} → (f : multimap ys x) → f • (over-over-map ? (over-lift ys)) == f
      --(ListOver ( λ y → ⟨ x ::: [[]] , 𝟙 ⟩ ) ys) == f
      -- Attempt with the alternative composition
      𝟙-left-● : ∀ {x ys} → (f : multimap ys x) → 𝟙 ● (f ::: [[]]) == f -- Here it seems that we have a lemma to prove to say that ys = (ys ++ flatten (list-map π₁ [])). Do we do it locally or would it be useful to have a more genral lemma ?

-- Here I tried to fix 𝟙-left and to define 𝟙-right, but I did not manage to do it, it both cases. Maybe we should revise the definition of _•_ ?
-- Agda seems to struggle with the fact that thnigs that should be equal are not equal by definition (conversion/reduction problems). Maybe there are some lemmas to prove here.
-- Also, I do not understand why we use the "over-map" : it would feel more natural to me if, once we lift a list to a dependent one, and use dependant lists, we only use dependeant lists, that's why I defined over-lift and over-over-map. (I also defined an "over-flatten" but don't know if it's useful)


module Ex3Lec where

----------------------------------------------------------------------------
-- EXERCISE 3 -- MONADS FOR HUTTON'S RAZOR
--
-- VALUE:     15%
-- DEADLINE:  5pm, Friday 20 November (week 9)
--
-- DON'T SUBMIT, COMMIT!
--
-- The purpose of this exercise is to introduce you to some useful
-- mathematical structures and build good tools for working with
-- vectors
----------------------------------------------------------------------------

open import CS410-Prelude
open import CS410-Monoid
open import CS410-Nat
open import CS410-Vec
open import CS410-Functor

-- HINT: your tasks are heralded with the eminently searchable tag, "???"

----------------------------------------------------------------------------
-- HUTTON'S RAZOR
----------------------------------------------------------------------------

HVal : Set   -- the set of *values* for Hutton's Razor
HVal = Two + Nat   -- Booleans or natural numbers

foo : HVal
foo = tt , ff

goo : HVal
goo = ff , 42

data HExp (X : Set) : Set where
  var        : X -> HExp X                  -- variables
  val        : HVal -> HExp X               -- values
  _+H_ _>=H_ : (e1 e2 : HExp X) -> HExp X   -- addition, comparison
  ifH_then_else_ : (e1 e2 e3 : HExp X) -> HExp X  -- conditional

_>=2_ : Nat -> Nat -> Two
x      >=2  zero   = tt
zero   >=2  suc _  = ff
suc m  >=2  suc n  = m >=2 n

----------------------------------------------------------------------------
-- ??? 3.1 the HExp syntax-with-substitution monad            (score: ? / 2)
----------------------------------------------------------------------------

-- Show that HExp is a monad, where the "bind" operation performs
-- simultaneous substitution (transforming all the variables in a term).

hExpMonad : Monad HExp
hExpMonad = record { return = {!!} ; _>>=_ = {!!} }

----------------------------------------------------------------------------
-- ??? 3.2 the error management monad                         (score: ? / 1)
----------------------------------------------------------------------------

-- show that "+ E" is monadic, generalising the "Maybe" monad by allowing
-- some sort of error report

errorMonad : (E : Set) -> Monad \ V -> V + E   -- "value or error"
errorMonad E = {!!}

----------------------------------------------------------------------------
-- ??? 3.3 the environment monad transformer                   (score: ? / 1)
----------------------------------------------------------------------------

-- show that "+ E" is monadic, generalising the "Maybe" monad by allowing
-- some sort of error report

envMonad : (G : Set){M : Set -> Set} -> Monad M ->
           Monad \ V -> G -> M V      -- "computation in an environment"
envMonad G MM = {!!} where
  open Monad MM

----------------------------------------------------------------------------
-- ??? 3.4 interpreting Hutton's Razor                        (score: ? / 3)
----------------------------------------------------------------------------

-- Implement an interpreter for Hutton's Razor.
-- You will need to construct a type to represent possible run-time errors.
-- Ensure that addition and comparison act on numbers, not Booleans.
-- Ensure that the condition in an "if" is a Boolean, not a number.

data InterpretError : Set where

-- helpful things to build

Env : Set -> Set    -- an environment for a given set of variables
Env X = X -> HVal

Compute : Set{- variables -} -> Set{- values -} -> Set
Compute X V = Env X -> V + InterpretError  -- how to compute a V

computeMonad : {X : Set} -> Monad (Compute X)
computeMonad = {!!} -- build this from the above parts

-- This operation should explain how to get the value of a variable
-- from the environment.
varVal : {X : Set} -> X -> Compute X HVal
varVal x = {!!}

-- These operations should ensure that you get the sort of value
-- that you want, in order to ensure that you don't do bogus
-- computation.
mustBeNat : {X : Set} -> HVal -> Compute X Nat
mustBeNat v = {!!}

mustBeTwo : {X : Set} -> HVal -> Compute X Two
mustBeTwo v = {!!}

-- Now, you're ready to go. Don't introduce the environent explicitly.
-- Use the monad to thread it.

interpret : {X : Set} -> HExp X -> Compute X HVal
interpret {X} = go where
  open Monad (envMonad (Env X) (errorMonad InterpretError))
  go : HExp X -> Env X -> HVal + InterpretError
  go t = {!!}


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

open import HoTT
open import homotopy.PtdAdjoint
open import homotopy.SuspAdjointLoop
open import cohomology.WithCoefficients

module cohomology.SuspAdjointLoopIso where

module SuspAdjointLoopIso {i} where

  private
    hadj : HomAdjoint {i} {i} Σ⊣Ω.SuspFunctor Σ⊣Ω.LoopFunctor
    hadj = counit-unit-to-hom Σ⊣Ω.adj

    module A = HomAdjoint hadj

  module _ (X Y : Ptd i) where

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

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

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

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

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

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