------------------------------------------------------------------------
-- The Agda standard library
--
-- Some Vec-related properties
------------------------------------------------------------------------

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

module Data.Vec.Properties where

open import Algebra.FunctionProperties
open import Data.Empty using (⊥-elim)
open import Data.Fin as Fin using (Fin; zero; suc; toℕ; fromℕ)
open import Data.Fin.Properties using (_+′_)
open import Data.List.Base as List using (List)
open import Data.Nat
open import Data.Nat.Properties using (+-assoc; ≤-step)
open import Data.Product as Prod
  using (_×_; _,_; proj₁; proj₂; <_,_>; uncurry)
open import Data.Vec
open import Function
open import Function.Inverse using (_↔_; inverse)
open import Relation.Binary as B hiding (Decidable)
open import Relation.Binary.PropositionalEquality as P
  using (_≡_; _≢_; refl; _≗_)
open import Relation.Unary using (Pred; Decidable)
open import Relation.Nullary using (yes; no)

------------------------------------------------------------------------
-- Properties of propositional equality over vectors

module _ {a} {A : Set a} {n} {x y : A} {xs ys : Vec A n} where

 ∷-injectiveˡ : x ∷ xs ≡ y ∷ ys → x ≡ y
 ∷-injectiveˡ refl = refl

 ∷-injectiveʳ : x ∷ xs ≡ y ∷ ys → xs ≡ ys
 ∷-injectiveʳ refl = refl

 ∷-injective : (x ∷ xs) ≡ (y ∷ ys) → x ≡ y × xs ≡ ys
 ∷-injective refl = refl , refl

module _ {a} {A : Set a} where

  ≡-dec : B.Decidable _≡_ → ∀ {n} → B.Decidable {A = Vec A n} _≡_
  ≡-dec _≟_ []       []       = yes refl
  ≡-dec _≟_ (x ∷ xs) (y ∷ ys) with x ≟ y | ≡-dec _≟_ xs ys
  ... | yes refl | yes refl = yes refl
  ... | no  x≢y  | _        = no (x≢y   ∘ ∷-injectiveˡ)
  ... | yes _    | no xs≢ys = no (xs≢ys ∘ ∷-injectiveʳ)

------------------------------------------------------------------------
-- _[_]=_

module _ {a} {A : Set a} where

  []=-injective : ∀ {n} {xs : Vec A n} {i x y} →
                  xs [ i ]= x → xs [ i ]= y → x ≡ y
  []=-injective here          here          = refl
  []=-injective (there xsᵢ≡x) (there xsᵢ≡y) = []=-injective xsᵢ≡x xsᵢ≡y

  -- See also Data.Vec.Properties.WithK.[]=-irrelevant.

------------------------------------------------------------------------
-- lookup

module _ {a} {A : Set a} where

  []=⇒lookup : ∀ {n} {x : A} {xs} {i : Fin n} →
               xs [ i ]= x → lookup xs i ≡ x
  []=⇒lookup here            = refl
  []=⇒lookup (there xs[i]=x) = []=⇒lookup xs[i]=x

  lookup⇒[]= : ∀ {n} (i : Fin n) {x : A} xs →
               lookup xs i ≡ x → xs [ i ]= x
  lookup⇒[]= zero    (_ ∷ _)  refl = here
  lookup⇒[]= (suc i) (_ ∷ xs) p    = there (lookup⇒[]= i xs p)

  []=↔lookup : ∀ {n i} {x} {xs : Vec A n} →
               xs [ i ]= x ↔ lookup xs i ≡ x
  []=↔lookup {i = i} =
    inverse []=⇒lookup (lookup⇒[]= _ _)
            lookup⇒[]=∘[]=⇒lookup ([]=⇒lookup∘lookup⇒[]= _ i)
    where
    lookup⇒[]=∘[]=⇒lookup :
      ∀ {n x xs} {i : Fin n} (p : xs [ i ]= x) →
      lookup⇒[]= i xs ([]=⇒lookup p) ≡ p
    lookup⇒[]=∘[]=⇒lookup here      = refl
    lookup⇒[]=∘[]=⇒lookup (there p) =
      P.cong there (lookup⇒[]=∘[]=⇒lookup p)

    []=⇒lookup∘lookup⇒[]= :
      ∀ {n} xs (i : Fin n) {x} (p : lookup xs i ≡ x) →
      []=⇒lookup (lookup⇒[]= i xs p) ≡ p
    []=⇒lookup∘lookup⇒[]= (x ∷ xs) zero    refl = refl
    []=⇒lookup∘lookup⇒[]= (x ∷ xs) (suc i) p    =
      []=⇒lookup∘lookup⇒[]= xs i p

------------------------------------------------------------------------
-- updateAt (_[_]%=_)

module _ {a} {A : Set a} where

  -- (+) updateAt i actually updates the element at index i.

  updateAt-updates : ∀ {n} (i : Fin n) {f : A → A} (xs : Vec A n) {x : A} →
                     xs [ i ]= x → (updateAt i f xs) [ i ]= f x
  updateAt-updates zero    (x ∷ xs) here        = here
  updateAt-updates (suc i) (x ∷ xs) (there loc) = there (updateAt-updates i xs loc)

  -- (-) updateAt i does not touch the elements at other indices.

  updateAt-minimal : ∀ {n} (i j : Fin n) {f : A → A} {x : A} (xs : Vec A n) →
                     i ≢ j → xs [ i ]= x → (updateAt j f xs) [ i ]= x
  updateAt-minimal zero    zero    (x ∷ xs) 0≢0 here        = ⊥-elim (0≢0 refl)
  updateAt-minimal zero    (suc j) (x ∷ xs) _   here        = here
  updateAt-minimal (suc i) zero    (x ∷ xs) _   (there loc) = there loc
  updateAt-minimal (suc i) (suc j) (x ∷ xs) i≢j (there loc) =
    there (updateAt-minimal i j xs (i≢j ∘ P.cong suc) loc)

  -- The other properties are consequences of (+) and (-).
  -- We spell the most natural properties out.
  -- Direct inductive proofs are in most cases easier than just using
  -- the defining properties.

  -- In the explanations, we make use of shorthand  f = g ↾ x
  -- meaning that f and g agree at point x, i.e.  f x ≡ g x.

  -- updateAt i  is a morphism from the monoid of endofunctions  A → A
  -- to the monoid of endofunctions  Vec A n → Vec A n

  -- 1a. relative identity:  f = id ↾ (lookup xs i)
  --                implies  updateAt i f = id ↾ xs

  updateAt-id-relative : ∀ {n} (i : Fin n) (xs : Vec A n) {f : A → A} →
                         f (lookup xs i) ≡ lookup xs i →
                         updateAt i f xs ≡ xs
  updateAt-id-relative zero    (x ∷ xs) eq = P.cong (_∷ xs) eq
  updateAt-id-relative (suc i) (x ∷ xs) eq = P.cong (x ∷_) (updateAt-id-relative i xs eq)

  -- 1b. identity:  updateAt i id ≗ id

  updateAt-id : ∀ {n} (i : Fin n) (xs : Vec A n) →
                updateAt i id xs ≡ xs
  updateAt-id i xs = updateAt-id-relative i xs refl

  -- 2a. relative composition:  f ∘ g = h ↾ (lookup i xs)
  --                   implies  updateAt i f ∘ updateAt i g ≗ updateAt i h

  updateAt-compose-relative : ∀ {n} (i : Fin n) {f g h : A → A} (xs : Vec A n) →
                              f (g (lookup xs i)) ≡ h (lookup xs i) →
                              updateAt i f (updateAt i g xs) ≡ updateAt i h xs
  updateAt-compose-relative zero    (x ∷ xs) fg=h = P.cong (_∷ xs) fg=h
  updateAt-compose-relative (suc i) (x ∷ xs) fg=h =
    P.cong (x ∷_) (updateAt-compose-relative i xs fg=h)

  -- 2b. composition:  updateAt i f ∘ updateAt i g ≗ updateAt i (f ∘ g)

  updateAt-compose : ∀ {n} (i : Fin n) {f g : A → A} →
                     updateAt i f ∘ updateAt i g ≗ updateAt i (f ∘ g)
  updateAt-compose i xs = updateAt-compose-relative i xs refl

  -- 3. congruence:  updateAt i  is a congruence wrt. extensional equality.

  -- 3a.  If    f = g ↾ (lookup i xs)
  --      then  updateAt i f = updateAt i g ↾ xs

  updateAt-cong-relative : ∀ {n} (i : Fin n) {f g : A → A} (xs : Vec A n) →
                           f (lookup xs i) ≡ g (lookup xs i) →
                           updateAt i f xs ≡ updateAt i g xs
  updateAt-cong-relative zero    (x ∷ xs) f=g = P.cong (_∷ xs) f=g
  updateAt-cong-relative (suc i) (x ∷ xs) f=g = P.cong (x ∷_) (updateAt-cong-relative i xs f=g)

  -- 3b. congruence:  f ≗ g → updateAt i f ≗ updateAt i g

  updateAt-cong : ∀ {n} (i : Fin n) {f g : A → A} →
                  f ≗ g → updateAt i f ≗ updateAt i g
  updateAt-cong i f≗g xs = updateAt-cong-relative i xs (f≗g (lookup xs i))

  -- The order of updates at different indices i ≢ j does not matter.

  -- This a consequence of updateAt-updates and updateAt-minimal
  -- but easier to prove inductively.

  updateAt-commutes : ∀ {n} (i j : Fin n) {f g : A → A} → i ≢ j →
                      updateAt i f ∘ updateAt j g ≗ updateAt j g ∘ updateAt i f
  updateAt-commutes zero    zero    0≢0 (x ∷ xs) = ⊥-elim (0≢0 refl)
  updateAt-commutes zero    (suc j) i≢j (x ∷ xs) = refl
  updateAt-commutes (suc i) zero    i≢j (x ∷ xs) = refl
  updateAt-commutes (suc i) (suc j) i≢j (x ∷ xs) =
    P.cong (x ∷_) (updateAt-commutes i j (i≢j ∘ P.cong suc) xs)

  -- lookup after updateAt reduces.

  -- For same index this is an easy consequence of updateAt-updates
  -- using []=↔lookup.

  lookup∘updateAt : ∀ {n} (i : Fin n) {f : A → A} →
                    ∀ xs → lookup (updateAt i f xs) i ≡ f (lookup xs i)
  lookup∘updateAt i xs =
    []=⇒lookup (updateAt-updates i xs (lookup⇒[]= i _ refl))

  -- For different indices it easily follows from updateAt-minimal.

  lookup∘updateAt′ : ∀ {n} (i j : Fin n) {f : A → A} → i ≢ j →
                     ∀ xs → lookup (updateAt j f xs) i ≡ lookup xs i
  lookup∘updateAt′ i j xs i≢j =
    []=⇒lookup (updateAt-minimal i j i≢j xs (lookup⇒[]= i _ refl))

  -- Aliases for notation _[_]%=_

  []%=-id : ∀ {n} (xs : Vec A n) (i : Fin n) → xs [ i ]%= id ≡ xs
  []%=-id xs i = updateAt-id i xs

  []%=-compose : ∀ {n} (xs : Vec A n) (i : Fin n) {f g : A → A} →
       xs [ i ]%= f
          [ i ]%= g
     ≡ xs [ i ]%= g ∘ f
  []%=-compose xs i = updateAt-compose i xs

------------------------------------------------------------------------
-- _[_]≔_ (update)
--
-- _[_]≔_ is defined in terms of updateAt, and all of its properties
-- are special cases of the ones for updateAt.

module _ {a} {A : Set a} where

  []≔-idempotent : ∀ {n} (xs : Vec A n) (i : Fin n) {x₁ x₂ : A} →
                   (xs [ i ]≔ x₁) [ i ]≔ x₂ ≡ xs [ i ]≔ x₂
  []≔-idempotent xs i = updateAt-compose i xs

  []≔-commutes : ∀ {n} (xs : Vec A n) (i j : Fin n) {x y : A} → i ≢ j →
                 (xs [ i ]≔ x) [ j ]≔ y ≡ (xs [ j ]≔ y) [ i ]≔ x
  []≔-commutes xs i j i≢j = updateAt-commutes j i (i≢j ∘ P.sym) xs

  []≔-updates : ∀ {n} (xs : Vec A n) (i : Fin n) {x : A} →
                (xs [ i ]≔ x) [ i ]= x
  []≔-updates xs i = updateAt-updates i xs (lookup⇒[]= i xs refl)

  []≔-minimal : ∀ {n} (xs : Vec A n) (i j : Fin n) {x y : A} → i ≢ j →
                xs [ i ]= x → (xs [ j ]≔ y) [ i ]= x
  []≔-minimal xs i j i≢j loc = updateAt-minimal i j xs i≢j loc

  []≔-lookup : ∀ {n} (xs : Vec A n) (i : Fin n) →
               xs [ i ]≔ lookup xs i ≡ xs
  []≔-lookup xs i = updateAt-id-relative i xs refl

  lookup∘update : ∀ {n} (i : Fin n) (xs : Vec A n) x →
                  lookup (xs [ i ]≔ x) i ≡ x
  lookup∘update i xs x = lookup∘updateAt i xs

  lookup∘update′ : ∀ {n} {i j : Fin n} → i ≢ j → ∀ (xs : Vec A n) y →
                   lookup (xs [ j ]≔ y) i ≡ lookup xs i
  lookup∘update′ {n} {i} {j} i≢j xs y = lookup∘updateAt′ i j i≢j xs

------------------------------------------------------------------------
-- map

map-id : ∀ {a n} {A : Set a} → map {n = n} {A} id ≗ id
map-id []       = refl
map-id (x ∷ xs) = P.cong (x ∷_) (map-id xs)

map-cong : ∀ {a b n} {A : Set a} {B : Set b} {f g : A → B} →
           f ≗ g → map {n = n} f ≗ map g
map-cong f≗g []       = refl
map-cong f≗g (x ∷ xs) = P.cong₂ _∷_ (f≗g x) (map-cong f≗g xs)

map-∘ : ∀ {a b c n} {A : Set a} {B : Set b} {C : Set c}
        (f : B → C) (g : A → B) →
        map {n = n} (f ∘ g) ≗ map f ∘ map g
map-∘ f g []       = refl
map-∘ f g (x ∷ xs) = P.cong (f (g x) ∷_) (map-∘ f g xs)

lookup-map : ∀ {a b n} {A : Set a} {B : Set b}
             (i : Fin n) (f : A → B) (xs : Vec A n) →
             lookup (map f xs) i ≡ f (lookup xs i)
lookup-map zero    f (x ∷ xs) = refl
lookup-map (suc i) f (x ∷ xs) = lookup-map i f xs

map-updateAt : ∀ {n a b} {A : Set a} {B : Set b} →
  ∀ {f : A → B} {g : A → A} {h : B → B} (xs : Vec A n) (i : Fin n)
  → f (g (lookup xs i)) ≡ h (f (lookup xs i))
  → map f (updateAt i g xs) ≡ updateAt i h (map f xs)
map-updateAt (x ∷ xs) zero    eq = P.cong (_∷ _) eq
map-updateAt (x ∷ xs) (suc i) eq = P.cong (_ ∷_) (map-updateAt xs i eq)

map-[]≔ : ∀ {n a b} {A : Set a} {B : Set b}
          (f : A → B) (xs : Vec A n) (i : Fin n) {x : A} →
          map f (xs [ i ]≔ x) ≡ map f xs [ i ]≔ f x
map-[]≔ f xs i = map-updateAt xs i refl

------------------------------------------------------------------------
-- _++_

module _ {a} {A : Set a} {m} {ys ys' : Vec A m} where

  -- See also Data.Vec.Properties.WithK.++-assoc.

  ++-injectiveˡ : ∀ {n} (xs xs' : Vec A n) →
                  xs ++ ys ≡ xs' ++ ys' → xs ≡ xs'
  ++-injectiveˡ []       []         _  = refl
  ++-injectiveˡ (x ∷ xs) (x' ∷ xs') eq =
    P.cong₂ _∷_ (∷-injectiveˡ eq) (++-injectiveˡ _ _ (∷-injectiveʳ eq))

  ++-injectiveʳ : ∀ {n} (xs xs' : Vec A n) →
                  xs ++ ys ≡ xs' ++ ys' → ys ≡ ys'
  ++-injectiveʳ []       []         eq = eq
  ++-injectiveʳ (x ∷ xs) (x' ∷ xs') eq =
    ++-injectiveʳ xs xs' (∷-injectiveʳ eq)

  ++-injective  : ∀ {n} (xs xs' : Vec A n) →
                  xs ++ ys ≡ xs' ++ ys' → xs ≡ xs' × ys ≡ ys'
  ++-injective xs xs' eq =
    (++-injectiveˡ xs xs' eq , ++-injectiveʳ xs xs' eq)

module _ {a} {A : Set a} where

  lookup-++-< : ∀ {m n} (xs : Vec A m) (ys : Vec A n) →
                ∀ i (i<m : toℕ i < m) →
                lookup (xs ++ ys)i  ≡ lookup xs (Fin.fromℕ≤ i<m)
  lookup-++-< []       ys i       ()
  lookup-++-< (x ∷ xs) ys zero    (s≤s z≤n)       = refl
  lookup-++-< (x ∷ xs) ys (suc i) (s≤s (s≤s i<m)) =
    lookup-++-< xs ys i (s≤s i<m)

  lookup-++-≥ : ∀ {m n} (xs : Vec A m) (ys : Vec A n) →
                ∀ i (i≥m : toℕ i ≥ m) →
                lookup (xs ++ ys) i ≡ lookup ys (Fin.reduce≥ i i≥m)
  lookup-++-≥ []       ys i       i≥m       = refl
  lookup-++-≥ (x ∷ xs) ys zero    ()
  lookup-++-≥ (x ∷ xs) ys (suc i) (s≤s i≥m) = lookup-++-≥ xs ys i i≥m

  lookup-++-inject+ : ∀ {m n} (xs : Vec A m) (ys : Vec A n) i →
                      lookup (xs ++ ys) (Fin.inject+ n i) ≡ lookup xs i
  lookup-++-inject+ []       ys ()
  lookup-++-inject+ (x ∷ xs) ys zero    = refl
  lookup-++-inject+ (x ∷ xs) ys (suc i) = lookup-++-inject+ xs ys i

  lookup-++-+′ : ∀ {m n} (xs : Vec A m) (ys : Vec A n) i →
                 lookup (xs ++ ys) (fromℕ m +′ i) ≡ lookup ys i
  lookup-++-+′ []       ys       zero    = refl
  lookup-++-+′ []       (y ∷ xs) (suc i) = lookup-++-+′ [] xs i
  lookup-++-+′ (x ∷ xs) ys       i       = lookup-++-+′ xs ys i

  []≔-++-inject+ : ∀ {m n x} (xs : Vec A m) (ys : Vec A n) i →
                   (xs ++ ys) [ Fin.inject+ n i ]≔ x ≡ (xs [ i ]≔ x) ++ ys
  []≔-++-inject+ []       ys ()
  []≔-++-inject+ (x ∷ xs) ys zero    = refl
  []≔-++-inject+ (x ∷ xs) ys (suc i) =
    P.cong (x ∷_) $ []≔-++-inject+ xs ys i

------------------------------------------------------------------------
-- zipWith

module _ {a} {A : Set a} {f : A → A → A} where

  zipWith-assoc : Associative _≡_ f → ∀ {n} →
                  Associative _≡_ (zipWith {n = n} f)
  zipWith-assoc assoc []       []       []       = refl
  zipWith-assoc assoc (x ∷ xs) (y ∷ ys) (z ∷ zs) =
    P.cong₂ _∷_ (assoc x y z) (zipWith-assoc assoc xs ys zs)

  zipWith-idem : Idempotent _≡_ f → ∀ {n} →
                 Idempotent _≡_ (zipWith {n = n} f)
  zipWith-idem idem []       = refl
  zipWith-idem idem (x ∷ xs) =
    P.cong₂ _∷_ (idem x) (zipWith-idem idem xs)

  zipWith-identityˡ : ∀ {1#} → LeftIdentity _≡_ 1# f → ∀ {n} →
                      LeftIdentity _≡_ (replicate 1#) (zipWith {n = n} f)
  zipWith-identityˡ idˡ []       = refl
  zipWith-identityˡ idˡ (x ∷ xs) =
    P.cong₂ _∷_ (idˡ x) (zipWith-identityˡ idˡ xs)

  zipWith-identityʳ : ∀ {1#} → RightIdentity _≡_ 1# f → ∀ {n} →
                      RightIdentity _≡_ (replicate 1#) (zipWith {n = n} f)
  zipWith-identityʳ idʳ []       = refl
  zipWith-identityʳ idʳ (x ∷ xs) =
    P.cong₂ _∷_ (idʳ x) (zipWith-identityʳ idʳ xs)

  zipWith-zeroˡ : ∀ {0#} → LeftZero _≡_ 0# f → ∀ {n} →
                  LeftZero _≡_ (replicate 0#) (zipWith {n = n} f)
  zipWith-zeroˡ zeˡ []       = refl
  zipWith-zeroˡ zeˡ (x ∷ xs) =
    P.cong₂ _∷_ (zeˡ x) (zipWith-zeroˡ zeˡ xs)

  zipWith-zeroʳ : ∀ {0#} → RightZero _≡_ 0# f → ∀ {n} →
                  RightZero _≡_ (replicate 0#) (zipWith {n = n} f)
  zipWith-zeroʳ zeʳ []       = refl
  zipWith-zeroʳ zeʳ (x ∷ xs) =
    P.cong₂ _∷_ (zeʳ x) (zipWith-zeroʳ zeʳ xs)

  zipWith-inverseˡ : ∀ {⁻¹ 0#} → LeftInverse _≡_ 0# ⁻¹ f → ∀ {n} →
                     LeftInverse _≡_ (replicate {n = n} 0#) (map ⁻¹) (zipWith f)
  zipWith-inverseˡ invˡ []       = refl
  zipWith-inverseˡ invˡ (x ∷ xs) =
    P.cong₂ _∷_ (invˡ x) (zipWith-inverseˡ invˡ xs)

  zipWith-inverseʳ : ∀ {⁻¹ 0#} → RightInverse _≡_ 0# ⁻¹ f → ∀ {n} →
                     RightInverse _≡_ (replicate {n = n} 0#) (map ⁻¹) (zipWith f)
  zipWith-inverseʳ invʳ []       = refl
  zipWith-inverseʳ invʳ (x ∷ xs) =
    P.cong₂ _∷_ (invʳ x) (zipWith-inverseʳ invʳ xs)

  zipWith-distribˡ : ∀ {g} → _DistributesOverˡ_ _≡_ f g → ∀ {n} →
                     _DistributesOverˡ_ _≡_ (zipWith {n = n} f) (zipWith g)
  zipWith-distribˡ distribˡ []        []      []       = refl
  zipWith-distribˡ distribˡ (x ∷ xs) (y ∷ ys) (z ∷ zs) =
    P.cong₂ _∷_ (distribˡ x y z) (zipWith-distribˡ distribˡ xs ys zs)

  zipWith-distribʳ : ∀ {g} → _DistributesOverʳ_ _≡_ f g → ∀ {n} →
                     _DistributesOverʳ_ _≡_ (zipWith {n = n} f) (zipWith g)
  zipWith-distribʳ distribʳ []        []      []       = refl
  zipWith-distribʳ distribʳ (x ∷ xs) (y ∷ ys) (z ∷ zs) =
    P.cong₂ _∷_ (distribʳ x y z) (zipWith-distribʳ distribʳ xs ys zs)

  zipWith-absorbs : ∀ {g} → _Absorbs_ _≡_ f g → ∀ {n} →
                   _Absorbs_ _≡_ (zipWith {n = n} f) (zipWith g)
  zipWith-absorbs abs []       []       = refl
  zipWith-absorbs abs (x ∷ xs) (y ∷ ys) =
    P.cong₂ _∷_ (abs x y) (zipWith-absorbs abs xs ys)

module _ {a b} {A : Set a} {B : Set b} {f : A → A → B} where

  zipWith-comm : (∀ x y → f x y ≡ f y x) → ∀ {n}
                 (xs ys : Vec A n) → zipWith f xs ys ≡ zipWith f ys xs
  zipWith-comm comm []       []       = refl
  zipWith-comm comm (x ∷ xs) (y ∷ ys) =
    P.cong₂ _∷_ (comm x y) (zipWith-comm comm xs ys)

module _ {a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} where

  zipWith-map₁ : ∀ {n} (_⊕_ : B → C → D) (f : A → B)
                 (xs : Vec A n) (ys : Vec C n) →
                 zipWith _⊕_ (map f xs) ys ≡ zipWith (λ x y → f x ⊕ y) xs ys
  zipWith-map₁ _⊕_ f []       []       = refl
  zipWith-map₁ _⊕_ f (x ∷ xs) (y ∷ ys) =
    P.cong (f x ⊕ y ∷_) (zipWith-map₁ _⊕_ f xs ys)

  zipWith-map₂ : ∀ {n} (_⊕_ : A → C → D) (f : B → C)
                 (xs : Vec A n) (ys : Vec B n) →
                 zipWith _⊕_ xs (map f ys) ≡ zipWith (λ x y → x ⊕ f y) xs ys
  zipWith-map₂ _⊕_ f []       []       = refl
  zipWith-map₂ _⊕_ f (x ∷ xs) (y ∷ ys) =
    P.cong (x ⊕ f y ∷_) (zipWith-map₂ _⊕_ f xs ys)

module _ {a b c} {A : Set a} {B : Set b} {C : Set c} where

  lookup-zipWith : ∀ (f : A → B → C) {n} (i : Fin n) xs ys →
                   lookup (zipWith f xs ys) i ≡ f (lookup xs i) (lookup ys i)
  lookup-zipWith _ zero    (x ∷ _)  (y ∷ _)   = refl
  lookup-zipWith _ (suc i) (_ ∷ xs) (_ ∷ ys)  = lookup-zipWith _ i xs ys

------------------------------------------------------------------------
-- zip

module _ {a b} {A : Set a} {B : Set b} where

  lookup-zip : ∀ {n} (i : Fin n) (xs : Vec A n) (ys : Vec B n) →
               lookup (zip xs ys) i ≡ (lookup xs i , lookup ys i)
  lookup-zip = lookup-zipWith _,_

  -- map lifts projections to vectors of products.

  map-proj₁-zip : ∀ {n} (xs : Vec A n) (ys : Vec B n) →
                  map proj₁ (zip xs ys) ≡ xs
  map-proj₁-zip []       []       = refl
  map-proj₁-zip (x ∷ xs) (y ∷ ys) = P.cong (x ∷_) (map-proj₁-zip xs ys)

  map-proj₂-zip : ∀ {n} (xs : Vec A n) (ys : Vec B n) →
                  map proj₂ (zip xs ys) ≡ ys
  map-proj₂-zip []       []       = refl
  map-proj₂-zip (x ∷ xs) (y ∷ ys) = P.cong (y ∷_) (map-proj₂-zip xs ys)

-- map lifts pairing to vectors of products.

map-<,>-zip : ∀ {a b c n} {A : Set a} {B : Set b} {C : Set c}
              (f : A → B) (g : A → C) (xs : Vec A n) →
              map < f , g > xs ≡ zip (map f xs) (map g xs)
map-<,>-zip f g []       = P.refl
map-<,>-zip f g (x ∷ xs) = P.cong (_ ∷_) (map-<,>-zip f g xs)

map-zip : ∀ {a b c d n} {A : Set a} {B : Set b} {C : Set c} {D : Set d}
          (f : A → B) (g : C → D) (xs : Vec A n) (ys : Vec C n) →
          map (Prod.map f g) (zip xs ys) ≡ zip (map f xs) (map g ys)
map-zip f g []       []       = refl
map-zip f g (x ∷ xs) (y ∷ ys) = P.cong (_ ∷_) (map-zip f g xs ys)

------------------------------------------------------------------------
-- unzip

module _ {a b} {A : Set a} {B : Set b} where

  lookup-unzip : ∀ {n} (i : Fin n) (xys : Vec (A × B) n) →
                 let xs , ys = unzip xys
                 in (lookup xs i , lookup ys i) ≡ lookup xys i
  lookup-unzip ()      []
  lookup-unzip zero    ((x , y) ∷ xys) = refl
  lookup-unzip (suc i) ((x , y) ∷ xys) = lookup-unzip i xys

  map-unzip : ∀ {c d n} {C : Set c} {D : Set d}
              (f : A → B) (g : C → D) (xys : Vec (A × C) n) →
              let xs , ys = unzip xys
              in (map f xs , map g ys) ≡ unzip (map (Prod.map f g) xys)
  map-unzip f g []              = refl
  map-unzip f g ((x , y) ∷ xys) =
    P.cong (Prod.map (f x ∷_) (g y ∷_)) (map-unzip f g xys)

  -- Products of vectors are isomorphic to vectors of products.

  unzip∘zip : ∀ {n} (xs : Vec A n) (ys : Vec B n) →
              unzip (zip xs ys) ≡ (xs , ys)
  unzip∘zip [] []             = refl
  unzip∘zip (x ∷ xs) (y ∷ ys) =
    P.cong (Prod.map (x ∷_) (y ∷_)) (unzip∘zip xs ys)

  zip∘unzip : ∀ {n} (xys : Vec (A × B) n) →
              uncurry zip (unzip xys) ≡ xys
  zip∘unzip []              = refl
  zip∘unzip ((x , y) ∷ xys) = P.cong ((x , y) ∷_) (zip∘unzip xys)

  ×v↔v× : ∀ {n} → (Vec A n × Vec B n) ↔ Vec (A × B) n
  ×v↔v× = inverse (uncurry zip) unzip (uncurry unzip∘zip) zip∘unzip

------------------------------------------------------------------------
-- _⊛_

module _ {a b} {A : Set a} {B : Set b} where

  lookup-⊛ : ∀ {n} i (fs : Vec (A → B) n) (xs : Vec A n) →
             lookup (fs ⊛ xs) i ≡ (lookup fs i $ lookup xs i)
  lookup-⊛ zero    (f ∷ fs) (x ∷ xs) = refl
  lookup-⊛ (suc i) (f ∷ fs) (x ∷ xs) = lookup-⊛ i fs xs

  map-is-⊛ : ∀ {n} (f : A → B) (xs : Vec A n) →
             map f xs ≡ (replicate f ⊛ xs)
  map-is-⊛ f []       = refl
  map-is-⊛ f (x ∷ xs) = P.cong (_ ∷_) (map-is-⊛ f xs)

  ⊛-is-zipWith : ∀ {n} (fs : Vec (A → B) n) (xs : Vec A n) →
                 (fs ⊛ xs) ≡ zipWith _$_ fs xs
  ⊛-is-zipWith []       []       = refl
  ⊛-is-zipWith (f ∷ fs) (x ∷ xs) = P.cong (f x ∷_) (⊛-is-zipWith fs xs)

  zipWith-is-⊛ : ∀ {c} {C : Set c} {n} (f : A → B → C) →
                 (xs : Vec A n) (ys : Vec B n) →
                 zipWith f xs ys ≡ (replicate f ⊛ xs ⊛ ys)
  zipWith-is-⊛ f []       []       = refl
  zipWith-is-⊛ f (x ∷ xs) (y ∷ ys) = P.cong (_ ∷_) (zipWith-is-⊛ f xs ys)

------------------------------------------------------------------------
-- foldr

-- See also Data.Vec.Properties.WithK.foldr-cong.

-- The (uniqueness part of the) universality property for foldr.

foldr-universal : ∀ {a b} {A : Set a} (B : ℕ → Set b)
                  (f : ∀ {n} → A → B n → B (suc n)) {e}
                  (h : ∀ {n} → Vec A n → B n) →
                  h [] ≡ e →
                  (∀ {n} x → h ∘ (x ∷_) ≗ f {n} x ∘ h) →
                  ∀ {n} → h ≗ foldr B {n} f e
foldr-universal B f {_} h base step []       = base
foldr-universal B f {e} h base step (x ∷ xs) = begin
  h (x ∷ xs)
    ≡⟨ step x xs ⟩
  f x (h xs)
    ≡⟨ P.cong (f x) (foldr-universal B f h base step xs) ⟩
  f x (foldr B f e xs)
    ∎
  where open P.≡-Reasoning

foldr-fusion : ∀ {a b c} {A : Set a}
               {B : ℕ → Set b} {f : ∀ {n} → A → B n → B (suc n)} e
               {C : ℕ → Set c} {g : ∀ {n} → A → C n → C (suc n)}
               (h : ∀ {n} → B n → C n) →
               (∀ {n} x → h ∘ f {n} x ≗ g x ∘ h) →
               ∀ {n} → h ∘ foldr B {n} f e ≗ foldr C g (h e)
foldr-fusion {B = B} {f} e {C} h fuse =
  foldr-universal C _ _ refl (λ x xs → fuse x (foldr B f e xs))

idIsFold : ∀ {a n} {A : Set a} → id ≗ foldr (Vec A) {n} _∷_ []
idIsFold = foldr-universal _ _ id refl (λ _ _ → refl)

------------------------------------------------------------------------
-- sum

sum-++-commute : ∀ {m n} (xs : Vec ℕ m) {ys : Vec ℕ n} →
                 sum (xs ++ ys) ≡ sum xs + sum ys
sum-++-commute []       {_}  = refl
sum-++-commute (x ∷ xs) {ys} = begin
  x + sum (xs ++ ys)     ≡⟨ P.cong (x +_) (sum-++-commute xs) ⟩
  x + (sum xs + sum ys)  ≡⟨ P.sym (+-assoc x (sum xs) (sum ys)) ⟩
  sum (x ∷ xs) + sum ys  ∎
  where open P.≡-Reasoning

------------------------------------------------------------------------
-- replicate

lookup-replicate : ∀ {a n} {A : Set a} (i : Fin n) (x : A) →
                   lookup (replicate x) i ≡ x
lookup-replicate zero    = λ _ → refl
lookup-replicate (suc i) = lookup-replicate i

map-replicate :  ∀ {a b} {A : Set a} {B : Set b} (f : A → B) (x : A) →
                 ∀ n → map f (replicate x) ≡ replicate {n = n} (f x)
map-replicate f x zero = refl
map-replicate f x (suc n) = P.cong (f x ∷_) (map-replicate f x n)

module _ {a b c} {A : Set a} {B : Set b} {C : Set c} where

  zipWith-replicate₁ : ∀ {n} (_⊕_ : A → B → C) (x : A) (ys : Vec B n) →
                     zipWith _⊕_ (replicate x) ys ≡ map (x ⊕_) ys
  zipWith-replicate₁ _⊕_ x []       = refl
  zipWith-replicate₁ _⊕_ x (y ∷ ys) =
    P.cong (x ⊕ y ∷_) (zipWith-replicate₁ _⊕_ x ys)

  zipWith-replicate₂ : ∀ {n} (_⊕_ : A → B → C) (xs : Vec A n) (y : B) →
                     zipWith _⊕_ xs (replicate y) ≡ map (_⊕ y) xs
  zipWith-replicate₂ _⊕_ []       y = refl
  zipWith-replicate₂ _⊕_ (x ∷ xs) y =
    P.cong (x ⊕ y ∷_) (zipWith-replicate₂ _⊕_ xs y)

------------------------------------------------------------------------
-- tabulate

lookup∘tabulate : ∀ {a n} {A : Set a} (f : Fin n → A) (i : Fin n) →
                  lookup (tabulate f) i ≡ f i
lookup∘tabulate f zero    = refl
lookup∘tabulate f (suc i) = lookup∘tabulate (f ∘ suc) i

tabulate∘lookup : ∀ {a n} {A : Set a} (xs : Vec A n) →
                  tabulate (lookup xs) ≡ xs
tabulate∘lookup []       = refl
tabulate∘lookup (x ∷ xs) = P.cong (x ∷_) (tabulate∘lookup xs)

tabulate-∘ : ∀ {n a b} {A : Set a} {B : Set b}
             (f : A → B) (g : Fin n → A) →
             tabulate (f ∘ g) ≡ map f (tabulate g)
tabulate-∘ {zero}  f g = refl
tabulate-∘ {suc n} f g = P.cong (f (g zero) ∷_) (tabulate-∘ f (g ∘ suc))

tabulate-cong : ∀ {n a} {A : Set a} {f g : Fin n → A} → f ≗ g → tabulate f ≡ tabulate g
tabulate-cong {zero} p = refl
tabulate-cong {suc n} p = P.cong₂ _∷_ (p zero) (tabulate-cong (p ∘ suc))

------------------------------------------------------------------------
-- allFin

lookup-allFin : ∀ {n} (i : Fin n) → lookup (allFin n) i ≡ i
lookup-allFin = lookup∘tabulate id

allFin-map : ∀ n → allFin (suc n) ≡ zero ∷ map suc (allFin n)
allFin-map n = P.cong (zero ∷_) $ tabulate-∘ suc id

tabulate-allFin : ∀ {n a} {A : Set a} (f : Fin n → A) →
                  tabulate f ≡ map f (allFin n)
tabulate-allFin f = tabulate-∘ f id

-- If you look up every possible index, in increasing order, then you
-- get back the vector you started with.

map-lookup-allFin : ∀ {a} {A : Set a} {n} (xs : Vec A n) →
                    map (lookup xs) (allFin n) ≡ xs
map-lookup-allFin {n = n} xs = begin
  map (lookup xs) (allFin n) ≡˘⟨ tabulate-∘ (lookup xs) id ⟩
  tabulate (lookup xs)       ≡⟨ tabulate∘lookup xs ⟩
  xs                         ∎
  where open P.≡-Reasoning

------------------------------------------------------------------------
-- count

module _ {a p} {A : Set a} {P : Pred A p} (P? : Decidable P) where

  count≤n : ∀ {n} (xs : Vec A n) → count P? xs ≤ n
  count≤n []       = z≤n
  count≤n (x ∷ xs) with P? x
  ... | yes _ = s≤s (count≤n xs)
  ... | no  _ = ≤-step (count≤n xs)

------------------------------------------------------------------------
-- insert

module _ {a} {A : Set a} where

  insert-lookup : ∀ {n} (xs : Vec A n) (i : Fin (suc n)) (v : A) →
                  lookup (insert xs i v) i ≡ v
  insert-lookup xs       zero     v = refl
  insert-lookup []       (suc ()) v
  insert-lookup (x ∷ xs) (suc i)  v = insert-lookup xs i v

  insert-punchIn : ∀ {n} (xs : Vec A n) (i : Fin (suc n)) (v : A)
                   (j : Fin n) →
                   lookup (insert xs i v) (Fin.punchIn i j) ≡ lookup xs j
  insert-punchIn xs       zero     v j       = refl
  insert-punchIn []       (suc ()) v j
  insert-punchIn (x ∷ xs) (suc i)  v zero    = refl
  insert-punchIn (x ∷ xs) (suc i)  v (suc j) = insert-punchIn xs i v j

  remove-punchOut : ∀ {n} (xs : Vec A (suc n))
                    {i : Fin (suc n)} {j : Fin (suc n)} (i≢j : i ≢ j) →
                    lookup (remove xs i) (Fin.punchOut i≢j) ≡ lookup xs j
  remove-punchOut (x ∷ xs) {zero} {zero} i≢j = ⊥-elim (i≢j refl)
  remove-punchOut (x ∷ xs) {zero} {suc j} i≢j = refl
  remove-punchOut (x ∷ []) {suc ()} {j} i≢j
  remove-punchOut (x ∷ y ∷ xs) {suc i} {zero} i≢j = refl
  remove-punchOut (x ∷ y ∷ xs) {suc i} {suc j} i≢j =
    remove-punchOut (y ∷ xs) (i≢j ∘ P.cong suc)

------------------------------------------------------------------------
-- remove

  remove-insert : ∀ {n} (xs : Vec A n) (i : Fin (suc n)) (v : A) →
                  remove (insert xs i v) i ≡ xs
  remove-insert xs           zero           v = refl
  remove-insert []           (suc ())       v
  remove-insert (x ∷ xs)     (suc zero)     v = refl
  remove-insert (x ∷ [])     (suc (suc ())) v
  remove-insert (x ∷ y ∷ xs) (suc (suc i))  v =
    P.cong (x ∷_) (remove-insert (y ∷ xs) (suc i) v)

  insert-remove : ∀ {n} (xs : Vec A (suc n)) (i : Fin (suc n)) →
                  insert (remove xs i) i (lookup xs i) ≡ xs
  insert-remove (x ∷ xs)     zero     = refl
  insert-remove (x ∷ [])     (suc ())
  insert-remove (x ∷ y ∷ xs) (suc i)  =
    P.cong (x ∷_) (insert-remove (y ∷ xs) i)

------------------------------------------------------------------------
-- Conversion function

module _ {a} {A : Set a} where

  toList∘fromList : (xs : List A) → toList (fromList xs) ≡ xs
  toList∘fromList List.[]       = refl
  toList∘fromList (x List.∷ xs) = P.cong (x List.∷_) (toList∘fromList xs)


 {-# OPTIONS --type-in-type #-}

module DescTT where

--********************************************
-- Prelude
--********************************************

-- Some preliminary stuffs, to avoid relying on the stdlib

--****************
-- Sigma and friends
--****************

data Sigma (A : Set) (B : A -> Set) : Set where
  _,_ : (x : A) (y : B x) -> Sigma A B

_*_ : (A : Set)(B : Set) -> Set
A * B = Sigma A \_ -> B

fst : {A : Set}{B : A -> Set} -> Sigma A B -> A
fst (a , _) = a

snd : {A : Set}{B : A -> Set} (p : Sigma A B) -> B (fst p)
snd (a , b) = b

data Zero : Set where
data Unit  : Set where
  Void : Unit

--****************
-- Sum and friends
--****************

data _+_ (A : Set)(B : Set) : Set where
  l : A -> A + B
  r : B -> A + B

--****************
-- Equality
--****************

data _==_ {A : Set}(x : A) : A -> Set where
  refl : x == x

cong : {A B : Set}(f : A -> B){x y : A} -> x == y -> f x == f y
cong f refl = refl

cong2 : {A B C : Set}(f : A -> B -> C){x y : A}{z t : B} ->
        x == y -> z == t -> f x z == f y t
cong2 f refl refl = refl

postulate
  reflFun : {A : Set}{B : A -> Set}(f : (a : A) -> B a)(g : (a : A) -> B a)-> ((a : A) -> f a == g a) -> f == g

--********************************************
-- Desc code
--********************************************

data Desc : Set where
  id : Desc
  const : Set -> Desc
  prod : Desc -> Desc -> Desc
  sigma : (S : Set) -> (S -> Desc) -> Desc
  pi : (S : Set) -> (S -> Desc) -> Desc

--********************************************
-- Desc interpretation
--********************************************

[|_|]_ : Desc -> Set -> Set
[| id |] Z        = Z
[| const X |] Z   = X
[| prod D D' |] Z = [| D |] Z * [| D' |] Z
[| sigma S T |] Z = Sigma S (\s -> [| T s |] Z)
[| pi S T |] Z    = (s : S) -> [| T s |] Z

--********************************************
-- Fixpoint construction
--********************************************

data Mu (D : Desc) : Set where
  con : [| D |] (Mu D) -> Mu D

--********************************************
-- Predicate: All
--********************************************

All : (D : Desc)(X : Set)(P : X -> Set) -> [| D |] X -> Set
All id          X P x        = P x
All (const Z)   X P x        = Unit
All (prod D D') X P (d , d') = (All D X P d) * (All D' X P d')
All (sigma S T) X P (a , b)  = All (T a) X P b
All (pi S T)    X P f        = (s : S) -> All (T s) X P (f s)

all : (D : Desc)(X : Set)(P : X -> Set)(R : (x : X) -> P x)(x : [| D |] X) -> All D X P x
all id X P R x = R x
all (const Z) X P R z = Void
all (prod D D') X P R (d , d') = all D X P R d , all D' X P R d'
all (sigma S T) X P R (a , b) = all (T a) X P R b
all (pi S T) X P R f = \ s -> all (T s) X P R (f s)

--********************************************
-- Map
--********************************************

map : (D : Desc)(X Y : Set)(f : X -> Y)(v : [| D |] X) -> [| D |] Y
map id X Y sig x = sig x
map (const Z) X Y sig z = z
map (prod D D') X Y sig (d , d') = map D X Y sig d , map D' X Y sig d'
map (sigma S T) X Y sig (a , b) = (a , map (T a) X Y sig b)
map (pi S T) X Y sig f = \x -> map (T x) X Y sig (f x)

proof-map-id : (D : Desc)(X : Set)(v : [| D |] X) -> map D X X (\x -> x) v == v
proof-map-id id X v = refl
proof-map-id (const Z) X v = refl
proof-map-id (prod D D') X (v , v') = cong2 (\x y -> (x , y)) (proof-map-id D X v) (proof-map-id D' X v')
proof-map-id (sigma S T) X (a , b) = cong (\x -> (a , x)) (proof-map-id (T a) X b)
proof-map-id (pi S T) X f = reflFun (\a -> map (T a) X X (\x -> x) (f a)) f (\a -> proof-map-id (T a) X (f a))

proof-map-compos : (D : Desc)(X Y Z : Set)
                   (f : X -> Y)(g : Y -> Z)
                   (v : [| D |] X) ->
                   map D X Z (\x -> g (f x)) v == map D Y Z g (map D X Y f v)
proof-map-compos id X Y Z f g v = refl
proof-map-compos (const K) X Y Z f g v = refl
proof-map-compos (prod D D') X Y Z f g (v , v') = cong2 (\x y -> (x , y))
                                                        (proof-map-compos D X Y Z f g v)
                                                        (proof-map-compos D' X Y Z f g v')
proof-map-compos (sigma S T) X Y Z f g (a , b) = cong (\x -> (a , x)) (proof-map-compos (T a) X Y Z f g b)
proof-map-compos (pi S T) X Y Z f g fc = reflFun (\a -> map (T a) X Z (\x -> g (f x)) (fc a))
                                                 (\a -> map (T a) Y Z g (map (T a) X Y f (fc a)))
                                                 (\a -> proof-map-compos (T a) X Y Z f g (fc a))

--********************************************
-- Elimination principle: induction
--********************************************

{-
induction : (D : Desc)
            (P : Mu D -> Set) ->
            ( (x : [| D |] (Mu D)) ->
              All D (Mu D) P x -> P (con x)) ->
            (v : Mu D) ->
            P v
induction D P ms (con xs) = ms xs (all D (Mu D) P (\x -> induction D P ms x) xs)
-}

module Elim (D : Desc)
            (P : Mu D -> Set)
            (ms : (x : [| D |] (Mu D)) ->
                  All D (Mu D) P x -> P (con x))
       where

    mutual
        induction : (x : Mu D) -> P x
        induction (con xs) =  ms xs (hyps D xs)

        hyps : (D' : Desc)
               (xs : [| D' |] (Mu D)) ->
               All D' (Mu D) P xs
        hyps id x = induction x
        hyps (const Z) z = Void
        hyps (prod D D') (d , d') = hyps D d , hyps D' d'
        hyps (sigma S T) (a , b) = hyps (T a) b
        hyps (pi S T) f = \s -> hyps (T s) (f s)

induction : (D : Desc)
            (P : Mu D -> Set) ->
            ( (x : [| D |] (Mu D)) ->
              All D (Mu D) P x -> P (con x)) ->
            (v : Mu D) ->
            P v
induction D P ms x = Elim.induction D P ms x

--********************************************
-- Examples
--********************************************

--****************
-- Nat
--****************

data NatConst : Set where
  Ze : NatConst
  Suc : NatConst

natCases : NatConst -> Desc
natCases Ze = const Unit
natCases Suc = id

NatD : Desc
NatD = sigma NatConst natCases

Nat : Set
Nat = Mu NatD

ze : Nat
ze = con (Ze , Void)

suc : Nat -> Nat
suc n = con (Suc , n)

--****************
-- List
--****************

data ListConst : Set where
  Nil : ListConst
  Cons : ListConst

listCases : Set -> ListConst -> Desc
listCases X Nil = const Unit
listCases X Cons = sigma X (\_ -> id)

ListD : Set -> Desc
ListD X = sigma ListConst (listCases X)

List : Set -> Set
List X = Mu (ListD X)

nil : {X : Set} -> List X
nil = con ( Nil , Void )

cons : {X : Set} -> X -> List X -> List X
cons x t = con ( Cons , ( x , t ))

--****************
-- Tree
--****************

data TreeConst : Set where
  Leaf : TreeConst
  Node : TreeConst

treeCases : Set -> TreeConst -> Desc
treeCases X Leaf = const Unit
treeCases X Node = sigma X (\_ -> prod id id)

TreeD : Set -> Desc
TreeD X = sigma TreeConst (treeCases X)

Tree : Set -> Set
Tree X = Mu (TreeD X)

leaf : {X : Set} -> Tree X
leaf = con (Leaf , Void)

node : {X : Set} -> X -> Tree X -> Tree X -> Tree X
node x le ri = con (Node , (x , (le , ri)))

--********************************************
-- Finite sets
--********************************************

EnumU : Set
EnumU = Nat

nilE : EnumU
nilE = ze

consE : EnumU -> EnumU
consE e = suc e

{-
data EnumU : Set where
  nilE : EnumU
  consE : EnumU -> EnumU
-}

data EnumT : (e : EnumU) -> Set where
  EZe : {e : EnumU} -> EnumT (consE e)
  ESu : {e : EnumU} -> EnumT e -> EnumT (consE e)

casesSpi : (xs : [| NatD |] Nat) ->
           All NatD Nat (\e -> (P' : EnumT e -> Set) -> Set) xs ->
           (P' : EnumT (con xs) -> Set) -> Set
casesSpi (Ze , Void) hs P' = Unit
casesSpi (Suc , n) hs P' = P' EZe * hs (\e -> P' (ESu e))

spi : (e : EnumU)(P : EnumT e -> Set) -> Set
spi e P = induction NatD (\e -> (P : EnumT e -> Set) -> Set) casesSpi e  P

{-
spi : (e : EnumU)(P : EnumT e -> Set) -> Set
spi nilE P = Unit
spi (consE e) P = P EZe * spi e (\e -> P (ESu e))
-}

casesSwitch : (xs : [| NatD |] Nat) ->
              All NatD Nat (\e -> (P' : EnumT e -> Set)(b' : spi e P')(x' : EnumT e) -> P' x') xs ->
              (P' : EnumT (con xs) -> Set)(b' : spi (con xs) P')(x' : EnumT (con xs)) -> P' x'
casesSwitch (Ze , Void) hs P' b' ()
casesSwitch (Suc , n) hs P' b' EZe = fst b'
casesSwitch (Suc , n) hs P' b' (ESu e') = hs (\e -> P' (ESu e)) (snd b') e'

switch : (e : EnumU)(P : EnumT e -> Set)(b : spi e P)(x : EnumT e) -> P x
switch e P b x = induction NatD (\e -> (P : EnumT e -> Set)(b : spi e P)(x : EnumT e) -> P x) casesSwitch e P b x

{-
switch : (e : EnumU)(P : EnumT e -> Set)(b : spi e P)(x : EnumT e) -> P x
switch nilE P b ()
switch (consE e) P b EZe = fst b
switch (consE e) P b (ESu n) = switch e (\e -> P (ESu e)) (snd b) n
-}

--********************************************
-- Tagged description
--********************************************

TagDesc : Set
TagDesc = Sigma EnumU (\e -> spi e (\_ -> Desc))

toDesc : TagDesc -> Desc
toDesc (B , F) = sigma (EnumT B) (\e -> switch B (\_ -> Desc) F e)

--********************************************
-- Catamorphism
--********************************************

cata : (D : Desc)
       (T : Set) ->
       ([| D |] T -> T) ->
       (Mu D) -> T
cata D T phi x = induction D (\_ -> T) (\x ms -> phi (replace D T x ms)) x
  where replace : (D' : Desc)(T : Set)(xs : [| D' |] (Mu D))(ms : All D' (Mu D) (\_ -> T) xs) -> [| D' |] T
        replace id T x y = y
        replace (const Z) T z z' = z
        replace (prod D D') T (x , x') (y , y') = replace D T x y , replace D' T x' y'
        replace (sigma A B) T (a , b) t = a , replace (B a) T b t
        replace (pi A B) T f t = \s -> replace (B s) T (f s) (t s)

--********************************************
-- Free monad construction
--********************************************

_**_ : TagDesc -> (X : Set) -> TagDesc
(e , D) ** X = consE e , (const X , D)

--********************************************
-- Substitution
--********************************************

apply : (D : TagDesc)(X Y : Set) ->
        (X -> Mu (toDesc (D ** Y))) ->
        [| toDesc (D ** X) |] (Mu (toDesc (D ** Y))) ->
        Mu (toDesc (D ** Y))
apply (E , B) X Y sig (EZe , x) = sig x
apply (E , B) X Y sig (ESu n , t) = con (ESu n , t)

subst : (D : TagDesc)(X Y : Set) ->
        Mu (toDesc (D ** X)) ->
        (X -> Mu (toDesc (D ** Y))) ->
        Mu (toDesc (D ** Y))
subst D X Y x sig = cata (toDesc (D ** X)) (Mu (toDesc (D ** Y))) (apply D X Y sig) x


{-# OPTIONS --cubical --no-import-sorts --no-exact-split --safe #-}

module Cubical.Data.InfNat.Properties where

open import Cubical.Data.Nat as ℕ using (ℕ)
open import Cubical.Data.InfNat.Base
open import Cubical.Core.Primitives
open import Cubical.Foundations.Prelude
open import Cubical.Relation.Nullary
open import Cubical.Data.Unit
open import Cubical.Data.Empty

fromInf-def : ℕ → ℕ+∞ → ℕ
fromInf-def n ∞ = n
fromInf-def _ (fin n) = n

fin-inj : (n m : ℕ) → fin n ≡ fin m → n ≡ m
fin-inj x _ eq = cong (fromInf-def x) eq

discreteInfNat : Discrete ℕ+∞
discreteInfNat ∞ ∞ = yes (λ i → ∞)
discreteInfNat ∞ (fin _) = no λ p → subst (caseInfNat ⊥ Unit) p tt
discreteInfNat (fin _) ∞ = no λ p → subst (caseInfNat Unit ⊥) p tt
discreteInfNat (fin n) (fin m) with ℕ.discreteℕ n m
discreteInfNat (fin n) (fin m) | yes p = yes (cong fin p)
discreteInfNat (fin n) (fin m) | no ¬p = no (λ p → ¬p (fin-inj n m p))


{-# OPTIONS --without-K #-}
module isos-examples where

open import Function
open import Function.Related.TypeIsomorphisms.NP
import Function.Inverse.NP as FI
open FI using (_↔_; inverses; module Inverse) renaming (_$₁_ to to; _$₂_ to from)
import Function.Related as FR
open import Type hiding (★)
open import Data.Product.NP
open import Data.Bool.NP using (✓)
open import Data.One using (𝟙)
open import Data.Bits
open import Relation.Binary
import Relation.Binary.PropositionalEquality as ≡
open ≡ using (_≡_; subst)

_≈₂_ : ∀ {a} {A : ★ a} (f g : A → Bit) → ★ _
_≈₂_ {A = A} f g = Σ A (✓ ∘ f) ↔ Σ A (✓ ∘ g)

module _ {a r} {A : ★ a} {R : ★ r} where
  _≈_ : (f g : A → R) → ★ _
  f ≈ g = ∀ (O : R → ★ r) → Σ A (O ∘ f) ↔ Σ A (O ∘ g)

  ≈-refl : Reflexive {A = A → R} _≈_
  ≈-refl _ = FI.id

  ≈-trans : Transitive {A = A → R} _≈_
  ≈-trans p q O = q O FI.∘ p O

  ≈-sym : Symmetric {A = A → R} _≈_
  ≈-sym p O = FI.sym (p O)

module _ {a r} {A : ★ a} {R : ★ r} (f : A → R) (p : A ↔ A) where
  stable : f ≈ (f ∘ from p)
  stable _ = first-iso p

  stable′ : f ≈ (f ∘ to p)
  stable′ _ = first-iso (FI.sym p)

module _ {a b r} {A : ★ a} {B : ★ b} {R : ★ r} where
  _≋_ : (f : A → R) (g : B → R) → ★ _
  f ≋ g = (f ∘ proj₁) ≈ (g ∘ proj₂)

module _ {a b r} {A : ★ a} {B : ★ b} {R : ★ r} where

  _≋′_ : (f : A → R) (g : B → R) → ★ _
  f ≋′ g = ∀ (O : R → ★ r) →
            (B × Σ A (O ∘ f)) ↔ (A × Σ B (O ∘ g))

  module _ {f : A → R} {g : B → R} where
    open FR.EquationalReasoning

    ≋′→≋ : f ≋′ g → f ≋ g
    ≋′→≋ h O = Σ (A × B) (O ∘ f ∘ proj₁)
             ↔⟨ Σ×-swap ⟩
               Σ (B × A) (O ∘ f ∘ proj₂)
             ↔⟨ Σ-assoc ⟩
               (B × Σ A (O ∘ f))
             ↔⟨ h O ⟩
               (A × Σ B (O ∘ g))
             ↔⟨ FI.sym Σ-assoc ⟩
               Σ (A × B) (O ∘ g ∘ proj₂)
             ∎

    ≋→≋′ : f ≋ g → f ≋′ g
    ≋→≋′ h O = (B × Σ A (O ∘ f))
             ↔⟨ FI.sym Σ-assoc ⟩
               Σ (B × A) (O ∘ f ∘ proj₂)
             ↔⟨ Σ×-swap ⟩
               Σ (A × B) (O ∘ f ∘ proj₁)
             ↔⟨ h O ⟩
               Σ (A × B) (O ∘ g ∘ proj₂)
             ↔⟨ Σ-assoc ⟩
               (A × Σ B (O ∘ g))
             ∎
             -- -}
             -- -}
             -- -}
             -- -}
             -- -}


------------------------------------------------------------------------
-- The Agda standard library
--
-- Indexed binary relations
------------------------------------------------------------------------

-- The contents of this module should be accessed via
-- `Relation.Binary.Indexed.Heterogeneous`.

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

module Relation.Binary.Indexed.Heterogeneous.Bundles where

open import Function.Base
open import Level using (suc; _⊔_)
open import Relation.Binary using (_⇒_)
open import Relation.Binary.PropositionalEquality.Core as P using (_≡_)
open import Relation.Binary.Indexed.Heterogeneous.Core
open import Relation.Binary.Indexed.Heterogeneous.Structures

------------------------------------------------------------------------
-- Definitions

record IndexedSetoid {i} (I : Set i) c ℓ : Set (suc (i ⊔ c ⊔ ℓ)) where
  infix 4 _≈_
  field
    Carrier       : I → Set c
    _≈_           : IRel Carrier ℓ
    isEquivalence : IsIndexedEquivalence Carrier _≈_

  open IsIndexedEquivalence isEquivalence public

record IndexedPreorder {i} (I : Set i) c ℓ₁ ℓ₂ :
                       Set (suc (i ⊔ c ⊔ ℓ₁ ⊔ ℓ₂)) where
  infix 4 _≈_ _∼_
  field
    Carrier    : I → Set c
    _≈_        : IRel Carrier ℓ₁  -- The underlying equality.
    _∼_        : IRel Carrier ℓ₂  -- The relation.
    isPreorder : IsIndexedPreorder Carrier _≈_ _∼_

  open IsIndexedPreorder isPreorder public


module Prelude.Maybe where

open import Prelude.Unit
open import Prelude.Empty
open import Prelude.Function
open import Prelude.Functor
open import Prelude.Applicative
open import Prelude.Monad
open import Prelude.Equality
open import Prelude.Decidable

data Maybe {a} (A : Set a) : Set a where
  nothing : Maybe A
  just    : A → Maybe A

{-# FOREIGN GHC type AgdaMaybe l = Maybe #-}
{-# COMPILE GHC Maybe = data MAlonzo.Code.Prelude.Maybe.AgdaMaybe (Nothing | Just) #-}

maybe : ∀ {a b} {A : Set a} {B : Set b} → B → (A → B) → Maybe A → B
maybe z f nothing  = z
maybe z f (just x) = f x
{-# INLINE maybe #-}

fromMaybe : ∀ {a} {A : Set a} → A → Maybe A → A
fromMaybe z = maybe z id
{-# INLINE fromMaybe #-}

IsJust : ∀ {a} {A : Set a} → Maybe A → Set
IsJust = maybe ⊥ (const ⊤)

FromJust : ∀ {a} {A : Set a} → Maybe A → Set a
FromJust {A = A} = maybe ⊤′ (const A)

fromJust : ∀ {a} {A : Set a} (m : Maybe A) → FromJust m
fromJust nothing  = _
fromJust (just x) = x

maybeYes : ∀ {a} {A : Set a} → Dec A → Maybe A
maybeYes (yes x) = just x
maybeYes (no _)  = nothing

--- Equality ---

just-inj : ∀ {a} {A : Set a} {x y : A} → just x ≡ just y → x ≡ y
just-inj refl = refl

instance
  EqMaybe : ∀ {a} {A : Set a} {{EqA : Eq A}} → Eq (Maybe A)
  _==_ {{EqMaybe}} nothing nothing  = yes refl
  _==_ {{EqMaybe}} nothing (just x) = no λ ()
  _==_ {{EqMaybe}} (just x) nothing = no λ ()
  _==_ {{EqMaybe}} (just x) (just y) with x == y
  ... | yes eq  = yes (just $≡ eq)
  ... | no  neq = no (λ eq → neq (just-inj eq))

--- Monad ---

instance
  FunctorMaybe : ∀ {a} → Functor (Maybe {a})
  fmap {{FunctorMaybe}} f m = maybe nothing (just ∘ f) m

  ApplicativeMaybe : ∀ {a} → Applicative (Maybe {a})
  pure  {{ApplicativeMaybe}} = just
  _<*>_ {{ApplicativeMaybe}} mf mx = maybe nothing (λ f → fmap f mx) mf

  MonadMaybe : ∀ {a} → Monad {a} Maybe
  _>>=_  {{MonadMaybe}} m f = maybe nothing f m

  FunctorMaybe′ : ∀ {a b} → Functor′ {a} {b} Maybe
  fmap′ {{FunctorMaybe′}} f m = maybe nothing (just ∘ f) m

  ApplicativeMaybe′ : ∀ {a b} → Applicative′ {a} {b} Maybe
  _<*>′_ {{ApplicativeMaybe′}} (just f) (just x) = just (f x)
  _<*>′_ {{ApplicativeMaybe′}}  _        _       = nothing

  MonadMaybe′ : ∀ {a b} → Monad′ {a} {b} Maybe
  _>>=′_ {{MonadMaybe′}} m f = maybe nothing f m


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

open import HoTT
open import cohomology.Theory

{- Ordinary cohomology groups of the n-torus Tⁿ = (S¹)ⁿ.
 - We have Cᵏ(Tⁿ) == C⁰(S⁰)^(n choose' k) where _choose'_ defined as below.
 - This argument could give Cᵏ((Sᵐ)ⁿ) with a little more work. -}

module cohomology.Torus {i} (OT : OrdinaryTheory i) where

open OrdinaryTheory OT
open import cohomology.Sn OT
open import cohomology.SphereProduct cohomology-theory
open import cohomology.Unit cohomology-theory

{- Almost n choose k, but with n choose' O = 0 for any n. -}
_choose'_ : ℕ → ℤ → ℕ
n choose' negsucc _ = 0
n choose' pos O = 0
n choose' pos (S O) = n
O choose' pos (S (S k)) = 0
S n choose' pos (S (S k)) = (n choose' (pos (S (S k)))) + (n choose' (pos (S k)))

_-⊙Torus : ℕ → Ptd i
O -⊙Torus = ⊙Lift ⊙Unit
(S n) -⊙Torus = ⊙Lift {j = i} ⊙S¹ ⊙× (n -⊙Torus)

C-nTorus : (k : ℤ) (n : ℕ)
  → C k (n -⊙Torus) == C 0 (⊙Lift ⊙S⁰) ^ᴳ (n choose' k)

C-nTorus (negsucc k) O = C-Unit (negsucc k)

C-nTorus (negsucc k) (S n) =
  C-Sphere× (negsucc k) 1 (n -⊙Torus)
  ∙ ap (λ K → K ×ᴳ (C (negsucc k) (n -⊙Torus)
                    ×ᴳ C (negsucc k) (⊙Susp^ 1 (n -⊙Torus))))
       (C-Sphere-≠ (negsucc k) 1 (ℤ-negsucc≠pos _ _))
  ∙ ×ᴳ-unit-l {G = C (negsucc k) (n -⊙Torus)
                   ×ᴳ C (negsucc k) (⊙Susp (n -⊙Torus))}
  ∙ ap (λ K → K ×ᴳ C (negsucc k) (⊙Susp (n -⊙Torus)))
       (C-nTorus (negsucc k) n)
  ∙ ×ᴳ-unit-l {G = C (negsucc k) (⊙Susp (n -⊙Torus))}
  ∙ group-ua (C-Susp (negsucc (S k)) (n -⊙Torus))
  ∙ C-nTorus (negsucc (S k)) n

C-nTorus (pos O) O = C-Unit (pos O)

C-nTorus (pos O) (S n) =
  C-Sphere× (pos O) 1 (n -⊙Torus)
  ∙ ap (λ K → K ×ᴳ (C 0 (n -⊙Torus) ×ᴳ C 0 (⊙Susp (n -⊙Torus))))
       (C-Sphere-≠ (pos O) 1 (pos-≠ (ℕ-O≠S _)))
  ∙ ×ᴳ-unit-l {G = C 0 (n -⊙Torus) ×ᴳ C 0 (⊙Susp (n -⊙Torus))}
  ∙ ap (λ K → K ×ᴳ C 0 (⊙Susp (n -⊙Torus)))
       (C-nTorus 0 n)
  ∙ ×ᴳ-unit-l {G = C 0 (⊙Susp (n -⊙Torus))}
  ∙ group-ua (C-Susp (negsucc O) (n -⊙Torus))
  ∙ C-nTorus (negsucc O) n

C-nTorus (pos (S O)) O = C-Unit 1

C-nTorus (pos (S O)) (S n) =
  C-Sphere× 1 1 (n -⊙Torus)
  ∙ ap (λ K → K ×ᴳ (C 1 (n -⊙Torus)
                    ×ᴳ C 1 (⊙Susp (n -⊙Torus))))
       (C-Sphere-diag 1)
  ∙ ap (λ K → C 0 (⊙Lift ⊙S⁰) ×ᴳ K)
       (ap2 _×ᴳ_
         (C-nTorus 1 n)
         (group-ua (C-Susp 0 (n -⊙Torus)) ∙ C-nTorus 0 n)
        ∙ ×ᴳ-unit-r {G = C 0 (⊙Lift ⊙S⁰) ^ᴳ (n choose' 1)})

C-nTorus (pos (S (S k))) O =
  C-Unit (pos (S (S k)))

C-nTorus (pos (S (S k))) (S n) =
  C-Sphere× (pos (S (S k))) 1 (n -⊙Torus)
  ∙ ap (λ K → K ×ᴳ (C (pos (S (S k))) (n -⊙Torus)
                    ×ᴳ C (pos (S (S k))) (⊙Susp (n -⊙Torus))))
       (C-Sphere-≠ (pos (S (S k))) 1 (pos-≠ (ℕ-S-≠ (ℕ-S≠O k))))
  ∙ ×ᴳ-unit-l {G = (C (pos (S (S k))) (n -⊙Torus))
                   ×ᴳ (C (pos (S (S k))) (⊙Susp (n -⊙Torus)))}
  ∙ ap2 _×ᴳ_ (C-nTorus (pos (S (S k))) n)
             (group-ua (C-Susp (pos (S k)) (n -⊙Torus)) ∙ C-nTorus (pos (S k)) n)
  ∙ ^ᴳ-sum (C 0 (⊙Lift ⊙S⁰)) (n choose' pos (S (S k))) (n choose' pos (S k))


module LC.Base where

open import Data.Nat

--------------------------------------------------------------------------------
-- de Bruijn indexed lambda calculus
infix  5 ƛ_
infixl 7 _∙_
infix  9 var_

data Term : Set where
  var_  : (x : ℕ) → Term
  ƛ_    : (M : Term) → Term
  _∙_   : (M : Term) → (N : Term) → Term


------------------------------------------------------------------------------
-- Common definitions
------------------------------------------------------------------------------

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

module Common.DefinitionsI where

open import Common.FOL.FOL using ( ¬_ ; D )
open import Common.FOL.Relation.Binary.PropositionalEquality using ( _≡_ )

-- We add 3 to the fixities of the Agda standard library 0.8.1 (see
-- Relation/Binary/Core.agda).
infix 7 _≢_

------------------------------------------------------------------------------
-- Inequality.
_≢_ : D → D → Set
x ≢ y = ¬ x ≡ y


{-# OPTIONS --omega-in-omega --no-termination-check --overlapping-instances #-}

open import Light.Library.Data.Natural as ℕ using (ℕ ; predecessor ; zero)
open import Light.Package using (Package)

module Light.Literals.Level ⦃ natural‐package : Package record { ℕ } ⦄ where

open import Light.Literals.Definition.Natural using (FromNatural)
open FromNatural using (convert)
open import Light.Level using (Level ; 0ℓ ; ++_)
open import Light.Library.Relation.Decidable using (if_then_else_)
open import Light.Library.Relation.Binary.Equality using (_≈_)
open import Light.Library.Relation.Binary.Equality.Decidable using (DecidableEquality)

instance from‐natural : FromNatural Level
convert from‐natural n =
      if n ≈ zero
      then 0ℓ
      else ++ convert from‐natural (predecessor n)


{-# OPTIONS --cubical --safe #-}
module JustBeInjective where

open import Cubical.Core.Everything
open import Cubical.Data.Unit
data maybe (A : Set) : Set where
  just : A -> maybe A
  nothing : maybe A

variable A : Set

unwrap : A → (a : maybe A) → A
unwrap _ (just x) = x
unwrap a nothing = a

just-injective : ∀ {A : Set} (a b : A) → just a ≡ just b → a ≡ b
just-injective a b p i = unwrap a (p i)


-- 2010-10-06 Andreas

module TerminationRecordPatternListAppend where

data Empty : Set where

record Unit : Set where
  constructor unit

data Bool : Set where
  true false : Bool

T : Bool -> Set
T true = Unit
T false = Empty

-- Thorsten suggests on the Agda list  thread "Coinductive families"
-- to encode lists as records
record List (A : Set) : Set where
  constructor list
  field
    isCons : Bool
    head   : T isCons -> A
    tail   : T isCons -> List A

open List public

-- if the record constructor list was counted as structural increase
-- Thorsten's function would not be rejected
append : {A : Set} -> List A -> List A -> List A
append (list true  h t) l' = list true h (\ _ -> append (t _) l')
append (list false _ _) l' = l'

-- but translating away the record pattern produces something
-- that is in any case rejected by the termination checker
append1 : {A : Set} -> List A -> List A -> List A
append1 l l' = append1' l l' (isCons l)
  where append1' : {A : Set} -> List A -> List A -> Bool -> List A
        append1' l l' true  = list true (head l) (\ _ -> append (tail l) l')
        append1' l l' false = l'
-- NEED inspect!


{-# OPTIONS --safe #-}
module Cubical.Algebra.CommAlgebra.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.SIP

open import Cubical.Data.Sigma

open import Cubical.Algebra.Semigroup
open import Cubical.Algebra.Monoid
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.Ring
open import Cubical.Algebra.Algebra

open import Cubical.Displayed.Base
open import Cubical.Displayed.Auto
open import Cubical.Displayed.Record
open import Cubical.Displayed.Universe

open import Cubical.Reflection.RecordEquiv

private
  variable
    ℓ ℓ' : Level

record IsCommAlgebra (R : CommRing ℓ) {A : Type ℓ'}
                     (0a : A) (1a : A)
                     (_+_ : A → A → A) (_·_ : A → A → A) (-_ : A → A)
                     (_⋆_ : ⟨ R ⟩ → A → A) : Type (ℓ-max ℓ ℓ') where

  constructor iscommalgebra

  field
    isAlgebra : IsAlgebra (CommRing→Ring R) 0a 1a _+_ _·_ -_ _⋆_
    ·-comm    : (x y : A) → x · y ≡ y · x

  open IsAlgebra isAlgebra public

unquoteDecl IsCommAlgebraIsoΣ = declareRecordIsoΣ IsCommAlgebraIsoΣ (quote IsCommAlgebra)

record CommAlgebraStr (R : CommRing ℓ) (A : Type ℓ') : Type (ℓ-max ℓ ℓ') where

  constructor commalgebrastr

  field
    0a             : A
    1a             : A
    _+_            : A → A → A
    _·_            : A → A → A
    -_             : A → A
    _⋆_            : ⟨ R ⟩ → A → A
    isCommAlgebra      : IsCommAlgebra R 0a 1a _+_ _·_ -_ _⋆_

  open IsCommAlgebra isCommAlgebra public

  infix  8 -_
  infixl 7 _·_
  infixl 7 _⋆_
  infixl 6 _+_

CommAlgebra : (R : CommRing ℓ) → ∀ ℓ' → Type (ℓ-max ℓ (ℓ-suc ℓ'))
CommAlgebra R ℓ' = Σ[ A ∈ Type ℓ' ] CommAlgebraStr R A

module _ {R : CommRing ℓ} where
  open CommRingStr (snd R) using (1r) renaming (_+_ to _+r_; _·_ to _·s_)

  CommAlgebraStr→AlgebraStr : {A : Type ℓ'} → CommAlgebraStr R A → AlgebraStr (CommRing→Ring R) A
  CommAlgebraStr→AlgebraStr (commalgebrastr _ _ _ _ _ _ (iscommalgebra isAlgebra ·-comm)) =
    algebrastr _ _ _ _ _ _ isAlgebra

  CommAlgebra→Algebra : (A : CommAlgebra R ℓ') → Algebra (CommRing→Ring R) ℓ'
  CommAlgebra→Algebra (_ , str) = (_ , CommAlgebraStr→AlgebraStr str)

  CommAlgebra→CommRing : (A : CommAlgebra R ℓ') → CommRing ℓ'
  CommAlgebra→CommRing (_ , commalgebrastr  _ _ _ _ _ _ (iscommalgebra isAlgebra ·-comm)) =
    _ , commringstr _ _ _ _ _ (iscommring (IsAlgebra.isRing isAlgebra) ·-comm)

  isSetCommAlgebra : (A : CommAlgebra R ℓ') → isSet ⟨ A ⟩
  isSetCommAlgebra A = isSetAlgebra (CommAlgebra→Algebra A)

  makeIsCommAlgebra : {A : Type ℓ'} {0a 1a : A}
                      {_+_ _·_ : A → A → A} { -_ : A → A} {_⋆_ : ⟨ R ⟩ → A → A}
                      (isSet-A : isSet A)
                      (+-assoc :  (x y z : A) → x + (y + z) ≡ (x + y) + z)
                      (+-rid : (x : A) → x + 0a ≡ x)
                      (+-rinv : (x : A) → x + (- x) ≡ 0a)
                      (+-comm : (x y : A) → x + y ≡ y + x)
                      (·-assoc :  (x y z : A) → x · (y · z) ≡ (x · y) · z)
                      (·-lid : (x : A) → 1a · x ≡ x)
                      (·-ldist-+ : (x y z : A) → (x + y) · z ≡ (x · z) + (y · z))
                      (·-comm : (x y : A) → x · y ≡ y · x)
                      (⋆-assoc : (r s : ⟨ R ⟩) (x : A) → (r ·s s) ⋆ x ≡ r ⋆ (s ⋆ x))
                      (⋆-ldist : (r s : ⟨ R ⟩) (x : A) → (r +r s) ⋆ x ≡ (r ⋆ x) + (s ⋆ x))
                      (⋆-rdist : (r : ⟨ R ⟩) (x y : A) → r ⋆ (x + y) ≡ (r ⋆ x) + (r ⋆ y))
                      (⋆-lid   : (x : A) → 1r ⋆ x ≡ x)
                      (⋆-lassoc : (r : ⟨ R ⟩) (x y : A) → (r ⋆ x) · y ≡ r ⋆ (x · y))
                    → IsCommAlgebra R 0a 1a _+_ _·_ -_ _⋆_
  makeIsCommAlgebra {A = A} {0a} {1a} {_+_} {_·_} { -_} {_⋆_} isSet-A
                    +-assoc +-rid +-rinv +-comm
                    ·-assoc ·-lid ·-ldist-+ ·-comm
                    ⋆-assoc ⋆-ldist ⋆-rdist ⋆-lid ⋆-lassoc
   = iscommalgebra
     (makeIsAlgebra
       isSet-A
       +-assoc +-rid +-rinv +-comm
       ·-assoc
         (λ x → x · 1a ≡⟨ ·-comm _ _ ⟩ 1a · x ≡⟨ ·-lid _ ⟩ x ∎)
         ·-lid
         (λ x y z → x · (y + z)       ≡⟨ ·-comm _ _ ⟩
                    (y + z) · x       ≡⟨ ·-ldist-+ _ _ _ ⟩
                    (y · x) + (z · x) ≡⟨ cong (λ u → (y · x) + u) (·-comm _ _) ⟩
                    (y · x) + (x · z) ≡⟨ cong (λ u → u + (x · z)) (·-comm _ _) ⟩
                    (x · y) + (x · z) ∎)
         ·-ldist-+
       ⋆-assoc
         ⋆-ldist
         ⋆-rdist
         ⋆-lid
         ⋆-lassoc
         λ r x y → r ⋆ (x · y) ≡⟨ cong (λ u → r ⋆ u) (·-comm _ _) ⟩
                   r ⋆ (y · x) ≡⟨ sym (⋆-lassoc _ _ _) ⟩
                   (r ⋆ y) · x ≡⟨ ·-comm _ _ ⟩
                   x · (r ⋆ y) ∎)
     ·-comm

  module _ (S : CommRing ℓ) where
    open CommRingStr (snd S) renaming (1r to 1S)
    open CommRingStr (snd R) using () renaming (_·_ to _·R_; _+_ to _+R_; 1r to 1R)
    commAlgebraFromCommRing :
          (_⋆_ : fst R → fst S → fst S)
        → ((r s : fst R) (x : fst S) → (r ·R s) ⋆ x ≡ r ⋆ (s ⋆ x))
        → ((r s : fst R) (x : fst S) → (r +R s) ⋆ x ≡ (r ⋆ x) + (s ⋆ x))
        → ((r : fst R) (x y : fst S) → r ⋆ (x + y) ≡ (r ⋆ x) + (r ⋆ y))
        → ((x : fst S) → 1R ⋆ x ≡ x)
        → ((r : fst R) (x y : fst S) → (r ⋆ x) · y ≡ r ⋆ (x · y))
        → CommAlgebra R ℓ
    commAlgebraFromCommRing _⋆_ ·Assoc⋆ ⋆DistR ⋆DistL ⋆Lid ⋆Assoc· = fst S ,
      commalgebrastr 0r 1S _+_ _·_  -_ _⋆_
        (makeIsCommAlgebra is-set +Assoc +Rid +Rinv +Comm ·Assoc ·Lid ·Ldist+ ·Comm
                                  ·Assoc⋆ ⋆DistR ⋆DistL ⋆Lid ⋆Assoc·)

  IsCommAlgebraEquiv : {A B : Type ℓ'}
    (M : CommAlgebraStr R A) (e : A ≃ B) (N : CommAlgebraStr R B)
    → Type (ℓ-max ℓ ℓ')
  IsCommAlgebraEquiv M e N =
    IsAlgebraHom (CommAlgebraStr→AlgebraStr M) (e .fst) (CommAlgebraStr→AlgebraStr N)

  CommAlgebraEquiv : (M N : CommAlgebra R ℓ') → Type (ℓ-max ℓ ℓ')
  CommAlgebraEquiv M N = Σ[ e ∈ ⟨ M ⟩ ≃ ⟨ N ⟩ ] IsCommAlgebraEquiv (M .snd) e (N .snd)

  IsCommAlgebraHom : {A B : Type ℓ'}
    (M : CommAlgebraStr R A) (f : A → B) (N : CommAlgebraStr R B)
    → Type (ℓ-max ℓ ℓ')
  IsCommAlgebraHom M f N =
    IsAlgebraHom (CommAlgebraStr→AlgebraStr M) f (CommAlgebraStr→AlgebraStr N)

  CommAlgebraHom : (M N : CommAlgebra R ℓ') → Type (ℓ-max ℓ ℓ')
  CommAlgebraHom M N = Σ[ f ∈ (⟨ M ⟩ → ⟨ N ⟩) ] IsCommAlgebraHom (M .snd) f (N .snd)

  module _ {M N : CommAlgebra R ℓ'} where
    open CommAlgebraStr {{...}}
    open IsAlgebraHom
    private
      instance
        _ = snd M
        _ = snd N

    makeCommAlgebraHom : (f : fst M → fst N)
                           → (fPres1 : f 1a ≡ 1a)
                           → (fPres+ : (x y : fst M) → f (x + y) ≡ f x + f y)
                           → (fPres· : (x y : fst M) → f (x · y) ≡ f x · f y)
                           → (fPres⋆ : (r : fst R) (x : fst M) → f (r ⋆ x) ≡ r ⋆ f x)
                           → CommAlgebraHom M N
    makeCommAlgebraHom f fPres1 fPres+ fPres· fPres⋆ = f , isHom
      where fPres0 =
                    f 0a                  ≡⟨ sym (+-rid _) ⟩
                    f 0a + 0a             ≡⟨ cong (λ u → f 0a + u) (sym (+-rinv (f 0a))) ⟩
                    f 0a + (f 0a - f 0a)  ≡⟨ +-assoc (f 0a) (f 0a) (- f 0a) ⟩
                    (f 0a + f 0a) - f 0a  ≡⟨ cong (λ u → u - f 0a) (sym (fPres+ 0a 0a)) ⟩
                    f (0a + 0a) - f 0a    ≡⟨ cong (λ u → f u - f 0a) (+-lid 0a) ⟩
                    f 0a - f 0a           ≡⟨ +-rinv (f 0a) ⟩
                    0a ∎

            isHom : IsCommAlgebraHom (snd M) f (snd N)
            pres0 isHom = fPres0
            pres1 isHom = fPres1
            pres+ isHom = fPres+
            pres· isHom = fPres·
            pres- isHom = (λ x →
                               f (- x) ≡⟨ sym (+-rid _) ⟩
                               (f (- x) + 0a) ≡⟨ cong (λ u → f (- x) + u) (sym (+-rinv (f x))) ⟩
                               (f (- x) + (f x - f x)) ≡⟨ +-assoc _ _ _ ⟩
                               ((f (- x) + f x) - f x) ≡⟨ cong (λ u → u - f x) (sym (fPres+ _ _)) ⟩
                               (f ((- x) + x) - f x) ≡⟨ cong (λ u → f u - f x) (+-linv x) ⟩
                               (f 0a - f x) ≡⟨ cong (λ u → u - f x) fPres0 ⟩
                               (0a - f x) ≡⟨ +-lid _ ⟩ (- f x) ∎)
            pres⋆ isHom = fPres⋆

    isPropIsCommAlgebraHom : (f : fst M → fst N) → isProp (IsCommAlgebraHom (snd M) f (snd N))
    isPropIsCommAlgebraHom f = isPropIsAlgebraHom
                                 (CommRing→Ring R)
                                 (snd (CommAlgebra→Algebra M))
                                 f
                                 (snd (CommAlgebra→Algebra N))

isPropIsCommAlgebra : (R : CommRing ℓ) {A : Type ℓ'}
  (0a 1a : A)
  (_+_ _·_ : A → A → A)
  (-_ : A → A)
  (_⋆_ : ⟨ R ⟩ → A → A)
  → isProp (IsCommAlgebra R 0a 1a _+_ _·_ -_ _⋆_)
isPropIsCommAlgebra R _ _ _ _ _ _ =
  isOfHLevelRetractFromIso 1 IsCommAlgebraIsoΣ
    (isPropΣ (isPropIsAlgebra _ _ _ _ _ _ _)
      (λ alg → isPropΠ2 λ _ _ → alg .IsAlgebra.is-set _ _))

𝒮ᴰ-CommAlgebra : (R : CommRing ℓ) → DUARel (𝒮-Univ ℓ') (CommAlgebraStr R) (ℓ-max ℓ ℓ')
𝒮ᴰ-CommAlgebra R =
  𝒮ᴰ-Record (𝒮-Univ _) (IsCommAlgebraEquiv {R = R})
    (fields:
      data[ 0a ∣ nul ∣ pres0 ]
      data[ 1a ∣ nul ∣ pres1 ]
      data[ _+_ ∣ bin ∣ pres+ ]
      data[ _·_ ∣ bin ∣ pres· ]
      data[ -_ ∣ autoDUARel _ _ ∣ pres- ]
      data[ _⋆_ ∣ autoDUARel _ _ ∣ pres⋆ ]
      prop[ isCommAlgebra ∣ (λ _ _ → isPropIsCommAlgebra _ _ _ _ _ _ _) ])
  where
  open CommAlgebraStr
  open IsAlgebraHom

  -- faster with some sharing
  nul = autoDUARel (𝒮-Univ _) (λ A → A)
  bin = autoDUARel (𝒮-Univ _) (λ A → A → A → A)

CommAlgebraPath : (R : CommRing ℓ) → (A B : CommAlgebra R ℓ') → (CommAlgebraEquiv A B) ≃ (A ≡ B)
CommAlgebraPath R = ∫ (𝒮ᴰ-CommAlgebra R) .UARel.ua

isGroupoidCommAlgebra : {R : CommRing ℓ} → isGroupoid (CommAlgebra R ℓ')
isGroupoidCommAlgebra A B = isOfHLevelRespectEquiv 2 (CommAlgebraPath _ _ _) (isSetAlgebraEquiv _ _)


module PushProdPull where

open import Categories.Category
open import Categories.Category.Cartesian.Bundle
open import Categories.Category.CartesianClosed.Canonical
open import Categories.Category.Core
open import Categories.Category.Product
open import Categories.Diagram.Pullback
open import Categories.Diagram.Pushout
open import Categories.Functor
open import Categories.Functor.Bifunctor
open import Data.Product
open import Function using (const)
open import Level
open import Relation.Binary.Core using (Rel)
open import Relation.Binary.PropositionalEquality
open Relation.Binary.PropositionalEquality.≡-Reasoning

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

data t : Set where
 ⊤ : t

!-unique-lemma : ∀ (x : t) → ⊤ ≡ x
!-unique-lemma ⊤ = refl

arrow : Category (suc zero) zero zero
arrow = record
  { Obj = Σ (Set × Set) (λ x → proj₁ x → proj₂ x)
  ; _⇒_ = arr
  ; _≈_ = _≡_
  ; id = (λ z → z) , λ z → z
  ; _∘_ = λ {A} {B} {C} f g → comp {A} {B} {C} f g
  ; assoc = refl
  ; sym-assoc = refl
  ; identityˡ = refl
  ; identityʳ = refl
  ; identity² = refl
  ; equiv = record { refl = refl ; sym = sym ; trans = trans }
  ; ∘-resp-≈ = λ {refl refl → refl}
  }
  where
  arr : Rel (Σ (Set × Set) (λ x → proj₁ x → proj₂ x)) zero
  arr ((a , b) , x1) ((c , d) , y1) = (a → c) × (b → d)
  comp : {A B C : Σ (Set × Set) (λ x → proj₁ x → proj₂ x)} → arr B C → arr A B → arr A C
  comp {A} {B} {C} (f0 , f1) (g0 , g1) = (λ x → f0 (g0 x)) , λ x → f1 (g1 x)

open Category arrow

SetC : Category (suc zero) zero zero
SetC =
 record
  { Obj = Set
  ; _⇒_ = λ x y → x → y
  ; _≈_ = _≡_
  ; id = λ x → x
  ; _∘_ = λ f g a → f (g a)
  ; assoc = refl
  ; sym-assoc = refl
  ; identityˡ = refl
  ; identityʳ = refl
  ; identity² = refl
  ; equiv = record { refl = refl ; sym = sym ; trans = trans }
  ; ∘-resp-≈ = λ {refl refl → refl}
  }

pushout-prod : (f g : Obj) → Obj
pushout-prod ((A0 , A1) , f) ((B0 , B1) , g) = {!   !}
-- Pushout SetC f×1 1×g
--  where
--  f×1 : A0 × B0 → A1 × B0
--  f×1 (a , b) = f a , b
--  1×g : A0 × B0 → A0 × B1
--  1×g (a , b) = a , g b

pullback-hom : (f g : Obj) → Obj
pullback-hom ((A0 , A1) , f) ((B0 , B1) , g) = {!   !}
-- Pullback SetC [f,1] [1,g]
--  where
--  [f,1] : (A1 → B1) → A0 → B1
--  [f,1] u = λ x → u (f x)
--  [1,g] : (A0 → B0) → A0 → B1
--  [1,g] u = λ x → g (u x)

pp-is-functor : Endobifunctor arrow
pp-is-functor =
 record
  { F₀ = F0
  ; F₁ = {!   !}
  ; identity = {!   !}
  ; homomorphism = {!   !}
  ; F-resp-≈ = {!   !}
  }
  where
  F0 : Category.Obj (Categories.Category.Product.Product arrow arrow) → Obj
  F0 x = pullback-hom (proj₁ x) (proj₂ x)
  F1 : {A B : Obj × Obj} → Product arrow arrow Categories.Category.[ A , B ] → arrow Categories.Category.[ F0 A , F0 B ]
  F1 {((A0 , A1) , a) , ((B0 , B1) , b)} {((C0 , C1) , c) , ((D0 , D1) , d)} ((u , v) , s , t) = {!   !}


-- Intuitionistic logic, PHOAS approach, initial encoding

module Pi.I (Indiv : Set) where

-- Types

data Ty : Set

Pred : Set
Pred = Indiv -> Ty

infixl 2 _&&_
infixl 1 _||_
infixr 0 _=>_
data Ty where
  UNIT   :             Ty
  _=>_   : Ty -> Ty -> Ty
  _&&_   : Ty -> Ty -> Ty
  _||_   : Ty -> Ty -> Ty
  FALSE  :             Ty
  FORALL : Pred     -> Ty
  EXISTS : Pred     -> Ty

infixr 0 _<=>_
_<=>_ : Ty -> Ty -> Ty
a <=> b = (a => b) && (b => a)

NOT : Ty -> Ty
NOT a = a => FALSE

TRUE : Ty
TRUE = FALSE => FALSE

-- Context and truth/individual judgement

data El : Set where
  mkTrue  : Ty    -> El
  mkIndiv : Indiv -> El

Cx : Set1
Cx = El -> Set

isTrue : Ty -> Cx -> Set
isTrue a tc = tc (mkTrue a)

isIndiv : Indiv -> Cx -> Set
isIndiv x tc = tc (mkIndiv x)

-- Terms

module I where
  infixl 2 _$$_
  infixl 1 _$_
  data Tm (tc : Cx) : Ty -> Set where
    var    : forall {a}     -> isTrue a tc                      -> Tm tc a
    lam'   : forall {a b}   -> (isTrue a tc -> Tm tc b)         -> Tm tc (a => b)
    _$_    : forall {a b}   -> Tm tc (a => b)   -> Tm tc a      -> Tm tc b
    pair'  : forall {a b}   -> Tm tc a          -> Tm tc b      -> Tm tc (a && b)
    fst    : forall {a b}   -> Tm tc (a && b)                   -> Tm tc a
    snd    : forall {a b}   -> Tm tc (a && b)                   -> Tm tc b
    left   : forall {a b}   -> Tm tc a                          -> Tm tc (a || b)
    right  : forall {a b}   -> Tm tc b                          -> Tm tc (a || b)
    case'  : forall {a b c} -> Tm tc (a || b)   -> (isTrue a tc -> Tm tc c) -> (isTrue b tc -> Tm tc c) -> Tm tc c
    pi'    : forall {p}     -> (forall {x} -> isIndiv x tc -> Tm tc (p x)) -> Tm tc (FORALL p)
    _$$_   : forall {p x}   -> Tm tc (FORALL p) -> isIndiv x tc -> Tm tc (p x)
    sig'   : forall {p x}   -> isIndiv x tc     -> Tm tc (p x)  -> Tm tc (EXISTS p)
    split' : forall {p x a} -> Tm tc (EXISTS p) -> (isTrue (p x) tc -> Tm tc a) -> Tm tc a
    abort  : forall {a}     -> Tm tc FALSE                      -> Tm tc a

  lam'' : forall {tc a b} -> (Tm tc a -> Tm tc b) -> Tm tc (a => b)
  lam'' f = lam' \x -> f (var x)

  case'' : forall {tc a b c} -> Tm tc (a || b) -> (Tm tc a -> Tm tc c) -> (Tm tc b -> Tm tc c) -> Tm tc c
  case'' xy f g = case' xy (\x -> f (var x)) (\y -> g (var y))

  split'' : forall {tc p x a} -> Tm tc (EXISTS p) -> (Tm tc (p x) -> Tm tc a) -> Tm tc a
  split'' x f = split' x \y -> f (var y)

  syntax lam''   (\a -> b)   = lam a => b
  syntax pair'   x y         = [ x , y ]
  syntax case''  xy (\x -> z1) (\y -> z2) = case xy of x => z1 or y => z2
  syntax pi'     (\x -> px)  = pi x => px
  syntax sig'    x px        = [ x ,, px ]
  syntax split'' x (\y -> z) = split x as y => z

  Thm : Ty -> Set1
  Thm a = forall {tc} -> Tm tc a
open I public

-- Example theorems

t214 : forall {p q : Pred} -> Thm (
  FORALL (\x -> p x || NOT (p x)) && FORALL (\x -> p x => EXISTS (\y -> q y)) =>
    FORALL (\x -> EXISTS (\y -> p x => q y)))
t214 =
  lam fg =>
    pi x =>
      case fst fg $$ x
        of px =>
          split snd fg $$ x $ px
            as qy =>
              [ x ,, lam _ => qy ]
        or npx =>
          [ x ,, lam px => abort (npx $ px) ]

l5 : forall {a} -> Thm (a && FALSE <=> FALSE)
l5 =
  [ lam xnt => snd xnt
  , lam nt  => abort nt
  ]

l10 : forall {a} -> Thm (a || FALSE <=> a)
l10 =
  [ lam xnt =>
      case xnt
        of x  => x
        or nt => abort nt
  , lam x => left x
  ]

l20 : forall {a} -> Thm ((FALSE => a) <=> TRUE)
l20 =
  [ lam _ => lam nt => nt
  , lam _ => lam nt => abort nt
  ]


open import Data.Bool using (Bool; false; true; _∧_)
open import Data.Maybe
open import Data.Nat
open import Data.Nat.Properties --using (_≟_; _<?_)
open import Data.Product
open import Relation.Binary.PropositionalEquality --using (_≡_; refl)
open import Relation.Nullary --using (Dec; yes; no)
--open import Relation.Nullary.Decidable --using (from-yes)

_ : Dec (0 ≡ 2)
_ = no (λ())
--_ = 0 ≟ 2
--_ = 0 ≟ 3

_ : Dec (2 ≡ 2)
_ = yes refl

fun : ℕ → ℕ → Maybe ℕ
fun x y with x <? y
... | yes _ = nothing
... | no _  = just y

_ : fun 3 5 ≡ nothing
_ = refl

_ : fun 3 2 ≡ just 2
_ = refl

--prop-:S : ∀ (x y)
--        → fun x y ≡ nothing
--        → x < y
--prop-:S 0 0 ()
--prop-:S 0 (suc y) refl = s≤s z≤n
--prop-:S (suc x) 0 ()
--prop-:S (suc x) (suc y) funsxsy≡nothing = s≤s (prop-:S x y ?)

--prop : ∀ (x y)
--     → fun x y ≡ nothing
--     → x < y
--prop x y with fun x y
--... | just _  = λ()
--... | nothing = λ{refl → ?} -- from-yes (x <? y)}
--
---- This fails because the pattern matching is incomplete,
---- but it shouldn't. There are no other cases
--prop' : ∀ (x y)
--      → fun x y ≡ nothing
--      → x < y
--prop' x y with fun x y | x <? y
--... | nothing | yes x<y = λ{refl → x<y}
--... | just _  | no _    = λ()
--... | _ | _ = ?

-- From: https://stackoverflow.com/a/63119870/1744344
prop : ∀ x y → fun x y ≡ nothing → x < y
prop x y with x <? y
... | yes p = λ _ → p

prop-old : ∀ x y → fun x y ≡ nothing → x < y
prop-old x y _ with x <? y
prop-old _ _ refl | yes p = p
prop-old _ _ () | no _

data Comp : Set where
  greater equal less : Comp

compare' : ℕ → ℕ → Comp
compare' x y with x <ᵇ y
...          | false with y <ᵇ x
...                  | false = equal
...                  | true  = greater
compare' x y | true = less

--fun2 : Maybe (ℕ × ℕ) → Maybe ℕ
--fun2 pair with pair
--... | just (x , y) with x <? y
--... | just (x , y) | yes _ = nothing
--... | just (x , y) | no _  = just y
--fun2 _ | nothing = nothing

prop-comp : ∀ x y
          → compare' x y ≡ greater
          → (y <ᵇ x) ≡ true
prop-comp x y _ with x <ᵇ y
... | false with y <ᵇ x
...         | true = refl
--same as
--prop-comp x y _ | false with y <ᵇ x
--prop-comp x y _ | false | true = refl

prop-comp← : ∀ x y
           → (y <ᵇ x) ≡ true
           → compare' x y ≡ greater
prop-comp← x y _ with x <ᵇ y
prop-comp← x y _  | true  with y <ᵇ x
prop-comp← _ _ _  | true | true = ?  -- This impossible!
prop-comp← _ _ () | true | false
prop-comp← x y _  | false with y <ᵇ x
prop-comp← _ _ _  | false | true = refl
--prop-comp← x y () | false | false

prop-new← : ∀ x y
           → (x <ᵇ y) ≡ false
           → (y <ᵇ x) ≡ false
           → compare' x y ≡ equal
--Why is this not possible?
--prop-new← x y _ with x <ᵇ y | y <ᵇ x
--... | false | false = refl
prop-new← x y _ _ with x <ᵇ y
... | false with y <ᵇ x
...   | false = refl

--a : inspect 3 2
--a = ?


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

open import Algebra.Structures.Bundles.Field

module Algebra.Linear.Morphism.Properties
  {k ℓᵏ} (K : Field k ℓᵏ)
  where

open import Level
open import Algebra.FunctionProperties as FP
import Algebra.Linear.Morphism.Definitions as LinearMorphismDefinitions
import Algebra.Morphism as Morphism
open import Relation.Binary using (Rel; Setoid)
open import Relation.Binary.Morphism.Structures
open import Algebra.Morphism
open import Algebra.Linear.Structures.VectorSpace K
open import Algebra.Linear.Structures.Bundles

module _
  {a₁ ℓ₁} (From : VectorSpace K a₁ ℓ₁)
  {a₂ ℓ₂} (To   : VectorSpace K a₂ ℓ₂)
  where

  open import Algebra.Linear.Morphism.VectorSpace K

  private
    module F = VectorSpace From
    module T = VectorSpace To

    open VectorSpaceField

  open F
    using ()
    renaming
      ( Carrier     to A
      ; _≈_         to _≈₁_
      ; refl        to ≈₁-refl
      ; sym         to ≈₁-sym
      ; trans       to ≈₁-trans
      ; setoid      to setoid₁
      ; _+_         to _+₁_
      ; +-cong      to +₁-cong
      ; +-identityˡ  to +₁-identityˡ
      ; _∙_         to _∙₁_
      ; ∙-identity  to ∙₁-identity
      ; ∙-absorbˡ    to ∙₁-absorbˡ
      ; ∙-absorbʳ    to ∙₁-absorbʳ
      ; 0#          to 0₁
      )

  open T
    using ()
    renaming
      ( Carrier    to B
      ; _≈_        to _≈₂_
      ; refl       to ≈₂-refl
      ; sym        to ≈₂-sym
      ; trans      to ≈₂-trans
      ; setoid     to setoid₂
      ; _+_        to _+₂_
      ; +-cong     to +₂-cong
      ; +-identityˡ to +₂-identityˡ
      ; _∙_        to _∙₂_
      ; ∙-identity to ∙₂-identity
      ; ∙-absorbˡ   to ∙₂-absorbˡ
      ; ∙-absorbʳ   to ∙₂-absorbʳ
      ; 0#         to 0₂
      )

  open import Function
  open import Function.Equality

  open LinearDefinitions (VectorSpace.Carrier From) (VectorSpace.Carrier To) _≈₂_

  linear : ∀ (f : setoid₁ ⟶ setoid₂)
         -> (∀ (a b : K') (u v : A) -> (f ⟨$⟩ (a ∙₁ u +₁ b ∙₁ v)) ≈₂ a ∙₂ (f ⟨$⟩ u) +₂ b ∙₂ (f ⟨$⟩ v))
         -> IsLinearMap From To (f ⟨$⟩_)
  linear f lin = record
    { isAbelianGroupMorphism = record
      { gp-homo = record
        { mn-homo = record
          { sm-homo = record
            { ⟦⟧-cong = cong f
            ; ∙-homo = λ x y →
              begin
                f ⟨$⟩ x +₁ y
              ≈⟨ cong f (+₁-cong (≈₁-sym (∙₁-identity x)) (≈₁-sym (∙₁-identity y))) ⟩
                f ⟨$⟩ (1ᵏ ∙₁ x +₁ 1ᵏ ∙₁ y)
              ≈⟨ lin 1ᵏ 1ᵏ x y ⟩
                1ᵏ ∙₂ (f ⟨$⟩ x) +₂ 1ᵏ ∙₂ (f ⟨$⟩ y)
              ≈⟨ +₂-cong (∙₂-identity (f ⟨$⟩ x)) (∙₂-identity (f ⟨$⟩ y)) ⟩
                (f ⟨$⟩ x) +₂ (f ⟨$⟩ y)
              ∎
            }
          ; ε-homo =
            begin
              f ⟨$⟩ 0₁
            ≈⟨ cong f (≈₁-trans (≈₁-sym (+₁-identityˡ 0₁)) (+₁-cong (≈₁-sym (∙₁-absorbˡ 0₁)) (≈₁-sym (∙₁-absorbˡ 0₁)))) ⟩
              f ⟨$⟩ 0ᵏ ∙₁ 0₁ +₁ 0ᵏ ∙₁ 0₁
            ≈⟨ lin 0ᵏ 0ᵏ 0₁ 0₁ ⟩
              0ᵏ ∙₂ (f ⟨$⟩ 0₁) +₂ 0ᵏ ∙₂ (f ⟨$⟩ 0₁)
            ≈⟨ ≈₂-trans (+₂-cong (∙₂-absorbˡ (f ⟨$⟩ 0₁)) (∙₂-absorbˡ (f ⟨$⟩ 0₁))) (+₂-identityˡ 0₂) ⟩
              0₂
            ∎
          }
        }
      }
    ; ∙-homo = λ c u →
      begin
        f ⟨$⟩ c ∙₁ u
      ≈⟨ cong f (≈₁-trans (≈₁-sym (+₁-identityˡ (c ∙₁ u))) (+₁-cong (≈₁-sym (∙₁-absorbˡ 0₁)) ≈₁-refl)) ⟩
        f ⟨$⟩ 0ᵏ ∙₁ 0₁ +₁ c ∙₁ u
      ≈⟨ lin 0ᵏ c 0₁ u ⟩
        0ᵏ ∙₂ (f ⟨$⟩ 0₁) +₂ c ∙₂ (f ⟨$⟩ u)
      ≈⟨ ≈₂-trans (+₂-cong (∙₂-absorbˡ (f ⟨$⟩ 0₁)) ≈₂-refl) (+₂-identityˡ (c ∙₂ (f ⟨$⟩ u))) ⟩
        c ∙₂ (f ⟨$⟩ u)
      ∎
    }
    where open import Relation.Binary.EqReasoning setoid₂


------------------------------------------------------------------------
-- The Agda standard library
--
-- Membership of vectors, along with some additional definitions.
------------------------------------------------------------------------

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

open import Relation.Binary using (Setoid; _Respects_)

module Data.Vec.Membership.Setoid {c ℓ} (S : Setoid c ℓ) where

open import Function
open import Data.Vec as Vec using (Vec; []; _∷_)
open import Data.Vec.Relation.Unary.Any as Any
  using (Any; here; there; index)
open import Data.Product using (∃; _×_; _,_)
open import Relation.Nullary using (¬_)
open import Relation.Unary using (Pred)

open Setoid S renaming (Carrier to A)

------------------------------------------------------------------------
-- Definitions

infix 4 _∈_ _∉_

_∈_ : A → ∀ {n} → Vec A n → Set _
x ∈ xs = Any (x ≈_) xs

_∉_ : A → ∀ {n} → Vec A n → Set _
x ∉ xs = ¬ x ∈ xs

------------------------------------------------------------------------
-- Operations

mapWith∈ : ∀ {b} {B : Set b} {n}
           (xs : Vec A n) → (∀ {x} → x ∈ xs → B) → Vec B n
mapWith∈ []       f = []
mapWith∈ (x ∷ xs) f = f (here refl) ∷ mapWith∈ xs (f ∘ there)

_∷=_ : ∀ {n} {xs : Vec A n} {x} → x ∈ xs → A → Vec A n
_∷=_ {xs = xs} x∈xs v = xs Vec.[ index x∈xs ]≔ v

------------------------------------------------------------------------
-- Finding and losing witnesses

module _ {p} {P : Pred A p} where

  find : ∀ {n} {xs : Vec A n} → Any P xs → ∃ λ x → x ∈ xs × P x
  find (here px)   = (_ , here refl , px)
  find (there pxs) with find pxs
  ... | (x , x∈xs , px) = (x , there x∈xs , px)

  lose : P Respects _≈_ → ∀ {x n} {xs : Vec A n} → x ∈ xs → P x → Any P xs
  lose resp x∈xs px = Any.map (flip resp px) x∈xs


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

module Cubical.HITs.Modulo.Base where

open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Prelude

open import Cubical.Data.Empty
open import Cubical.Data.Fin
open import Cubical.Data.Nat
open import Cubical.Data.Unit renaming (Unit to ⊤)

open import Cubical.Relation.Nullary

NonZero : ℕ → Type₀
NonZero 0 = ⊥
NonZero _ = ⊤

private
  variable
    ℓ : Level
    k : ℕ

-- The Modulo type is similar to the Fin type, but instead of being
-- inhabited by canonical values, the inhabitants are all naturals,
-- and paths are added between numbers that have the same residue.
--
-- This representation makes it easier to do certain arithmetic
-- without changing the modulus. For instance, we can just add any
-- natural to a Modulo k to get another, whereas with Fin k, we must
-- calculate the canonical representative.
--
-- The reason the path constructor is guarded is to avoid adding
-- non-trivial path structure to the k=0 case. If it were not guarded,
-- each `Modulo 0` would become like the circle, and guarding the
-- constructor is somewhat easier to work with than truncation.
--
-- Note also that unlike `Fin 0`, `Modulo 0` is equivalent to the naturals.
data Modulo (k : ℕ) : Type₀ where
  embed : (n : ℕ) → Modulo k
  pre-step : NonZero k → (n : ℕ) → embed n ≡ embed (k + n)

-- When we are working with k = suc k₀, the `step` alias is much
-- we can use this alias.
pattern step n i = pre-step _ n i

-- Helper to avoid having to case on `k` in certain places.
ztep : ∀{k} n → Path (Modulo k) (embed n) (embed (k + n))
ztep {0}     n = refl
ztep {suc k} n = step n

-- The standard eliminator for `Modulo`.
elim
  : (P : ∀ k → Modulo k → Type ℓ)
  → (e : ∀ k n → P k (embed n))
  → (st : ∀ k n → PathP (λ i → P (suc k) (step n i)) (e (suc k) n) (e (suc k) (suc k + n)))
  → (m : Modulo k) → P k m
elim P e st (embed n) = e _ n
elim {k = suc k} P e st (step n i) = st k n i


------------------------------------------------------------------------
-- A formalisation of one variant of the language χ, along with a
-- number of properties
--
-- Nils Anders Danielsson
------------------------------------------------------------------------

-- A description of the language χ, as well as some of the definitions
-- and proofs used in this development, can be found in "The language
-- χ" by Bengt Nordström (edited by me):
--
-- * https://chalmers.instructure.com/courses/10744/file_contents/course%20files/reading/The_language_chi.pdf
--
-- See also the following lecture slides:
--
-- * https://chalmers.instructure.com/courses/10744/file_contents/course%20files/lectures/L4.pdf
-- * https://chalmers.instructure.com/courses/10744/file_contents/course%20files/lectures/L5.pdf
-- * https://chalmers.instructure.com/courses/10744/file_contents/course%20files/lectures/L6.pdf

module README where

-- Atoms.

import Atom

-- Various constants.

import Constants

-- A specification of the language χ.

import Chi

-- The semantics is deterministic.

import Deterministic

-- Values.

import Values

-- Some cancellation lemmas.

import Cancellation

-- "Reasoning" combinators.

import Reasoning

-- The abstract syntax is a set, and the semantics is propositional.

import Propositional

-- The "terminates" relation.

import Termination

-- Compatibility lemmas.

import Compatibility

-- Some substitution lemmas.

import Substitution

-- Definitions of "free in" and "closed", along with some properties.

import Free-variables

-- Alpha-equivalence.

import Alpha-equivalence

-- Encoders and decoders.

import Coding

-- Encoder and decoder instances.

import Coding.Instances

-- Encoder and decoder instances for Atom.χ-ℕ-atoms.

import Coding.Instances.Nat

-- Internal coding.

import Internal-coding

-- Some χ program combinators.

import Combinators

-- The rec construction can be encoded using λ-terms.

import Recursion-without-rec

-- Definition of the size of an expression, along with some
-- properties.

import Expression-size

-- A self-interpreter (without correctness proof).

import Self-interpreter

-- Partial functions, computability.

import Computability

-- The halting problem.

import Halting-problem

-- Rice's theorem.

import Rices-theorem

-- A theorem related to pointwise equality.

import Pointwise-equality


open import Type

module Graph.Category {ℓ₁ ℓ₂} {V : Type{ℓ₁}} where

open import Data.Tuple as Tuple using ()
open import Functional
open import Logic.Propositional
import      Lvl
open import Graph{ℓ₁}{ℓ₂}(V)
open import Graph.Walk{ℓ₁}{ℓ₂}{V}
open import Graph.Walk.Proofs{ℓ₁}{ℓ₂}{V}
open import Relator.Equals
open import Relator.Equals.Proofs
open import Relator.Equals.Proofs.Equiv
open import Structure.Category
import      Structure.Categorical.Names as Names
open import Structure.Categorical.Properties
open import Structure.Function
open import Structure.Operator
open import Structure.Relator.Properties
open import Syntax.Transitivity

module _ (_⟶_ : Graph) where
  private variable a b c d : V
  private variable e e₁ e₂ e₃ : a ⟶ b
  private variable w w₁ w₂ w₃ : Walk(_⟶_) a b

  Walk-transitivity-raw-identityₗ-raw : (Walk-transitivity-raw at w ≡ w)
  Walk-transitivity-raw-identityₗ-raw = [≡]-intro

  Walk-transitivity-raw-identityᵣ-raw : (Walk-transitivity-raw w at ≡ w)
  Walk-transitivity-raw-identityᵣ-raw {a}{.a} {Walk.at}                  = [≡]-intro
  Walk-transitivity-raw-identityᵣ-raw {a}{c}  {Walk.prepend {b = b} e w} = congruence₁(prepend e) (Walk-transitivity-raw-identityᵣ-raw {b}{c} {w})

  Walk-transitivity-raw-associativity-raw : Names.Morphism.Associativity{Obj = V}(\{w} → Walk-transitivity-raw{_⟶_ = _⟶_}{z = w})
  Walk-transitivity-raw-associativity-raw {a}{b}{c}{d}  {Walk.at}          {w₂}{w₃} = [≡]-intro
  Walk-transitivity-raw-associativity-raw {a}{b}{c}{d}  {Walk.prepend e w₁}{w₂}{w₃} = congruence₁(prepend e) (Walk-transitivity-raw-associativity-raw {a}{b}{c}{_} {w₁}{w₂}{w₃})

  instance
    Walk-transitivity-raw-identityₗ : Morphism.Identityₗ{Obj = V}(\{w} → Walk-transitivity-raw{z = w})(at)
    Walk-transitivity-raw-identityₗ = Morphism.intro Walk-transitivity-raw-identityₗ-raw

  instance
    Walk-transitivity-raw-identityᵣ : Morphism.Identityᵣ{Obj = V}(\{w} → Walk-transitivity-raw{z = w})(at)
    Walk-transitivity-raw-identityᵣ = Morphism.intro Walk-transitivity-raw-identityᵣ-raw

  instance
    Walk-transitivity-raw-identity : Morphism.Identity{Obj = V}(\{w} → Walk-transitivity-raw{_⟶_ = _⟶_}{z = w})(at)
    Walk-transitivity-raw-identity = [∧]-intro Walk-transitivity-raw-identityₗ Walk-transitivity-raw-identityᵣ

  -- The category arising from a graph by its vertices and walks.
  -- Note that `Walk` is defined so that every sequence of edges have an unique walk. For example `ReflexitiveTransitiveClosure` (a relation equivalent to a walk) would not work.
  free-category : Category(Walk(_⟶_))
  Category._∘_ free-category = swap(transitivity(Walk(_⟶_)))
  Category.id  free-category = reflexivity(Walk(_⟶_))
  BinaryOperator.congruence (Category.binaryOperator free-category) [≡]-intro [≡]-intro = [≡]-intro
  Morphism.Associativity.proof (Category.associativity free-category) {a}{b}{c}{d} {w₁}{w₂}{w₃} = symmetry(_≡_) (Walk-transitivity-raw-associativity-raw {d}{c}{b}{a} {w₃}{w₂}{w₁})
  Morphism.Identityₗ.proof (Tuple.left  (Category.identity free-category)) = Walk-transitivity-raw-identityᵣ-raw
  Morphism.Identityᵣ.proof (Tuple.right (Category.identity free-category)) = Walk-transitivity-raw-identityₗ-raw


{-# OPTIONS --safe #-}
module Cubical.Algebra.DirectSum.DirectSumHIT.UniversalProperty where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels

open import Cubical.Data.Sigma

open import Cubical.Algebra.Group.Morphisms
open import Cubical.Algebra.Group.MorphismProperties
open import Cubical.Algebra.AbGroup
open import Cubical.Algebra.AbGroup.Instances.DirectSumHIT
open import Cubical.Algebra.DirectSum.DirectSumHIT.Base

private variable
  ℓ ℓ' ℓ'' : Level

open AbGroupStr
open IsGroupHom

-----------------------------------------------------------------------------
-- Definition

module _
  (Idx              : Type ℓ)
  (G                : (k : Idx) → Type ℓ')
  (Gstr             : (k : Idx) → AbGroupStr (G k))
  (HAbGr@(H , Hstr) : AbGroup ℓ'')
  (fHhom            : (k : Idx) → AbGroupHom ((G k) , (Gstr k)) HAbGr)
  where

  private
    fH : (k : Idx) → G k → H
    fH = λ k → fst (fHhom k)
    fHstr : (k : Idx) → _
    fHstr = λ k → snd (fHhom k)

  injₖ : (k : Idx) → G k → ⊕HIT Idx G Gstr
  injₖ k a = base k a

  injₖ-hom : (k : Idx) → AbGroupHom ((G k) , (Gstr k)) (⊕HIT-AbGr Idx G Gstr)
  injₖ-hom k = injₖ k , (makeIsGroupHom λ a b → sym (base-add _ _ _) )

  -- universalProperty :
  ⊕HIT→H : ⊕HIT Idx G Gstr → H
  ⊕HIT→H = DS-Rec-Set.f _ _ _ _ (is-set Hstr)
            (0g Hstr)
            (λ k a → fH k a)
            (Hstr ._+_)
            (+Assoc Hstr)
            (+IdR Hstr)
            (+Comm Hstr)
            (λ k → pres1 (fHstr k))
            λ k a b → sym (pres· (fHstr k) _ _)

  ⊕HIT→H-hom : AbGroupHom (⊕HIT-AbGr Idx G Gstr) HAbGr
  ⊕HIT→H-hom = ⊕HIT→H , (makeIsGroupHom (λ _ _ → refl))

  -- Universal Property
  up∃⊕HIT : (k : Idx) → fHhom k ≡ compGroupHom (injₖ-hom k) ⊕HIT→H-hom
  up∃⊕HIT k = ΣPathTransport→PathΣ _ _
             ((funExt (λ _ → refl))
             , isPropIsGroupHom _ _ _ _)

  upUnicity⊕HIT : (hhom : AbGroupHom (⊕HIT-AbGr Idx G Gstr) HAbGr) →
                  (eqInj : (k : Idx) → fHhom k ≡ compGroupHom (injₖ-hom k) hhom)
                  → hhom ≡ ⊕HIT→H-hom
  upUnicity⊕HIT (h , hstr) eqInj = ΣPathTransport→PathΣ _ _
                             (helper , isPropIsGroupHom _ _ _ _)
    where

    helper : _
    helper = funExt (DS-Ind-Prop.f _ _ _ _ (λ _ → is-set Hstr _ _)
                    (pres1 hstr)
                    (λ k a → sym (funExt⁻ (cong fst (eqInj k)) a))
                    λ {U V} ind-U ind-V → pres· hstr _ _ ∙ cong₂ (_+_ Hstr) ind-U ind-V)


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

open import LogicalFormulae
open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Functions.Definition
open import Setoids.Setoids
open import Setoids.Subset
open import Graphs.Definition
open import Sets.FinSet.Definition
open import Numbers.Naturals.Semiring

module Graphs.Colouring where

Colouring : (n : ℕ) {a b c : _} {V' : Set a} {V : Setoid {a} {b} V'} (G : Graph c V) → Set (a ⊔ c)
Colouring n {V' = V'} G = {x y : V'} → (Graph._<->_ G x y) → FinSet n

inheritColouring : {n : ℕ} {a b c : _} {V' : Set a} {V : Setoid {a} {b} V'} {G : Graph c V} → (k : Colouring n G) → {e : _} {pred : V' → Set e} {sub : subset V pred} {d : _} {_<->'_ : Rel {_} {d} (Sg V' pred)} (H : Subgraph G sub _<->'_) → Colouring n (subgraphIsGraph H)
inheritColouring k H {v1 , _} {v2 , _} x-y = k (Subgraph.inherits H x-y)

Monochromatic : {a b c : Level} {V' : Set a} {V : Setoid {a} {b} V'} {G : Graph c V} {n : ℕ} → (k : Colouring n G) → {d e : _} {pred : V' → Set d} {sub : subset V pred} {_<->'_ : Rel {_} {e} (Sg V' pred)} (H : Subgraph G sub _<->'_) → Set (a ⊔ d ⊔ e)
Monochromatic {V' = V'} {n = n} k {pred = pred} {sub = sub} {_<->'_} H = Sg (FinSet n) (λ c → {x : Sg V' pred} {y : Sg V' pred} (edge : x <->' y) → (inheritColouring k H) edge ≡ c)


module Cats.Category.Lift where

open import Relation.Binary using (IsEquivalence)
open import Level as L using (Level ; suc ; _⊔_ ; lift ; lower)

open import Cats.Category.Base

open IsEquivalence

LCategory : (l : Level) → Set (suc l)
LCategory l = Category l l l

Lift : ∀ {lo la l≈ lo′ la′ l≈′} → Category lo la l≈
  → Category (lo ⊔ lo′) (la ⊔ la′) (l≈ ⊔ l≈′)
Lift {lo′ = lo′} {la′} {l≈′} C = record
    { Obj = L.Lift lo′ C.Obj
    ; _⇒_ = λ A B → L.Lift la′ (lower A C.⇒ lower B)
    ; _≈_ = λ f g → L.Lift l≈′ (lower f C.≈ lower g)
    ; id = lift C.id
    ; _∘_ = λ f g → lift (lower f C.∘ lower g)
    ; equiv = record
        { refl = lift (refl C.equiv)
        ; sym = λ eq → lift (sym C.equiv (lower eq))
        ; trans = λ eq₁ eq₂ → lift (trans C.equiv (lower eq₁) (lower eq₂))
        }
    ; ∘-resp = λ eq₁ eq₂ → lift (C.∘-resp (lower eq₁) (lower eq₂))
    ; id-r = lift C.id-r
    ; id-l = lift C.id-l
    ; assoc = lift C.assoc
    }
  where
    module C = Category C

LiftEq : ∀ {lo la l≈} → Category lo la l≈
  → LCategory (lo ⊔ la ⊔ l≈)
LiftEq {lo} {la} {l≈} = Lift {lo′ = la ⊔ l≈} {la′ = lo ⊔ l≈} {l≈′ = lo ⊔ la}


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

module Categories.Utils.EqReasoning where

open import Level
open import Relation.Binary.PropositionalEquality
open import Data.Product using (Σ; _,_; _×_)

open import Categories.Utils.Product

subst₂-sym-subst₂ : {ℓa ℓb ℓp : Level} {A : Set ℓa} {B : Set ℓb} {a₁ a₂ : A} {b₁ b₂ : B}
                    (R : A → B → Set ℓp) {p : R a₁ b₁} (eq₁ : a₁ ≡ a₂) (eq₂ : b₁ ≡ b₂) →
                    subst₂ R (sym eq₁) (sym eq₂) (subst₂ R eq₁ eq₂ p) ≡ p
subst₂-sym-subst₂ R refl refl = refl

subst₂-subst₂-sym : {ℓa ℓb ℓp : Level} {A : Set ℓa} {B : Set ℓb} {a₁ a₂ : A} {b₁ b₂ : B}
                    (R : A → B → Set ℓp) {p : R a₁ b₁} (eq₁ : a₂ ≡ a₁) (eq₂ : b₂ ≡ b₁) →
                    subst₂ R eq₁ eq₂ (subst₂ R (sym eq₁) (sym eq₂) p) ≡ p
subst₂-subst₂-sym R refl refl = refl

subst₂-subst₂ : {ℓa ℓb ℓp : Level} {A : Set ℓa} {B : Set ℓb} {a₁ a₂ a₃ : A} {b₁ b₂ b₃ : B}
              (R : A → B → Set ℓp) {p : R a₁ b₁}
              (eq-a₁₂ : a₁ ≡ a₂) (eq-a₂₃ : a₂ ≡ a₃)
              (eq-b₁₂ : b₁ ≡ b₂) (eq-b₂₃ : b₂ ≡ b₃) →
              subst₂ R eq-a₂₃ eq-b₂₃ (subst₂ R eq-a₁₂ eq-b₁₂ p) ≡ subst₂ R (trans eq-a₁₂ eq-a₂₃) (trans eq-b₁₂ eq-b₂₃) p
subst₂-subst₂ R refl refl refl refl = refl

subst₂-app : ∀ {a₁ a₂ a₃ a₄ b₁ b₂} {A₁ : Set a₁} {A₂ : Set a₂} {A₃ : Set a₃} {A₄ : Set a₄}
                  (B₁ : A₁ → A₃ → Set b₁) {B₂ : A₂ → A₄ → Set b₂}
                  {f : A₂ → A₁} {h : A₄ → A₃} {x₁ x₂ : A₂} {x₃ x₄ : A₄} (y : B₂ x₁ x₃)
                  (g : ∀ w z → B₂ w z → B₁ (f w) (h z)) (eq₁ : x₁ ≡ x₂) (eq₂ : x₃ ≡ x₄) →
                  subst₂ B₁ (cong f eq₁) (cong h eq₂) (g x₁ x₃ y) ≡ g x₂ x₄ (subst₂ B₂ eq₁ eq₂ y)
subst₂-app _ _ _ refl refl = refl

subst₂-prod : ∀ {ℓa ℓb ℓr ℓs} {A : Set ℓa} {B : Set ℓb}
      (R : A → A → Set ℓr) (S : B → B → Set ℓs) {a b c d : A} {x y z w : B}
      {f : R a b} {g : S x y}
      (eq₁ : a ≡ c) (eq₂ : b ≡ d) (eq₃ : x ≡ z) (eq₄ : y ≡ w) →
      subst₂ (λ { (p , q) (r , s) → R p r × S q s })
      (sym (cong₂ _,_ (sym eq₁) (sym eq₃)))
      (sym (cong₂ _,_ (sym eq₂) (sym eq₄)))
      (f , g)
      ≡
      (subst₂ R eq₁ eq₂ f , subst₂ S eq₃ eq₄ g)
subst₂-prod R S refl refl refl refl = refl

------------------
-- For reasoning with things from Categories.Utils.Product
subst₂-expand : ∀ {a b ℓ ℓ′ p q} {A : Set a} {B : Set b}
  {P : A → Set p} {Q : B → Set q}
  (_∙_ : A → B → Set ℓ) → (_∘_ : ∀ {x y} → P x → Q y → Set ℓ′) →
  {a₁ a₂ : A} {b₁ b₂ : B} {p₁ p₂ : P a₁} {q₁ q₂ : Q b₁}
  (eq₁ : a₁ ≡ a₂) (eq₂ : p₁ ≡ p₂) (eq₃ : b₁ ≡ b₂) (eq₄ : q₁ ≡ q₂) →
  (F : a₁ ∙ b₁) → (G : p₁ ∘ q₁) →
  subst₂ (_∙_ -< _×_ >- _∘_)
      (cong₂ _,_ eq₁ eq₂)
      (cong₂ _,_ eq₃ eq₄)
      (F , G) ≡ (subst₂ _∙_ eq₁ eq₃ F , subst₂ _∘_ eq₂ eq₄ G)
subst₂-expand T₁ T₂ refl refl refl refl F G = refl


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

module sets.unit where

open import level

record ⊤ {i : Level} : Set i where
  constructor tt

⊤-elim : ∀ {i j} (P : ⊤ {i} → Set j) → P tt → (x : ⊤) → P x
⊤-elim P ptt tt = ptt