------------------------------------------------------------------------
-- The Agda standard library
--
-- Vectors
------------------------------------------------------------------------

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

module Data.Vec where

open import Data.Nat
open import Data.Fin using (Fin; zero; suc)
open import Data.List.Base as List using (List)
open import Data.Product as Prod using (∃; ∃₂; _×_; _,_)
open import Data.These as These using (These; this; that; these)
open import Function
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
open import Relation.Nullary using (yes; no)
open import Relation.Unary using (Pred; Decidable)

------------------------------------------------------------------------
-- Types

infixr 5 _∷_

data Vec {a} (A : Set a) : ℕ → Set a where
  []  : Vec A zero
  _∷_ : ∀ {n} (x : A) (xs : Vec A n) → Vec A (suc n)

infix 4 _[_]=_

data _[_]=_ {a} {A : Set a} :
            {n : ℕ} → Vec A n → Fin n → A → Set a where
  here  : ∀ {n}     {x}   {xs : Vec A n} → x ∷ xs [ zero ]= x
  there : ∀ {n} {i} {x y} {xs : Vec A n}
          (xs[i]=x : xs [ i ]= x) → y ∷ xs [ suc i ]= x

------------------------------------------------------------------------
-- Basic operations

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

  head : ∀ {n} → Vec A (1 + n) → A
  head (x ∷ xs) = x

  tail : ∀ {n} → Vec A (1 + n) → Vec A n
  tail (x ∷ xs) = xs

  lookup : ∀ {n} → Vec A n → Fin n → A
  lookup (x ∷ xs) zero    = x
  lookup (x ∷ xs) (suc i) = lookup xs i

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

  remove : ∀ {n} → Vec A (suc n) → Fin (suc n) → Vec A n
  remove (_ ∷ xs)     zero     = xs
  remove (x ∷ [])     (suc ())
  remove (x ∷ y ∷ xs) (suc i)  = x ∷ remove (y ∷ xs) i

  updateAt : ∀ {n} → Fin n → (A → A) → Vec A n → Vec A n
  updateAt zero    f (x ∷ xs) = f x ∷ xs
  updateAt (suc i) f (x ∷ xs) = x   ∷ updateAt i f xs

  -- xs [ i ]%= f  modifies the i-th element of xs according to f

  infixl 6 _[_]%=_

  _[_]%=_ : ∀ {n} → Vec A n → Fin n → (A → A) → Vec A n
  xs [ i ]%= f = updateAt i f xs

  -- xs [ i ]≔ y  overwrites the i-th element of xs with y

  infixl 6 _[_]≔_

  _[_]≔_ : ∀ {n} → Vec A n → Fin n → A → Vec A n
  xs [ i ]≔ y = xs [ i ]%= const y

------------------------------------------------------------------------
-- Operations for transforming vectors

map : ∀ {a b n} {A : Set a} {B : Set b} →
      (A → B) → Vec A n → Vec B n
map f []       = []
map f (x ∷ xs) = f x ∷ map f xs

-- Concatenation.

infixr 5 _++_

_++_ : ∀ {a m n} {A : Set a} → Vec A m → Vec A n → Vec A (m + n)
[]       ++ ys = ys
(x ∷ xs) ++ ys = x ∷ (xs ++ ys)

concat : ∀ {a m n} {A : Set a} →
         Vec (Vec A m) n → Vec A (n * m)
concat []         = []
concat (xs ∷ xss) = xs ++ concat xss

-- Align and Zip.

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

  alignWith : ∀ {m n} → (These A B → C) → Vec A m → Vec B n → Vec C (m ⊔ n)
  alignWith f []         bs       = map (f ∘′ that) bs
  alignWith f as@(_ ∷ _) []       = map (f ∘′ this) as
  alignWith f (a ∷ as)   (b ∷ bs) = f (these a b) ∷ alignWith f as bs

  zipWith : ∀ {n} → (A → B → C) → Vec A n → Vec B n → Vec C n
  zipWith f []       []       = []
  zipWith f (x ∷ xs) (y ∷ ys) = f x y ∷ zipWith f xs ys

  unzipWith : ∀ {n} → (A → B × C) → Vec A n → Vec B n × Vec C n
  unzipWith f []       = [] , []
  unzipWith f (a ∷ as) = Prod.zip _∷_ _∷_ (f a) (unzipWith f as)

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

  align : ∀ {m n} → Vec A m → Vec B n → Vec (These A B) (m ⊔ n)
  align = alignWith id

  zip : ∀ {n} → Vec A n → Vec B n → Vec (A × B) n
  zip = zipWith _,_

  unzip : ∀ {n} → Vec (A × B) n → Vec A n × Vec B n
  unzip = unzipWith id

-- Interleaving.

infixr 5 _⋎_

_⋎_ : ∀ {a m n} {A : Set a} →
      Vec A m → Vec A n → Vec A (m +⋎ n)
[]       ⋎ ys = ys
(x ∷ xs) ⋎ ys = x ∷ (ys ⋎ xs)

-- Pointwise application

infixl 4 _⊛_

_⊛_ : ∀ {a b n} {A : Set a} {B : Set b} →
      Vec (A → B) n → Vec A n → Vec B n
[]       ⊛ _        = []
(f ∷ fs) ⊛ (x ∷ xs) = f x ∷ (fs ⊛ xs)

-- Multiplication

infixl 1 _>>=_

_>>=_ : ∀ {a b m n} {A : Set a} {B : Set b} →
        Vec A m → (A → Vec B n) → Vec B (m * n)
xs >>= f = concat (map f xs)

infixl 4 _⊛*_

_⊛*_ : ∀ {a b m n} {A : Set a} {B : Set b} →
       Vec (A → B) m → Vec A n → Vec B (m * n)
fs ⊛* xs = fs >>= λ f → map f xs

allPairs : ∀ {a b m n} {A : Set a} {B : Set b} →
           Vec A m → Vec B n → Vec (A × B) (m * n)
allPairs xs ys = map _,_ xs ⊛* ys

------------------------------------------------------------------------
-- Operations for reducing vectors

foldr : ∀ {a b} {A : Set a} (B : ℕ → Set b) {m} →
        (∀ {n} → A → B n → B (suc n)) →
        B zero →
        Vec A m → B m
foldr b _⊕_ n []       = n
foldr b _⊕_ n (x ∷ xs) = x ⊕ foldr b _⊕_ n xs

foldr₁ : ∀ {a} {A : Set a} {m} →
         (A → A → A) → Vec A (suc m) → A
foldr₁ _⊕_ (x ∷ [])     = x
foldr₁ _⊕_ (x ∷ y ∷ ys) = x ⊕ foldr₁ _⊕_ (y ∷ ys)

foldl : ∀ {a b} {A : Set a} (B : ℕ → Set b) {m} →
        (∀ {n} → B n → A → B (suc n)) →
        B zero →
        Vec A m → B m
foldl b _⊕_ n []       = n
foldl b _⊕_ n (x ∷ xs) = foldl (λ n → b (suc n)) _⊕_ (n ⊕ x) xs

foldl₁ : ∀ {a} {A : Set a} {m} →
         (A → A → A) → Vec A (suc m) → A
foldl₁ _⊕_ (x ∷ xs) = foldl _ _⊕_ x xs

-- Special folds

sum : ∀ {n} → Vec ℕ n → ℕ
sum = foldr _ _+_ 0

count : ∀ {a p} {A : Set a} {P : Pred A p} → Decidable P →
        ∀ {n} → Vec A n → ℕ
count P? []       = zero
count P? (x ∷ xs) with P? x
... | yes _ = suc (count P? xs)
... | no  _ = count P? xs

------------------------------------------------------------------------
-- Operations for building vectors

[_] : ∀ {a} {A : Set a} → A → Vec A 1
[ x ] = x ∷ []

replicate : ∀ {a n} {A : Set a} → A → Vec A n
replicate {n = zero}  x = []
replicate {n = suc n} x = x ∷ replicate x

tabulate : ∀ {n a} {A : Set a} → (Fin n → A) → Vec A n
tabulate {zero}  f = []
tabulate {suc n} f = f zero ∷ tabulate (f ∘ suc)

allFin : ∀ n → Vec (Fin n) n
allFin _ = tabulate id

------------------------------------------------------------------------
-- Operations for dividing vectors

splitAt : ∀ {a} {A : Set a} m {n} (xs : Vec A (m + n)) →
          ∃₂ λ (ys : Vec A m) (zs : Vec A n) → xs ≡ ys ++ zs
splitAt zero    xs                = ([] , xs , refl)
splitAt (suc m) (x ∷ xs)          with splitAt m xs
splitAt (suc m) (x ∷ .(ys ++ zs)) | (ys , zs , refl) =
  ((x ∷ ys) , zs , refl)

take : ∀ {a} {A : Set a} m {n} → Vec A (m + n) → Vec A m
take m xs          with splitAt m xs
take m .(ys ++ zs) | (ys , zs , refl) = ys

drop : ∀ {a} {A : Set a} m {n} → Vec A (m + n) → Vec A n
drop m xs          with splitAt m xs
drop m .(ys ++ zs) | (ys , zs , refl) = zs

group : ∀ {a} {A : Set a} n k (xs : Vec A (n * k)) →
        ∃ λ (xss : Vec (Vec A k) n) → xs ≡ concat xss
group zero    k []                  = ([] , refl)
group (suc n) k xs                  with splitAt k xs
group (suc n) k .(ys ++ zs)         | (ys , zs , refl) with group n k zs
group (suc n) k .(ys ++ concat zss) | (ys , ._ , refl) | (zss , refl) =
  ((ys ∷ zss) , refl)

split : ∀ {a n} {A : Set a} → Vec A n → Vec A ⌈ n /2⌉ × Vec A ⌊ n /2⌋
split []           = ([]     , [])
split (x ∷ [])     = (x ∷ [] , [])
split (x ∷ y ∷ xs) = Prod.map (x ∷_) (y ∷_) (split xs)

------------------------------------------------------------------------
-- Operations for converting between lists

toList : ∀ {a n} {A : Set a} → Vec A n → List A
toList []       = List.[]
toList (x ∷ xs) = List._∷_ x (toList xs)

fromList : ∀ {a} {A : Set a} → (xs : List A) → Vec A (List.length xs)
fromList List.[]         = []
fromList (List._∷_ x xs) = x ∷ fromList xs

------------------------------------------------------------------------
-- Operations for reversing vectors

reverse : ∀ {a n} {A : Set a} → Vec A n → Vec A n
reverse {A = A} = foldl (Vec A) (λ rev x → x ∷ rev) []

infixl 5 _∷ʳ_

_∷ʳ_ : ∀ {a n} {A : Set a} → Vec A n → A → Vec A (1 + n)
[]       ∷ʳ y = [ y ]
(x ∷ xs) ∷ʳ y = x ∷ (xs ∷ʳ y)

initLast : ∀ {a n} {A : Set a} (xs : Vec A (1 + n)) →
           ∃₂ λ (ys : Vec A n) (y : A) → xs ≡ ys ∷ʳ y
initLast {n = zero}  (x ∷ [])         = ([] , x , refl)
initLast {n = suc n} (x ∷ xs)         with initLast xs
initLast {n = suc n} (x ∷ .(ys ∷ʳ y)) | (ys , y , refl) =
  ((x ∷ ys) , y , refl)

init : ∀ {a n} {A : Set a} → Vec A (1 + n) → Vec A n
init xs         with initLast xs
init .(ys ∷ʳ y) | (ys , y , refl) = ys

last : ∀ {a n} {A : Set a} → Vec A (1 + n) → A
last xs         with initLast xs
last .(ys ∷ʳ y) | (ys , y , refl) = y


------------------------------------------------------------------------
-- INCREMENTAL λ-CALCULUS
--
-- The syntax of function types (Fig. 1a).
------------------------------------------------------------------------

module Parametric.Syntax.Type where

open import Base.Data.DependentList

-- This is a module in the Parametric.* hierarchy, so it defines
-- an extension point for plugins. In this case, the plugin can
-- choose the syntax for data types. We always provide a binding
-- called "Structure" that defines the type of the value that has
-- to be provided by the plugin. In this case, the plugin has to
-- provide a type, so we say "Structure = Set".

Structure : Set₁
Structure = Set

-- Here is the parametric module that defines the syntax of
-- simply types in terms of the syntax of base types. In the
-- Parametric.* hierarchy, we always call these parametric
-- modules "Structure". Note that in Agda, there is a separate
-- namespace for module names and for other bindings, so this
-- doesn't clash with the "Structure" above.
--
-- The idea behind all these structures is that the parametric
-- module lifts some structure of the plugin to the corresponding
-- structure in the full language. In this case, we lift the
-- structure of base types to the structure of simple types.
-- Maybe not the best choice of names, but it seemed clever at
-- the time.

module Structure (Base : Structure) where
  infixr 5 _⇒_

  -- A simple type is either a base type or a function types.
  -- Note that we can use our module parameter "Base" here just
  -- like any other type.
  data Type : Set where
    base : (ι : Base) → Type
    _⇒_ : (σ : Type) → (τ : Type) → Type

  -- We also provide the definitions of contexts of the newly
  -- defined simple types, variables as de Bruijn indices
  -- pointing into such a context, and sets of bound variables.
  open import Base.Syntax.Context Type public
  open import Base.Syntax.Vars Type public

  -- Internalize a context to a type.
  --
  -- Is there a better name for this function?
  internalizeContext : (Σ : Context) (τ′ : Type) → Type
  internalizeContext ∅ τ′ = τ′
  internalizeContext (τ • Σ) τ′ = τ ⇒ internalizeContext Σ τ′


-- Limits.
-- Heavily inspired by https://github.com/UniMath/UniMath/blob/master/UniMath/CategoryTheory/limits/graphs/limits.v
-- (except we're using categories instead of graphs to index limits)

{-# OPTIONS --safe #-}
module Cubical.Categories.Limits.Limits where

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

open import Cubical.Data.Sigma.Base

open import Cubical.Categories.Category
open import Cubical.Categories.Functor

open import Cubical.HITs.PropositionalTruncation.Base

module _ {ℓJ ℓJ' ℓC ℓC' : Level} {J : Category ℓJ ℓJ'} {C : Category ℓC ℓC'} where
  open Category
  open Functor

  private
    ℓ = ℓ-max (ℓ-max (ℓ-max ℓJ ℓJ') ℓC) ℓC'

  record Cone (D : Functor J C) (c : ob C) : Type (ℓ-max (ℓ-max ℓJ ℓJ') ℓC') where
    constructor cone

    field
      coneOut : (v : ob J) → C [ c , F-ob D v ]
      coneOutCommutes : {u v : ob J} (e : J [ u , v ]) →
                        coneOut u ⋆⟨ C ⟩ D .F-hom e ≡ coneOut v

  open Cone

  cone≡ : {D : Functor J C} {c : ob C} → {c1 c2 : Cone D c}
        → ((v : ob J) → coneOut c1 v ≡ coneOut c2 v) → c1 ≡ c2
  coneOut (cone≡ h i) v = h v i
  coneOutCommutes (cone≡ {D} {_} {c1} {c2} h i) {u} {v} e =
    isProp→PathP (λ j → isSetHom C (h u j ⋆⟨ C ⟩ D .F-hom e) (h v j))
                 (coneOutCommutes c1 e) (coneOutCommutes c2 e) i

  -- TODO: can we automate this a bit?
  isSetCone : (D : Functor J C) (c : ob C) → isSet (Cone D c)
  isSetCone D c = isSetRetract toConeΣ fromConeΣ (λ _ → refl)
                               (isSetΣSndProp (isSetΠ (λ _ → isSetHom C))
                                              (λ _ → isPropImplicitΠ2 (λ _ _ → isPropΠ (λ _ → isSetHom C _ _))))
    where
    ConeΣ = Σ[ f ∈ ((v : ob J) → C [ c , F-ob D v ]) ]
               ({u v : ob J} (e : J [ u , v ]) → f u ⋆⟨ C ⟩ D .F-hom e ≡ f v)

    toConeΣ : Cone D c → ConeΣ
    fst (toConeΣ x) = coneOut x
    snd (toConeΣ x) = coneOutCommutes x

    fromConeΣ : ConeΣ → Cone D c
    coneOut (fromConeΣ x) = fst x
    coneOutCommutes (fromConeΣ x) = snd x

  isConeMor : {c1 c2 : ob C} {D : Functor J C}
              (cc1 : Cone D c1) (cc2 : Cone D c2)
            → C [ c1 , c2 ] → Type (ℓ-max ℓJ ℓC')
  isConeMor cc1 cc2 f = (v : ob J) → f ⋆⟨ C ⟩ coneOut cc2 v ≡ coneOut cc1 v

  isPropIsConeMor : {c1 c2 : ob C} {D : Functor J C}
                    (cc1 : Cone D c1) (cc2 : Cone D c2) (f : C [ c1 , c2 ])
                  → isProp (isConeMor cc1 cc2 f)
  isPropIsConeMor cc1 cc2 f = isPropΠ (λ _ → isSetHom C _ _)

  isLimCone : (D : Functor J C) (c0 : ob C) → Cone D c0 → Type ℓ
  isLimCone D c0 cc0 = (c : ob C) → (cc : Cone D c)
                     → ∃![ f ∈ C [ c , c0 ] ] isConeMor cc cc0 f

  isPropIsLimCone : (D : Functor J C) (c0 : ob C) (cc0 : Cone D c0)
                  → isProp (isLimCone D c0 cc0)
  isPropIsLimCone D c0 cc0 = isPropΠ2 λ _ _ → isProp∃!

  record LimCone (D : Functor J C) : Type ℓ where
    constructor climCone

    field
      lim : ob C
      limCone : Cone D lim
      univProp : isLimCone D lim limCone

    limOut : (v : ob J) → C [ lim , D .F-ob v ]
    limOut = coneOut limCone

    limOutCommutes : {u v : ob J} (e : J [ u , v ])
                   → limOut u ⋆⟨ C ⟩ D .F-hom e ≡ limOut v
    limOutCommutes = coneOutCommutes limCone

    limArrow : (c : ob C) → (cc : Cone D c) → C [ c , lim ]
    limArrow c cc = univProp c cc .fst .fst

    limArrowCommutes : (c : ob C) → (cc : Cone D c) (u : ob J)
                     → limArrow c cc ⋆⟨ C ⟩ limOut u ≡ coneOut cc u
    limArrowCommutes c cc = univProp c cc .fst .snd

    limArrowUnique : (c : ob C) → (cc : Cone D c) (k : C [ c , lim ])
                   → isConeMor cc limCone k → limArrow c cc ≡ k
    limArrowUnique c cc k hk = cong fst (univProp c cc .snd (k , hk))

  open LimCone
  limOfArrows : (D₁ D₂ : Functor J C)
                (CC₁ : LimCone D₁) (CC₂ : LimCone D₂)
                (f : (u : ob J) → C [ D₁ .F-ob u , D₂ .F-ob u ])
                (fNat : {u v : ob J} (e : J [ u , v ])
                      →  f u ⋆⟨ C ⟩ D₂ .F-hom e ≡ D₁ .F-hom e ⋆⟨ C ⟩ f v)
              → C [ CC₁ .lim , CC₂ .lim ]
  limOfArrows D₁ D₂ CC₁ CC₂ f fNat = limArrow CC₂ (CC₁ .lim) coneD₂Lim₁
   where
   coneD₂Lim₁ : Cone D₂ (CC₁ .lim)
   coneOut coneD₂Lim₁ v = limOut CC₁ v ⋆⟨ C ⟩ f v
   coneOutCommutes coneD₂Lim₁ {u = u} {v = v} e =
     limOut CC₁ u ⋆⟨ C ⟩ f u ⋆⟨ C ⟩ D₂ .F-hom e   ≡⟨ ⋆Assoc C _ _ _ ⟩
     limOut CC₁ u ⋆⟨ C ⟩ (f u ⋆⟨ C ⟩ D₂ .F-hom e) ≡⟨ cong (λ x → seq' C (limOut CC₁ u) x) (fNat e) ⟩
     limOut CC₁ u ⋆⟨ C ⟩ (D₁ .F-hom e ⋆⟨ C ⟩ f v) ≡⟨ sym (⋆Assoc C _ _ _) ⟩
     limOut CC₁ u ⋆⟨ C ⟩ D₁ .F-hom e ⋆⟨ C ⟩ f v   ≡⟨ cong (λ x → x ⋆⟨ C ⟩ f v) (limOutCommutes CC₁ e) ⟩
     limOut CC₁ v ⋆⟨ C ⟩ f v ∎

-- A category is complete if it has all limits
Limits : {ℓJ ℓJ' ℓC ℓC' : Level} → Category ℓC ℓC' → Type _
Limits {ℓJ} {ℓJ'} C = (J : Category ℓJ ℓJ') → (D : Functor J C) → LimCone D

hasLimits : {ℓJ ℓJ' ℓC ℓC' : Level} → Category ℓC ℓC' → Type _
hasLimits {ℓJ} {ℓJ'} C = (J : Category ℓJ ℓJ') → (D : Functor J C) → ∥ LimCone D ∥

-- Limits of a specific shape J in a category C
LimitsOfShape : {ℓJ ℓJ' ℓC ℓC' : Level} → Category ℓJ ℓJ' → Category ℓC ℓC' → Type _
LimitsOfShape J C = (D : Functor J C) → LimCone D

-- Preservation of limits

module _ {ℓJ ℓJ' ℓC1 ℓC1' ℓC2 ℓC2' : Level}
         {C1 : Category ℓC1 ℓC1'} {C2 : Category ℓC2 ℓC2'}
         (F : Functor C1 C2) where
  open Category
  open Functor
  open Cone

  private
    ℓ = ℓ-max (ℓ-max (ℓ-max (ℓ-max (ℓ-max ℓJ ℓJ') ℓC1) ℓC1') ℓC2) ℓC2'

  mapCone : {J : Category ℓJ ℓJ'} (D : Functor J C1) {x : ob C1} (ccx : Cone D x) → Cone (funcComp F D) (F .F-ob x)
  coneOut (mapCone D ccx) v = F .F-hom (coneOut ccx v)
  coneOutCommutes (mapCone D ccx) e =
    sym (F-seq F (coneOut ccx _) (D ⟪ e ⟫)) ∙ cong (F .F-hom) (coneOutCommutes ccx e)

  preservesLimit : {J : Category ℓJ ℓJ'} (D : Functor J C1)
                 → (L : ob C1) → Cone D L → Type ℓ
  preservesLimit D L ccL =
    isLimCone D L ccL → isLimCone (funcComp F D) (F .F-ob L) (mapCone D ccL)

  -- TODO: prove that right adjoints preserve limits


------------------------------------------------------------------------
-- Identity and composition for higher lenses
------------------------------------------------------------------------

{-# OPTIONS --cubical #-}

import Equality.Path as P

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

open P.Derived-definitions-and-properties eq

import Bi-invertibility
open import Logical-equivalence using (_⇔_)
open import Prelude as P hiding (id) renaming (_∘_ to _⊚_)

open import Bijection equality-with-J as Bij using (_↔_)
open import Category equality-with-J as C using (Category; Precategory)
import Circle eq as Circle
open import Equality.Path.Isomorphisms eq
open import Equivalence equality-with-J as Eq
  using (_≃_; Is-equivalence)
open import Function-universe equality-with-J as F hiding (id; _∘_)
open import H-level equality-with-J as H-level
open import H-level.Closure equality-with-J
open import H-level.Truncation.Propositional eq as Trunc
open import Surjection equality-with-J using (_↠_)
open import Univalence-axiom equality-with-J

open import Lens.Non-dependent.Higher eq
import Lens.Non-dependent.Traditional eq as Traditional
open import Lens.Non-dependent.Traditional.Combinators eq as TC
  using (Naive-category; Univalent)

private
  variable
    a b c d : Level
    A B C   : Type a

------------------------------------------------------------------------
-- Lens combinators

-- The definition of the identity lens is unique, if the get
-- function is required to be the identity (assuming univalence).

id-unique :
  {A : Type a} →
  Univalence a →
  (l₁ l₂ : Lens A A) →
  Lens.get l₁ ≡ P.id →
  Lens.get l₂ ≡ P.id →
  l₁ ≡ l₂
id-unique {A = A} univ l₁ l₂ get-l₁≡id get-l₂≡id =   $⟨ trans get-l₁≡id (sym get-l₂≡id) ⟩
  _≃_.to (_≃_.from f l₁′) ≡ _≃_.to (_≃_.from f l₂′)  ↝⟨ Eq.lift-equality ext ⟩
  _≃_.from f l₁′ ≡ _≃_.from f l₂′                    ↝⟨ _≃_.to $ Eq.≃-≡ (inverse f) {x = l₁′} {y = l₂′} ⟩
  l₁′ ≡ l₂′                                          ↝⟨ cong proj₁ ⟩□
  l₁ ≡ l₂                                            □
  where
  open Lens

  f : (A ≃ A) ≃ (∃ λ (l : Lens A A) → Is-equivalence (Lens.get l))
  f = ≃-≃-Σ-Lens-Is-equivalence-get univ

  is-equiv :
    (l : Lens A A) →
    get l ≡ P.id → Is-equivalence (get l)
  is-equiv _ get≡id =
    Eq.respects-extensional-equality
      (ext⁻¹ $ sym get≡id)
      (_≃_.is-equivalence Eq.id)

  l₁′ l₂′ : ∃ λ (l : Lens A A) → Is-equivalence (Lens.get l)
  l₁′ = l₁ , is-equiv l₁ get-l₁≡id
  l₂′ = l₂ , is-equiv l₂ get-l₂≡id

-- The result of composing two lenses is unique if the codomain type
-- is inhabited whenever it is merely inhabited, and we require that
-- the resulting setter function is defined in a certain way
-- (assuming univalence).

∘-unique :
  let open Lens in
  {A : Type a} {C : Type c} →
  Univalence (a ⊔ c) →
  (∥ C ∥ → C) →
  ((comp₁ , _) (comp₂ , _) :
     ∃ λ (comp : Lens B C → Lens A B → Lens A C) →
       ∀ l₁ l₂ a c →
         set (comp l₁ l₂) a c ≡ set l₂ a (set l₁ (get l₂ a) c)) →
  comp₁ ≡ comp₂
∘-unique {A = A} {C = C} univ ∥C∥→C
         (comp₁ , set₁) (comp₂ , set₂) =
  ⟨ext⟩ λ l₁ → ⟨ext⟩ λ l₂ →
    lenses-equal-if-setters-equal univ
      (comp₁ l₁ l₂) (comp₂ l₁ l₂) (λ _ → ∥C∥→C) $
      ⟨ext⟩ λ a → ⟨ext⟩ λ c →
        set (comp₁ l₁ l₂) a c           ≡⟨ set₁ _ _ _ _ ⟩
        set l₂ a (set l₁ (get l₂ a) c)  ≡⟨ sym $ set₂ _ _ _ _ ⟩∎
        set (comp₂ l₁ l₂) a c           ∎
  where
  open Lens

-- Identity lens.

id : Block "id" → Lens A A
id {A = A} ⊠ = record
  { R         = ∥ A ∥
  ; equiv     = A          ↔⟨ inverse ∥∥×↔ ⟩□
                ∥ A ∥ × A  □
  ; inhabited = P.id
  }

-- Composition of lenses.
--
-- Note that the domains are required to be at least as large as the
-- codomains; this should match many use-cases. Without this
-- restriction I failed to find a satisfactory definition of
-- composition. (I suspect that if Agda had had cumulativity, then
-- the domain and codomain could have lived in the same universe
-- without any problems.)
--
-- The composition operation matches on the lenses to ensure that it
-- does not unfold when applied to neutral lenses.
--
-- See also Lens.Non-dependent.Equivalent-preimages.⟨_⟩_⊚_.

infix 9 ⟨_,_⟩_∘_

⟨_,_⟩_∘_ :
  ∀ a b {A : Type (a ⊔ b ⊔ c)} {B : Type (b ⊔ c)} {C : Type c} →
  Lens B C → Lens A B → Lens A C
⟨_,_⟩_∘_ _ _ {A = A} {B} {C} l₁@(⟨ _ , _ , _ ⟩) l₂@(⟨ _ , _ , _ ⟩) =
  record
    { R         = R l₂ × R l₁
    ; equiv     = A                  ↝⟨ equiv l₂ ⟩
                  R l₂ × B           ↝⟨ F.id ×-cong equiv l₁ ⟩
                  R l₂ × (R l₁ × C)  ↔⟨ ×-assoc ⟩□
                  (R l₂ × R l₁) × C  □
    ; inhabited = ∥∥-map (get l₁) ⊚ inhabited l₂ ⊚ proj₁
    }
  where
  open Lens

-- The composition operation implements set in a certain way.

∘-set :
  let open Lens in
  ∀ ℓa ℓb {A : Type (ℓa ⊔ ℓb ⊔ c)} {B : Type (ℓb ⊔ c)} {C : Type c}
  (l₁ : Lens B C) (l₂ : Lens A B) a c →
  set (⟨ ℓa , ℓb ⟩ l₁ ∘ l₂) a c ≡ set l₂ a (set l₁ (get l₂ a) c)
∘-set _ _ ⟨ _ , _ , _ ⟩ ⟨ _ , _ , _ ⟩ _ _ = refl _

-- A variant of composition for lenses between types with the same
-- universe level.

infixr 9 _∘_

_∘_ :
  {A B C : Type a} →
  Lens B C → Lens A B → Lens A C
l₁ ∘ l₂ = ⟨ lzero , lzero ⟩ l₁ ∘ l₂

-- Other definitions of composition match ⟨_,_⟩_∘_, if the codomain
-- type is inhabited whenever it is merely inhabited, and the
-- resulting setter function is defined in a certain way (assuming
-- univalence).

composition≡∘ :
  let open Lens in
  ∀ a b {A : Type (a ⊔ b ⊔ c)} {B : Type (b ⊔ c)} {C : Type c} →
  Univalence (a ⊔ b ⊔ c) →
  (∥ C ∥ → C) →
  (comp : Lens B C → Lens A B → Lens A C) →
  (∀ l₁ l₂ a c →
     set (comp l₁ l₂) a c ≡ set l₂ a (set l₁ (get l₂ a) c)) →
  comp ≡ ⟨ a , b ⟩_∘_
composition≡∘ _ _ univ ∥C∥→C comp set-comp =
  ∘-unique univ ∥C∥→C (comp , set-comp)
    (_ , λ { ⟨ _ , _ , _ ⟩ ⟨ _ , _ , _ ⟩ _ _ → refl _ })

-- Identity and composition form a kind of precategory (assuming
-- univalence).

associativity :
  ∀ a b c
    {A : Type (a ⊔ b ⊔ c ⊔ d)} {B : Type (b ⊔ c ⊔ d)}
    {C : Type (c ⊔ d)} {D : Type d} →
  Univalence (a ⊔ b ⊔ c ⊔ d) →
  (l₁ : Lens C D) (l₂ : Lens B C) (l₃ : Lens A B) →
  ⟨ a ⊔ b , c ⟩ l₁ ∘ (⟨ a , b ⟩ l₂ ∘ l₃) ≡
  ⟨ a , b ⊔ c ⟩ (⟨ b , c ⟩ l₁ ∘ l₂) ∘ l₃
associativity _ _ _ univ ⟨ _ , _ , _ ⟩ ⟨ _ , _ , _ ⟩ ⟨ _ , _ , _ ⟩ =
  _↔_.from (equality-characterisation₁ ⊠ univ)
           (Eq.↔⇒≃ (inverse ×-assoc) , λ _ → refl _)

left-identity :
  ∀ bi a {A : Type (a ⊔ b)} {B : Type b} →
  Univalence (a ⊔ b) →
  (l : Lens A B) →
  ⟨ a , lzero ⟩ id bi ∘ l ≡ l
left-identity ⊠ _ {B = B} univ l@(⟨ _ , _ , _ ⟩) =
  _↔_.from (equality-characterisation₁ ⊠ univ)
    ( (R × ∥ B ∥  ↔⟨ lemma ⟩□
       R          □)
    , λ _ → refl _
    )
  where
  open Lens l

  lemma : R × ∥ B ∥ ↔ R
  lemma = record
    { surjection = record
      { logical-equivalence = record
        { to   = proj₁
        ; from = λ r → r , inhabited r
        }
      ; right-inverse-of = refl
      }
    ; left-inverse-of = λ { (r , _) →
        cong (λ x → r , x) $ truncation-is-proposition _ _ }
    }

right-identity :
  ∀ bi a {A : Type (a ⊔ b)} {B : Type b} →
  Univalence (a ⊔ b) →
  (l : Lens A B) →
  ⟨ lzero , a ⟩ l ∘ id bi ≡ l
right-identity ⊠ _ {A = A} univ l@(⟨ _ , _ , _ ⟩) =
  _↔_.from (equality-characterisation₁ ⊠ univ)
    ( (∥ A ∥ × R  ↔⟨ lemma ⟩□
       R          □)
    , λ _ → refl _
    )
  where
  open Lens l

  lemma : ∥ A ∥ × R ↔ R
  lemma = record
    { surjection = record
      { logical-equivalence = record
        { to   = proj₂
        ; from = λ r →   ∥∥-map (λ b → _≃_.from equiv (r , b))
                                (inhabited r)
                       , r
        }
      ; right-inverse-of = refl
      }
    ; left-inverse-of = λ { (_ , r) →
        cong (λ x → x , r) $ truncation-is-proposition _ _ }
    }

------------------------------------------------------------------------
-- Isomorphisms expressed using lens quasi-inverses

private
  module B {a} (b : Block "id") =
    Bi-invertibility equality-with-J (Type a) Lens (id b) _∘_
  module BM {a} (b : Block "id") (univ : Univalence a) = B.More
    b
    (left-identity b _ univ)
    (right-identity b _ univ)
    (associativity _ _ _ univ)

-- A form of isomorphism between types, expressed using lenses.

open B public
  using ()
  renaming (_≅_ to [_]_≅_;
            Has-quasi-inverse to Has-quasi-inverse)

-- Lenses with quasi-inverses can be converted to equivalences.

≅→≃ : ∀ b → [ b ] A ≅ B → A ≃ B
≅→≃
  ⊠
  (l@(⟨ _ , _ , _ ⟩) , l⁻¹@(⟨ _ , _ , _ ⟩) , l∘l⁻¹≡id , l⁻¹∘l≡id) =
  Eq.↔⇒≃ (record
    { surjection = record
      { logical-equivalence = record
        { to   = get l
        ; from = get l⁻¹
        }
      ; right-inverse-of = λ b → cong (λ l → get l b) l∘l⁻¹≡id
      }
    ; left-inverse-of = λ a → cong (λ l → get l a) l⁻¹∘l≡id
    })
  where
  open Lens

-- There is a split surjection from [ b ] A ≅ B to A ≃ B (assuming
-- univalence).

≅↠≃ :
  {A B : Type a}
  (b : Block "id") →
  Univalence a →
  ([ b ] A ≅ B) ↠ (A ≃ B)
≅↠≃ {A = A} {B = B} b univ = record
  { logical-equivalence = record
    { to   = ≅→≃ b
    ; from = from b
    }
  ; right-inverse-of = ≅→≃∘from b
  }
  where
  from : ∀ b → A ≃ B → [ b ] A ≅ B
  from b A≃B = l , l⁻¹ , l∘l⁻¹≡id b , l⁻¹∘l≡id b
    where
    l   = ≃→lens′ A≃B
    l⁻¹ = ≃→lens′ (inverse A≃B)

    l∘l⁻¹≡id : ∀ b → l ∘ l⁻¹ ≡ id b
    l∘l⁻¹≡id ⊠ = _↔_.from (equality-characterisation₁ ⊠ univ)
      ( (∥ A ∥ × ∥ B ∥  ↝⟨ Eq.⇔→≃
                             (×-closure 1 truncation-is-proposition
                                          truncation-is-proposition)
                             truncation-is-proposition
                             proj₂
                             (λ b → ∥∥-map (_≃_.from A≃B) b , b) ⟩
         ∥ B ∥          □)
      , λ _ → cong₂ _,_
               (truncation-is-proposition _ _)
               (_≃_.right-inverse-of A≃B _)
      )

    l⁻¹∘l≡id : ∀ b → l⁻¹ ∘ l ≡ id b
    l⁻¹∘l≡id ⊠ = _↔_.from (equality-characterisation₁ ⊠ univ)
      ( (∥ B ∥ × ∥ A ∥  ↝⟨ Eq.⇔→≃
                             (×-closure 1 truncation-is-proposition
                                          truncation-is-proposition)
                             truncation-is-proposition
                             proj₂
                             (λ a → ∥∥-map (_≃_.to A≃B) a , a) ⟩
         ∥ A ∥          □)
      , λ _ → cong₂ _,_
                (truncation-is-proposition _ _)
                (_≃_.left-inverse-of A≃B _)
      )

  ≅→≃∘from : ∀ b A≃B → ≅→≃ b (from b A≃B) ≡ A≃B
  ≅→≃∘from ⊠ _ = Eq.lift-equality ext (refl _)

-- There is not necessarily a split surjection from
-- Is-equivalence (Lens.get l) to Has-quasi-inverse l, if l is a
-- lens between types in the same universe (assuming univalence).

¬Is-equivalence-get↠Has-quasi-inverse :
  (b : Block "id") →
  Univalence a →
  ¬ ({A B : Type a}
     (l : Lens A B) →
     Is-equivalence (Lens.get l) ↠ Has-quasi-inverse b l)
¬Is-equivalence-get↠Has-quasi-inverse b univ surj =
                                    $⟨ ⊤-contractible ⟩
  Contractible ⊤                    ↝⟨ H-level.respects-surjection lemma₃ 0 ⟩
  Contractible ((z : Z) → z ≡ z)    ↝⟨ mono₁ 0 ⟩
  Is-proposition ((z : Z) → z ≡ z)  ↝⟨ ¬-prop ⟩□
  ⊥                                 □
  where
  open Lens

  Z,¬-prop = Circle.¬-type-of-refl-propositional
  Z        = proj₁ Z,¬-prop
  ¬-prop   = proj₂ Z,¬-prop

  lemma₂ :
    ∀ b →
    (∃ λ (eq : R (id b) ≃ R (id b)) →
       (∀ z → _≃_.to eq (remainder (id b) z) ≡ remainder (id b) z)
         ×
       ((z : Z) → get (id b) z ≡ get (id b) z)) ≃
    ((z : Z) → z ≡ z)
  lemma₂ ⊠ =
    (∃ λ (eq : ∥ Z ∥ ≃ ∥ Z ∥) →
       ((z : Z) → _≃_.to eq ∣ z ∣ ≡ ∣ z ∣)
         ×
       ((z : Z) → z ≡ z))                   ↔⟨ (∃-cong λ _ → drop-⊤-left-Σ $ _⇔_.to contractible⇔↔⊤ $
                                                Π-closure ext 0 λ _ →
                                                +⇒≡ truncation-is-proposition) ⟩

    (∥ Z ∥ ≃ ∥ Z ∥) × ((z : Z) → z ≡ z)     ↔⟨ drop-⊤-left-Σ $ _⇔_.to contractible⇔↔⊤ $
                                               propositional⇒inhabited⇒contractible
                                                 (Eq.left-closure ext 0 truncation-is-proposition)
                                                 F.id ⟩□
    ((z : Z) → z ≡ z)                       □

  lemma₃ =
    ⊤                                                               ↔⟨ inverse $ _⇔_.to contractible⇔↔⊤ $
                                                                       propositional⇒inhabited⇒contractible
                                                                         (Eq.propositional ext _)
                                                                         (_≃_.is-equivalence Eq.id) ⟩

    Is-equivalence P.id                                             ↔⟨ ≡⇒↝ equivalence $ cong Is-equivalence $
                                                                       unblock b (λ b → _ ≡ get (id b)) (refl _) ⟩

    Is-equivalence (get (id b))                                     ↝⟨ surj (id b) ⟩

    Has-quasi-inverse b (id b)                                      ↔⟨ BM.Has-quasi-inverse≃id≡id-domain b univ
                                                                         (id b , left-identity b _ univ _ , right-identity b _ univ _) ⟩

    id b ≡ id b                                                     ↔⟨ equality-characterisation₂ ⊠ univ ⟩

    (∃ λ (eq : R (id b) ≃ R (id b)) →
       (∀ z → _≃_.to eq (remainder (id b) z) ≡ remainder (id b) z)
         ×
       (∀ z → get (id b) z ≡ get (id b) z))                         ↔⟨ lemma₂ b ⟩□

    ((z : Z) → z ≡ z)                                               □

-- See also ≃≃≅ below.

------------------------------------------------------------------------
-- Equivalences expressed using bi-invertibility for lenses

-- A form of equivalence between types, expressed using lenses.

open B public
  using ()
  renaming (_≊_ to [_]_≊_;
            Has-left-inverse to Has-left-inverse;
            Has-right-inverse to Has-right-inverse;
            Is-bi-invertible to Is-bi-invertible)
open BM public
  using ()
  renaming (equality-characterisation-≊ to equality-characterisation-≊)

-- Lenses with left inverses have propositional remainder types.

has-left-inverse→remainder-propositional :
  (b : Block "id")
  (l : Lens A B) →
  Has-left-inverse b l →
  Is-proposition (Lens.R l)
has-left-inverse→remainder-propositional
  ⊠ l@(⟨ _ , _ , _ ⟩) (l⁻¹@(⟨ _ , _ , _ ⟩) , l⁻¹∘l≡id) =
                                $⟨ get≡id→remainder-propositional
                                     (l⁻¹ ∘ l)
                                     (λ a → cong (flip get a) l⁻¹∘l≡id) ⟩
  Is-proposition (R (l⁻¹ ∘ l))  ↔⟨⟩
  Is-proposition (R l × R l⁻¹)  ↝⟨ H-level-×₁ (∥∥-map (remainder l⁻¹) ⊚ inhabited l) 1 ⟩□
  Is-proposition (R l)          □
  where
  open Lens

-- Lenses with right inverses have propositional remainder types.

has-right-inverse→remainder-propositional :
  (b : Block "id")
  (l : Lens A B) →
  Has-right-inverse b l →
  Is-proposition (Lens.R l)
has-right-inverse→remainder-propositional
  ⊠ l@(⟨ _ , _ , _ ⟩) (l⁻¹@(⟨ _ , _ , _ ⟩) , l∘l⁻¹≡id) =
                                $⟨ get≡id→remainder-propositional
                                     (l ∘ l⁻¹)
                                     (λ a → cong (flip get a) l∘l⁻¹≡id) ⟩
  Is-proposition (R (l ∘ l⁻¹))  ↔⟨⟩
  Is-proposition (R l⁻¹ × R l)  ↝⟨ H-level-×₂ (∥∥-map (remainder l⁻¹) ⊚ inhabited l) 1 ⟩□
  Is-proposition (R l)          □
  where
  open Lens

-- There is an equivalence between A ≃ B and [ b ] A ≊ B (assuming
-- univalence).

≃≃≊ :
  {A B : Type a}
  (b : Block "id") →
  Univalence a →
  (A ≃ B) ≃ ([ b ] A ≊ B)
≃≃≊ {A = A} {B = B} b univ = Eq.↔⇒≃ (record
  { surjection = record
    { logical-equivalence = record
      { to   = to b
      ; from = from b
      }
    ; right-inverse-of = to∘from b
    }
  ; left-inverse-of = from∘to b
  })
  where
  open Lens

  to : ∀ b → A ≃ B → [ b ] A ≊ B
  to b = B.≅→≊ b ⊚ _↠_.from (≅↠≃ b univ)

  from : ∀ b → [ b ] A ≊ B → A ≃ B
  from b = _↠_.to (≅↠≃ b univ) ⊚ _↠_.from (BM.≅↠≊ b univ)

  to∘from : ∀ b A≊B → to b (from b A≊B) ≡ A≊B
  to∘from b A≊B =
    _≃_.from (equality-characterisation-≊ b univ _ _) $
    _↔_.from (equality-characterisation₂ b univ)
      ( ∥B∥≃R  b A≊B
      , lemma₁ b A≊B
      , lemma₂ b A≊B
      )
    where
    l′ : ∀ b (A≊B : [ b ] A ≊ B) → Lens A B
    l′ b A≊B = proj₁ (to b (from b A≊B))

    ∥B∥≃R : ∀ b (A≊B@(l , _) : [ b ] A ≊ B) → ∥ B ∥ ≃ R l
    ∥B∥≃R b (l , (l-inv@(l⁻¹ , _) , _)) = Eq.⇔→≃
      truncation-is-proposition
      R-prop
      (Trunc.rec R-prop (remainder l ⊚ get l⁻¹))
      (inhabited l)
      where
      R-prop = has-left-inverse→remainder-propositional b l l-inv

    lemma₁ :
      ∀ b (A≊B@(l , _) : [ b ] A ≊ B) a →
      _≃_.to (∥B∥≃R b A≊B) (remainder (l′ b A≊B) a) ≡ remainder l a
    lemma₁
      ⊠
      ( l@(⟨ _ , _ , _ ⟩)
      , (l⁻¹@(⟨ _ , _ , _ ⟩) , l⁻¹∘l≡id)
      , (⟨ _ , _ , _ ⟩ , _)
      ) a =
      remainder l (get l⁻¹ (get l a))  ≡⟨⟩
      remainder l (get (l⁻¹ ∘ l) a)    ≡⟨ cong (λ l′ → remainder l (get l′ a)) l⁻¹∘l≡id ⟩
      remainder l (get (id ⊠) a)       ≡⟨⟩
      remainder l a                    ∎

    lemma₂ :
      ∀ b (A≊B@(l , _) : [ b ] A ≊ B) a →
      get (l′ b A≊B) a ≡ get l a
    lemma₂ ⊠
      (⟨ _ , _ , _ ⟩ , (⟨ _ , _ , _ ⟩ , _) , (⟨ _ , _ , _ ⟩ , _)) _ =
      refl _

  from∘to :
    ∀ b A≃B →
    _↠_.to (≅↠≃ b univ) (_↠_.from (BM.≅↠≊ b univ)
      (B.≅→≊ b (_↠_.from (≅↠≃ b univ) A≃B))) ≡
    A≃B
  from∘to ⊠ _ = Eq.lift-equality ext (refl _)

-- The right-to-left direction of ≃≃≊ maps bi-invertible lenses to
-- their getter functions.

to-from-≃≃≊≡get :
  (b : Block "id")
  (univ : Univalence a)
  (A≊B@(l , _) : [ b ] A ≊ B) →
  _≃_.to (_≃_.from (≃≃≊ b univ) A≊B) ≡ Lens.get l
to-from-≃≃≊≡get
  ⊠ _ (⟨ _ , _ , _ ⟩ , (⟨ _ , _ , _ ⟩ , _) , (⟨ _ , _ , _ ⟩ , _)) =
  refl _

-- A variant of ≃≃≊ that works even if A and B live in different
-- universes.
--
-- This variant came up in a discussion with Andrea Vezzosi.

≃≃≊′ :
  {A : Type a} {B : Type b}
  (b-id : Block "id") →
  Univalence (a ⊔ b) →
  (A ≃ B) ≃ ([ b-id ] ↑ b A ≊ ↑ a B)
≃≃≊′ {a = a} {b = b} {A = A} {B = B} b-id univ =
  A ≃ B                   ↔⟨ inverse $ Eq.≃-preserves-bijections ext Bij.↑↔ Bij.↑↔ ⟩
  ↑ b A ≃ ↑ a B           ↝⟨ ≃≃≊ b-id univ ⟩□
  [ b-id ] ↑ b A ≊ ↑ a B  □

-- The right-to-left direction of ≃≃≊′ maps bi-invertible lenses to a
-- variant of their getter functions.

to-from-≃≃≊′≡get :
  {A : Type a} {B : Type b}
  (b-id : Block "id")
  (univ : Univalence (a ⊔ b)) →
  (A≊B@(l , _) : [ b-id ] ↑ b A ≊ ↑ a B) →
  _≃_.to (_≃_.from (≃≃≊′ b-id univ) A≊B) ≡ lower ⊚ Lens.get l ⊚ lift
to-from-≃≃≊′≡get
  ⊠ _ (⟨ _ , _ , _ ⟩ , (⟨ _ , _ , _ ⟩ , _) , (⟨ _ , _ , _ ⟩ , _)) =
  refl _

-- The getter function of a bi-invertible lens is an equivalence
-- (assuming univalence).

Is-bi-invertible→Is-equivalence-get :
  {A : Type a}
  (b : Block "id") →
  Univalence a →
  (l : Lens A B) →
  Is-bi-invertible b l → Is-equivalence (Lens.get l)
Is-bi-invertible→Is-equivalence-get
  b@⊠ univ l@(⟨ _ , _ , _ ⟩)
  is-bi-inv@((⟨ _ , _ , _ ⟩ , _) , (⟨ _ , _ , _ ⟩ , _)) =
  _≃_.is-equivalence (_≃_.from (≃≃≊ b univ) (l , is-bi-inv))

-- If l is a lens between types in the same universe, then there is an
-- equivalence between "l is bi-invertible" and "the getter of l is an
-- equivalence" (assuming univalence).

Is-bi-invertible≃Is-equivalence-get :
  {A B : Type a}
  (b : Block "id") →
  Univalence a →
  (l : Lens A B) →
  Is-bi-invertible b l ≃ Is-equivalence (Lens.get l)
Is-bi-invertible≃Is-equivalence-get b univ l = Eq.⇔→≃
  (BM.Is-bi-invertible-propositional b univ l)
  (Eq.propositional ext _)
  (Is-bi-invertible→Is-equivalence-get b univ l)
  (λ is-equiv →

     let l′ = ≃→lens′ Eq.⟨ get l , is-equiv ⟩ in

                            $⟨ proj₂ (_≃_.to (≃≃≊ b univ) Eq.⟨ _ , is-equiv ⟩) ⟩
     Is-bi-invertible b l′  ↝⟨ subst (Is-bi-invertible b) (sym $ get-equivalence→≡≃→lens′ univ l is-equiv) ⟩□
     Is-bi-invertible b l   □)
  where
  open Lens

-- If A is a set, then there is an equivalence between [ b ] A ≊ B and
-- [ b ] A ≅ B (assuming univalence).

≊≃≅ :
  {A B : Type a}
  (b : Block "id") →
  Univalence a →
  Is-set A →
  ([ b ] A ≊ B) ≃ ([ b ] A ≅ B)
≊≃≅ b univ A-set =
  ∃-cong λ _ →
    BM.Is-bi-invertible≃Has-quasi-inverse-domain
      b univ
      (Is-set-closed-domain univ A-set)

------------------------------------------------------------------------
-- A category

-- If A is a set, then there is an equivalence between A ≃ B and
-- [ b ] A ≅ B (assuming univalence).

≃≃≅ :
  {A B : Type a}
  (b : Block "≃≃≅") →
  Univalence a →
  Is-set A →
  (A ≃ B) ≃ ([ b ] A ≅ B)
≃≃≅ {A = A} {B = B} b@⊠ univ A-set =
  A ≃ B        ↝⟨ ≃≃≊ b univ ⟩
  [ b ] A ≊ B  ↝⟨ ≊≃≅ b univ A-set ⟩□
  [ b ] A ≅ B  □

-- The equivalence ≃≃≅ maps identity to identity.

≃≃≅-id≡id :
  {A : Type a}
  (b : Block "≃≃≅") (univ : Univalence a) (A-set : Is-set A) →
  proj₁ (_≃_.to (≃≃≅ b univ A-set) F.id) ≡ id b
≃≃≅-id≡id ⊠ univ _ =
  _↔_.from (equality-characterisation₁ ⊠ univ)
    (F.id , λ _ → refl _)

-- Lenses between sets in the same universe form a precategory
-- (assuming univalence).

private

  precategory′ :
    Block "id" →
    Univalence a →
    C.Precategory′ (lsuc a) (lsuc a)
  precategory′ {a = a} b univ =
      Set a
    , (λ (A , A-set) (B , _) →
           Lens A B
         , Is-set-closed-domain univ A-set)
    , id b
    , _∘_
    , left-identity b lzero univ _
    , right-identity b lzero univ _
    , (λ {_ _ _ _ l₁ l₂ l₃} →
         associativity lzero lzero lzero univ l₃ l₂ l₁)

precategory :
  Block "precategory" →
  Univalence a →
  Precategory (lsuc a) (lsuc a)
precategory ⊠ univ .C.Precategory.precategory =
  block λ b → precategory′ b univ

-- Lenses between sets in the same universe form a category
-- (assuming univalence).

category :
  Block "category" →
  Univalence a →
  Category (lsuc a) (lsuc a)
category ⊠ univ =
  block λ b →
  C.precategory-with-Set-to-category
    ext
    (λ _ _ → univ)
    (proj₂ $ precategory′ b univ)
    (λ (_ , A-set) _ → ≃≃≅ b univ A-set)
    (λ (_ , A-set) → ≃≃≅-id≡id b univ A-set)

-- The precategory defined here is equal to the one defined in
-- Traditional, if the latter one is lifted (assuming univalence).

precategory≡precategory :
  (b : Block "precategory") →
  Univalence (lsuc a) →
  (univ : Univalence a) →
  precategory b univ ≡
  C.lift-precategory-Hom _ TC.precategory
precategory≡precategory ⊠ univ⁺ univ =
  block λ b →
  _↔_.to (C.equality-characterisation-Precategory ext univ⁺ univ⁺)
    ( F.id
    , (λ (X , X-set) (Y , _) →
         Lens X Y                    ↔⟨ Lens↔Traditional-lens b univ X-set ⟩
         Traditional.Lens X Y        ↔⟨ inverse Bij.↑↔ ⟩□
         ↑ _ (Traditional.Lens X Y)  □)
    , (λ (_ , X-set) → cong lift $ _↔_.from
         (Traditional.equality-characterisation-for-sets X-set)
         (refl _))
    , (λ (_ , X-set) (_ , Y-set) _ (lift l₁) (lift l₂) →
         cong lift (∘-lemma b X-set Y-set l₁ l₂))
    )
  where
  ∘-lemma :
    ∀ b (A-set : Is-set A) (B-set : Is-set B)
    (l₁ : Traditional.Lens B C) (l₂ : Traditional.Lens A B) →
    Lens.traditional-lens
      (_↠_.from (Lens↠Traditional-lens b B-set) l₁ ∘
       _↠_.from (Lens↠Traditional-lens b A-set) l₂) ≡
    l₁ TC.∘ l₂
  ∘-lemma ⊠ A-set _ _ _ =
    _↔_.from (Traditional.equality-characterisation-for-sets A-set)
      (refl _)

-- The category defined here is equal to the one defined in
-- Traditional, if the latter one is lifted (assuming univalence).

category≡category :
  (b : Block "category") →
  Univalence (lsuc a) →
  (univ : Univalence a) →
  category b univ ≡
  C.lift-category-Hom _ (TC.category univ)
category≡category b univ⁺ univ =
  _↔_.from (C.≡↔precategory≡precategory ext)
    (Category.precategory (category b univ)        ≡⟨ lemma b ⟩

     precategory b univ                            ≡⟨ precategory≡precategory b univ⁺ univ ⟩∎

     Category.precategory
       (C.lift-category-Hom _ (TC.category univ))  ∎)
  where
  lemma :
    ∀ b →
    Category.precategory (category b univ) ≡
    precategory b univ
  lemma ⊠ = refl _

-- Types in a fixed universe and higher lenses between them form a
-- naive category (assuming univalence).

naive-category :
  Block "id" →
  Univalence a →
  Naive-category (lsuc a) (lsuc a)
naive-category {a = a} b univ =
    Type a
  , Lens
  , id b
  , _∘_
  , left-identity b lzero univ
  , right-identity b lzero univ
  , associativity lzero lzero lzero univ

-- This category is univalent.
--
-- An anonymous reviewer asked if something like this could be proved,
-- given ≃≃≊. The proof of this result is due to Andrea Vezzosi.

univalent :
  (b : Block "id")
  (univ : Univalence a) →
  Univalent (naive-category b univ)
univalent b univ =
  BM.≡≃≊→Univalence-≊ b univ λ {A B} →
    A ≡ B        ↝⟨ ≡≃≃ univ ⟩
    A ≃ B        ↝⟨ ≃≃≊ b univ ⟩□
    [ b ] A ≊ B  □


-- Andreas, 2011-10-04, transcription of Dan Doel's post on the Agda list
{-# OPTIONS --experimental-irrelevance #-}
module IrrelevantMatchRefl where

postulate
  Level : Set
  lzero : Level
  lsuc  : (i : Level) → Level
  _⊔_   : Level -> Level -> Level

{-# BUILTIN LEVEL     Level #-}
{-# BUILTIN LEVELZERO lzero  #-}
{-# BUILTIN LEVELSUC  lsuc   #-}
{-# BUILTIN LEVELMAX  _⊔_ #-}

infixl 6 _⊔_

data _≡_ {i : Level}{A : Set i}(a : A) : A → Set where
  refl : a ≡ a

sym : ∀ {i}{A B : Set i} → A ≡ B → B ≡ A
sym refl = refl

-- irrelevant subst should be rejected, because it suggests
-- that the equality proof is irrelevant also for reduction
subst : ∀ {i j}{A : Set i}(P : A → Set j){a b : A} → .(a ≡ b) → P a → P b
subst P refl x = x

postulate
  D   : Set
  lie : (D → D) ≡ D

-- the following two substs may not reduce! ...
abs : (D → D) → D
abs f = subst (λ T → T) lie f

app : D → D → D
app d = subst (λ T → T) (sym lie) d

ω : D
ω = abs (λ d → app d d)

-- ... otherwise Ω loops
Ω : D
Ω = app ω ω

-- ... and this would be a real fixed-point combinator
Y : (D → D) → D
Y f = app δ δ
  where δ = abs (λ x → f (app x x))

K : D → D
K x = abs (λ y → x)

K∞ : D
K∞ = Y K

mayloop : K∞ ≡ abs (λ y → K∞)
mayloop = refl
-- gives error D != D → D


-- By Philipp Hausmann.

open import Agda.Builtin.Bool
open import Agda.Builtin.Equality
open import Agda.Builtin.Float

data ⊥ : Set where

_/_ = primFloatDiv
_<_ = primFloatNumericalLess

cong : {A B : Set} {x y : A} (f : A → B) → x ≡ y → f x ≡ f y
cong f refl = refl

0eq : 0.0 ≡ -0.0
0eq = refl

bug : ⊥
bug = f (cong (λ x → (1.0 / x) < 0.0) 0eq)
  where
    f : false ≡ true → ⊥
    f ()


------------------------------------------------------------------------
-- Strong bisimilarity for partially defined values, along with a
-- proof showing that this relation is pointwise isomorphic to path
-- equality
------------------------------------------------------------------------

{-# OPTIONS --cubical --sized-types #-}

module Delay-monad.Bisimilarity.For-all-sizes where

open import Equality.Path hiding (ext)
open import Prelude
open import Prelude.Size

open import Bijection equality-with-J using (_↔_)

open import Delay-monad

private
  variable
    a : Level
    A : Type a
    x : A
    i : Size

mutual

  -- A variant of strong bisimilarity that relates values of any size,
  -- not only those of size ∞.
  --
  -- Note: I have not managed to prove that this relation is pointwise
  -- isomorphic to strong bisimilarity as defined in
  -- Delay-monad.Bisimilarity (when the relation is restricted to
  -- fully defined values).

  infix 4 [_]_∼ˢ_ [_]_∼ˢ′_

  data [_]_∼ˢ_ {A : Type a} (i : Size) :
               Delay A i → Delay A i → Type a where
    now   : [ i ] now x ∼ˢ now x
    later : {x y : Delay′ A i} →
            [ i ] x ∼ˢ′ y → [ i ] later x ∼ˢ later y

  record [_]_∼ˢ′_ {A : Type a}
                  (i : Size) (x y : Delay′ A i) : Type a where
    coinductive
    field
      force : {j : Size< i} → [ j ] x .force ∼ˢ y .force

open [_]_∼ˢ′_ public

mutual

  -- Strong bisimilarity is reflexive. Note that the proof is
  -- size-preserving.

  reflexiveˢ : (x : Delay A i) → [ i ] x ∼ˢ x
  reflexiveˢ (now x)   = now
  reflexiveˢ (later x) = later (reflexiveˢ′ x)

  reflexiveˢ′ : (x : Delay′ A i) → [ i ] x ∼ˢ′ x
  reflexiveˢ′ x .force = reflexiveˢ (x .force)

mutual

  -- Extensionality: Strong bisimilarity implies equality.

  ext : {x y : Delay A i} →
        [ i ] x ∼ˢ y → x ≡ y
  ext now       = refl
  ext (later p) = cong later (ext′ p)

  ext′ : {x y : Delay′ A i} →
         [ i ] x ∼ˢ′ y → x ≡ y
  ext′ p i .force = ext (p .force) i

mutual

  -- The extensionality proof maps reflexivity to reflexivity.

  ext-reflexiveˢ :
    (x : Delay A i) →
    ext (reflexiveˢ x) ≡ refl {x = x}
  ext-reflexiveˢ         (now x)   = refl
  ext-reflexiveˢ {i = i} (later x) =
    ext (reflexiveˢ (later x))         ≡⟨⟩
    cong later (ext′ (reflexiveˢ′ x))  ≡⟨ cong (cong later) (ext′-reflexiveˢ′ x) ⟩
    cong later refl                    ≡⟨⟩
    refl                               ∎

  ext′-reflexiveˢ′ :
    (x : Delay′ A i) →
    ext′ (reflexiveˢ′ x) ≡ refl {x = x}
  ext′-reflexiveˢ′ x i j .force = ext-reflexiveˢ (x .force) i j

-- Equality implies strong bisimilarity.

≡⇒∼ : {x y : Delay A i} → x ≡ y → [ i ] x ∼ˢ y
≡⇒∼ {x = x} eq = subst ([ _ ] x ∼ˢ_) eq (reflexiveˢ x)

≡⇒∼′ : {x y : Delay′ A i} → x ≡ y → [ i ] x ∼ˢ′ y
≡⇒∼′ eq .force = ≡⇒∼ (cong (λ x → x .force) eq)

private

  ≡⇒∼″ : {x y : Delay′ A i} → x ≡ y → [ i ] x ∼ˢ′ y
  ≡⇒∼″ {x = x} eq = subst ([ _ ] x ∼ˢ′_) eq (reflexiveˢ′ x)

-- The three lemmas above map reflexivity to reflexivity.

≡⇒∼-refl :
  {x : Delay A i} →
  ≡⇒∼ (refl {x = x}) ≡ reflexiveˢ x
≡⇒∼-refl {x = x} =
  ≡⇒∼ refl                                 ≡⟨⟩
  subst ([ _ ] x ∼ˢ_) refl (reflexiveˢ x)  ≡⟨ subst-refl ([ _ ] x ∼ˢ_) (reflexiveˢ x) ⟩∎
  reflexiveˢ x                             ∎

≡⇒∼′-refl :
  {x : Delay′ A i} →
  ≡⇒∼′ (refl {x = x}) ≡ reflexiveˢ′ x
≡⇒∼′-refl i .force = ≡⇒∼-refl i

private

  ≡⇒∼″-refl :
    {x : Delay′ A i} →
    ≡⇒∼″ (refl {x = x}) ≡ reflexiveˢ′ x
  ≡⇒∼″-refl {x = x} =
    ≡⇒∼″ refl                                  ≡⟨⟩
    subst ([ _ ] x ∼ˢ′_) refl (reflexiveˢ′ x)  ≡⟨ subst-refl ([ _ ] x ∼ˢ′_) (reflexiveˢ′ x) ⟩∎
    reflexiveˢ′ x                              ∎

  -- ≡⇒∼′ and ≡⇒″ are pointwise equal.

  ≡⇒∼′≡≡⇒∼″ :
    {x y : Delay′ A i} {eq : x ≡ y} →
    ≡⇒∼′ eq ≡ ≡⇒∼″ eq
  ≡⇒∼′≡≡⇒∼″ = elim
    (λ eq → ≡⇒∼′ eq ≡ ≡⇒∼″ eq)
    (λ x →
       ≡⇒∼′ refl      ≡⟨ ≡⇒∼′-refl ⟩
       reflexiveˢ′ x  ≡⟨ sym ≡⇒∼″-refl ⟩∎
       ≡⇒∼″ refl      ∎)
    _

private

  -- Extensionality and ≡⇒∼/≡⇒∼′ are inverses.

  ext∘≡⇒∼ :
    {x y : Delay A i}
    (eq : x ≡ y) → ext (≡⇒∼ eq) ≡ eq
  ext∘≡⇒∼ = elim
    (λ eq → ext (≡⇒∼ eq) ≡ eq)
    (λ x → ext (≡⇒∼ refl)      ≡⟨ cong ext ≡⇒∼-refl ⟩
           ext (reflexiveˢ x)  ≡⟨ ext-reflexiveˢ x ⟩∎
           refl                ∎)

  ext′∘≡⇒∼′ :
    {x y : Delay′ A i}
    (eq : x ≡ y) → ext′ (≡⇒∼′ eq) ≡ eq
  ext′∘≡⇒∼′ = elim
    (λ eq → ext′ (≡⇒∼′ eq) ≡ eq)
    (λ x → ext′ (≡⇒∼′ refl)      ≡⟨ cong ext′ ≡⇒∼′-refl ⟩
           ext′ (reflexiveˢ′ x)  ≡⟨ ext′-reflexiveˢ′ x ⟩∎
           refl                  ∎)

  mutual

    ≡⇒∼∘ext :
      {x y : Delay A i}
      (eq : [ i ] x ∼ˢ y) →
      ≡⇒∼ (ext eq) ≡ eq
    ≡⇒∼∘ext now =
      ≡⇒∼ (ext now)  ≡⟨⟩
      ≡⇒∼ refl       ≡⟨ ≡⇒∼-refl ⟩∎
      now            ∎
    ≡⇒∼∘ext (later {x = x} {y = y} p) =
      ≡⇒∼ (ext (later p))                                    ≡⟨⟩

      ≡⇒∼ (cong later (ext′ p))                              ≡⟨⟩

      subst ([ _ ] later x ∼ˢ_) (cong later (ext′ p))
        (later (reflexiveˢ′ x))                              ≡⟨ sym $ subst-∘ ([ _ ] later x ∼ˢ_) later _ {p = later (reflexiveˢ′ x)} ⟩

      subst (λ y → [ _ ] later x ∼ˢ later y) (ext′ p)
        (later (reflexiveˢ′ x))                              ≡⟨ elim¹
                                                                  (λ eq → subst (λ y → [ _ ] later x ∼ˢ later y) eq (later (reflexiveˢ′ x)) ≡
                                                                          later (subst ([ _ ] x ∼ˢ′_) eq (reflexiveˢ′ x))) (
          subst (λ y → [ _ ] later x ∼ˢ later y) refl
            (later (reflexiveˢ′ x))                                 ≡⟨ subst-refl (λ y → [ _ ] later x ∼ˢ later y) _ ⟩

          later (reflexiveˢ′ x)                                     ≡⟨ cong later $ sym $ subst-refl ([ _ ] x ∼ˢ′_) _ ⟩∎

          later (subst ([ _ ] x ∼ˢ′_) refl (reflexiveˢ′ x))         ∎)
                                                                  (ext′ p) ⟩
      later (subst ([ _ ] x ∼ˢ′_) (ext′ p) (reflexiveˢ′ x))  ≡⟨⟩

      later (≡⇒∼″ (ext′ p))                                  ≡⟨ cong later $ sym ≡⇒∼′≡≡⇒∼″ ⟩

      later (≡⇒∼′ (ext′ p))                                  ≡⟨ cong later (≡⇒∼′∘ext′ p) ⟩∎

      later p                                                ∎

    ≡⇒∼′∘ext′ :
      {x y : Delay′ A i}
      (eq : [ i ] x ∼ˢ′ y) →
      ≡⇒∼′ (ext′ eq) ≡ eq
    ≡⇒∼′∘ext′ eq i .force = ≡⇒∼∘ext (eq .force) i

-- Strong bisimilarity and equality are pointwise isomorphic.

∼↔≡ : {x y : Delay A i} → [ i ] x ∼ˢ y ↔ x ≡ y
∼↔≡ = record
  { surjection = record
    { logical-equivalence = record
      { to   = ext
      ; from = ≡⇒∼
      }
    ; right-inverse-of = ext∘≡⇒∼
    }
  ; left-inverse-of = ≡⇒∼∘ext
  }

∼′↔≡ : {x y : Delay′ A i} → [ i ] x ∼ˢ′ y ↔ x ≡ y
∼′↔≡ = record
  { surjection = record
    { logical-equivalence = record
      { to   = ext′
      ; from = ≡⇒∼′
      }
    ; right-inverse-of = ext′∘≡⇒∼′
    }
  ; left-inverse-of = ≡⇒∼′∘ext′
  }


module _ where

open import Agda.Builtin.Nat

module M (n : Nat) where

  postulate A : Set

module M₁ (m : Nat) where

  open module Mm = M m

  -- bar : M.A (m + 1)
  -- bar = m  -- Nat !=< M.A (m + 1)

postulate
  n : Nat

foo : M.A n
foo = n -- Nat !=< M.A n


{-# OPTIONS --cubical --safe --postfix-projections #-}

module Data.Array where

open import Data.Binary
open import Prelude

private
  variable
    ns : 𝔹

record 2× {a} (A : Type a) : Type a where
  eta-equality
  constructor _⊛_
  field
    pr₁ pr₂ : A
open 2× public

infixr 5 _∷₁_ _∷₂_ _∷_

mutual
  record Array0ᵇ {a} : Type a where
    eta-equality
    constructor []

  record Array1ᵇ {a} (A : Type a) (ns : 𝔹) : Type a where
    eta-equality
    inductive
    constructor _∷₁_
    field
      head1ᵇ : A
      tail1ᵇ : Array (2× A) ns

  record Array2ᵇ {a} (A : Type a) (ns : 𝔹) : Type a where
    eta-equality
    inductive
    constructor _∷₂_
    field
      head2ᵇ : 2× A
      tail2ᵇ : Array (2× A) ns

  Array : Type a → 𝔹 → Type a
  Array A 0ᵇ = Array0ᵇ
  Array A (1ᵇ ns) = Array1ᵇ A ns
  Array A (2ᵇ ns) = Array2ᵇ A ns

_∷_ : A → Array A ns → Array A (inc ns)
_∷_ {ns = 0ᵇ} x xs = x ∷₁ xs
_∷_ {ns = 1ᵇ ns} x₁ (x₂ ∷₁ xs) = (x₁ ⊛ x₂) ∷₂ xs
_∷_ {ns = 2ᵇ ns} x₁ (x₂ ∷₂ xs) = x₁ ∷₁ x₂ ∷ xs

open import Lens

⦅head1ᵇ⦆ : Lens (Array A (1ᵇ ns)) A
⦅head1ᵇ⦆ .fst (x ∷₁ xs) = lens-part x (_∷₁ xs)
⦅head1ᵇ⦆ .snd .get-set xs v i = v
⦅head1ᵇ⦆ .snd .set-get xs i = xs
⦅head1ᵇ⦆ .snd .set-set (x ∷₁ xs) v₁ v₂ i = v₂ ∷₁ xs

⦅head2ᵇ⦆ : Lens (Array A (2ᵇ ns)) (2× A)
⦅head2ᵇ⦆ .fst (x ∷₂ xs) = lens-part x (_∷₂ xs)
⦅head2ᵇ⦆ .snd .get-set (x ∷₂ xs) v i = v
⦅head2ᵇ⦆ .snd .set-get (x ∷₂ xs) i = x ∷₂ xs
⦅head2ᵇ⦆ .snd .set-set (x ∷₂ xs) v₁ v₂ i = v₂ ∷₂ xs

⦅tail1ᵇ⦆ : Lens (Array A (1ᵇ ns)) (Array (2× A) ns)
⦅tail1ᵇ⦆ .fst (x ∷₁ xs) = lens-part xs (x ∷₁_)
⦅tail1ᵇ⦆ .snd .get-set (x ∷₁ xs) v i = v
⦅tail1ᵇ⦆ .snd .set-get (x ∷₁ xs) i = x ∷₁ xs
⦅tail1ᵇ⦆ .snd .set-set (x ∷₁ xs) v₁ v₂ i = x ∷₁ v₂

⦅tail2ᵇ⦆ : Lens (Array A (2ᵇ ns)) (Array (2× A) ns)
⦅tail2ᵇ⦆ .fst (x ∷₂ xs) = lens-part xs (x ∷₂_)
⦅tail2ᵇ⦆ .snd .get-set (x ∷₂ xs) v i = v
⦅tail2ᵇ⦆ .snd .set-get (x ∷₂ xs) i = x ∷₂ xs
⦅tail2ᵇ⦆ .snd .set-set (x ∷₂ xs) v₁ v₂ i = x ∷₂ v₂

⦅pr₁⦆ : Lens (2× A) A
⦅pr₁⦆ .fst (x ⊛ y) = lens-part x (_⊛ y)
⦅pr₁⦆ .snd .get-set s v i = v
⦅pr₁⦆ .snd .set-get s i = s
⦅pr₁⦆ .snd .set-set s v₁ v₂ i = v₂ ⊛ s .pr₂

⦅pr₂⦆ : Lens (2× A) A
⦅pr₂⦆ .fst (x ⊛ y) = lens-part y (x ⊛_)
⦅pr₂⦆ .snd .get-set s v i = v
⦅pr₂⦆ .snd .set-get s i = s
⦅pr₂⦆ .snd .set-set s v₁ v₂ i = s .pr₁ ⊛ v₂

open import Data.Binary.Order

mutual
  index : ∀ is {js} → is < js → Lens (Array A js) A
  index 0ᵇ      {1ᵇ js} p = ⦅head1ᵇ⦆
  index 0ᵇ      {2ᵇ js} p = ⦅head2ᵇ⦆ ⋯ ⦅pr₁⦆
  index (1ᵇ is) {js}    p = ind₂1ᵇ is p
  index (2ᵇ is) {1ᵇ js} p = ⦅tail1ᵇ⦆ ⋯ index is p ⋯ ⦅pr₂⦆
  index (2ᵇ is) {2ᵇ js} p = ⦅tail2ᵇ⦆ ⋯ index is p ⋯ ⦅pr₁⦆

  ind₂1ᵇ : ∀ is {js} → 1ᵇ is < js → Lens (Array A js) A
  ind₂1ᵇ is      {1ᵇ js} p = ⦅tail1ᵇ⦆ ⋯ index is p ⋯ ⦅pr₁⦆
  ind₂1ᵇ 0ᵇ      {2ᵇ js} p = ⦅head2ᵇ⦆ ⋯ ⦅pr₂⦆
  ind₂1ᵇ (1ᵇ is) {2ᵇ js} p = ⦅tail2ᵇ⦆ ⋯ ind₃ is p ⋯ ⦅pr₂⦆
  ind₂1ᵇ (2ᵇ is) {2ᵇ js} p = ⦅tail2ᵇ⦆ ⋯ ind₂2ᵇ is p ⋯ ⦅pr₂⦆

  ind₂2ᵇ : ∀ is {js} → 2ᵇ is ≤ js → Lens (Array A js) A
  ind₂2ᵇ is      {1ᵇ js} p = ⦅tail1ᵇ⦆ ⋯ index is p ⋯ ⦅pr₁⦆
  ind₂2ᵇ 0ᵇ      {2ᵇ js} p = ⦅head2ᵇ⦆ ⋯ ⦅pr₂⦆
  ind₂2ᵇ (1ᵇ is) {2ᵇ js} p = ⦅tail2ᵇ⦆ ⋯ ind₃ is p ⋯ ⦅pr₂⦆
  ind₂2ᵇ (2ᵇ is) {2ᵇ js} p = ⦅tail2ᵇ⦆ ⋯ ind₂2ᵇ is p ⋯ ⦅pr₂⦆

  ind₃ : ∀ is {js} → 1ᵇ is ≤ js → Lens (Array A js) A
  ind₃ 0ᵇ      {1ᵇ js} p = ⦅head1ᵇ⦆
  ind₃ 0ᵇ      {2ᵇ js} p = ⦅head2ᵇ⦆ ⋯ ⦅pr₁⦆
  ind₃ (1ᵇ is) {1ᵇ js} p = ⦅tail1ᵇ⦆ ⋯ ind₃ is p ⋯ ⦅pr₂⦆
  ind₃ (1ᵇ is) {2ᵇ js} p = ⦅tail2ᵇ⦆ ⋯ ind₃ is p ⋯ ⦅pr₁⦆
  ind₃ (2ᵇ is) {1ᵇ js} p = ⦅tail1ᵇ⦆ ⋯ ind₂2ᵇ is p ⋯ ⦅pr₂⦆
  ind₃ (2ᵇ is) {2ᵇ js} p = ⦅tail2ᵇ⦆ ⋯ ind₂2ᵇ is p ⋯ ⦅pr₁⦆

at : ∀ is {js} → {p : is < js} → Lens (Array A js) A
at is {p = p} = index is p

import Data.Nat as ℕ

foldrP : ∀ {p} (P : ℕ → Type p) → (∀ {n} → A → P n → P (suc n)) → P zero → Array A ns → P ⟦ ns ⇓⟧
foldrP {ns = 0ᵇ} P f b [] = b
foldrP {ns = 1ᵇ ns} P f b (x ∷₁ xs) = f x (foldrP (λ n → P (n ℕ.* 2)) (λ { (x₁ ⊛ x₂) b → f x₁ (f x₂ b) }) b xs)
foldrP {ns = 2ᵇ ns} P f b ((x₁ ⊛ x₂) ∷₂ xs) = f x₁ (f x₂ (foldrP (λ n → P (n ℕ.* 2)) (λ { (x₁ ⊛ x₂) b → f x₁ (f x₂ b)}) b xs))

foldr : (A → B → B) → B → Array A ns → B
foldr f b = foldrP (λ _ → _) f b

map : (A → B) → Array A ns → Array B ns
map {ns = 0ᵇ}    f xs = []
map {ns = 1ᵇ ns} f (x ∷₁ xs) = f x ∷₁ map (λ { (x₁ ⊛ x₂) → f x₁ ⊛ f x₂ }) xs
map {ns = 2ᵇ ns} f ((x₁ ⊛ x₂) ∷₂ xs) = (f x₁ ⊛ f x₂) ∷₂ map (λ { (x₁ ⊛ x₂) → f x₁ ⊛ f x₂ }) xs

import Data.List as List
open import Data.List using (List)

toList : Array A ns → List.List A
toList = foldr List._∷_ List.[]

fromList : (xs : List A) → Array A ⟦ List.length xs ⇑⟧
fromList List.[] = []
fromList (x List.∷ xs) = x ∷ fromList xs

-- Lens laws are satisfied definitionally:

-- get-set
_ : (xs : Array ℕ ⟦ 10 ⇑⟧) (n : ℕ) →  (xs [ at ⟦ 5 ⇑⟧ ]≔ n [ at ⟦ 5 ⇑⟧ ]) ≡ n
_ = λ xs n → refl

-- set-get
_ : (xs : Array ℕ ⟦ 10 ⇑⟧) → (xs [ at ⟦ 5 ⇑⟧ ]≔ (xs [ at ⟦ 5 ⇑⟧ ])) ≡ xs
_ = λ xs → refl

-- set-set
_ : (xs : Array ℕ ⟦ 10 ⇑⟧) (n m : ℕ) → (xs [ at ⟦ 5 ⇑⟧ ]≔ n [ at ⟦ 5 ⇑⟧ ]≔ m) ≡ (xs [ at ⟦ 5 ⇑⟧ ]≔ m)
_ = λ xs n m → refl


------------------------------------------------------------------------
-- A type-checker
------------------------------------------------------------------------

import Axiom.Extensionality.Propositional as E
import Level
open import Data.Universe

-- The code makes use of the assumption that propositional equality of
-- functions is extensional.

module README.DependentlyTyped.Type-checker
  (Uni₀ : Universe Level.zero Level.zero)
  (ext : E.Extensionality Level.zero Level.zero)
  where

open import Category.Monad
open import Data.Maybe hiding (_>>=_)
import Data.Maybe.Categorical as Maybe
open import Data.Nat using (ℕ; zero; suc; pred)
open import Data.Product as Prod
open import Function as F hiding (_∋_) renaming (_∘_ to _⊚_)
import README.DependentlyTyped.Equality-checker as EC; open EC Uni₀ ext
import README.DependentlyTyped.NBE as NBE; open NBE Uni₀ ext
import README.DependentlyTyped.NormalForm as NF
open NF Uni₀ hiding (⌊_⌋; no)
import README.DependentlyTyped.Raw-term as RT; open RT Uni₀
import README.DependentlyTyped.Term as Term; open Term Uni₀
import README.DependentlyTyped.Term.Substitution as S; open S Uni₀
open import Relation.Binary.PropositionalEquality as P using (_≡_)
open import Relation.Nullary
import Relation.Nullary.Decidable as Dec
open import Relation.Nullary.Product

open P.≡-Reasoning
open RawMonadZero (Maybe.monadZero {f = Level.zero})

-- Computes a syntactic type for a variable from a matching syntactic
-- context.

type-of-var : ∀ {Γ σ} → Γ ∋ σ → Γ ctxt → Γ ⊢ σ type
type-of-var zero    (Γ′ ▻ σ′) = σ′               /⊢t wk
type-of-var (suc x) (Γ′ ▻ σ′) = type-of-var x Γ′ /⊢t wk

-- Infers the type of a variable, if possible.

infer-var : (Γ : Ctxt) (x : ℕ) →
            Dec (∃₂ λ σ (x′ : Γ ∋ σ) → position x′ ≡ x)
infer-var ε x = no helper
  where
  helper : ¬ ∃₂ λ σ (x′ : ε ∋ σ) → position x′ ≡ x
  helper (_ , () , _)
infer-var (Γ ▻ σ) zero    = yes (σ /̂ ŵk , zero , P.refl)
infer-var (Γ ▻ σ) (suc x) =
  Dec.map′ (Prod.map (λ σ → σ /̂ ŵk) (Prod.map suc (P.cong suc)))
           helper
           (infer-var Γ x)
  where
  helper : (∃₂ λ τ (x′ : Γ ▻ σ ∋ τ) → position x′ ≡ suc x) →
           (∃₂ λ τ (x′ : Γ     ∋ τ) → position x′ ≡ x)
  helper (._ , zero   , ())
  helper (._ , suc x′ , eq) = (_ , x′ , P.cong pred eq)

-- Infers the /syntactic/ type of a variable, if possible.

infer-var-syntactic :
  ∀ {Γ} → Γ ctxt → (x : ℕ) →
  Dec (∃ λ σ → Γ ⊢ σ type × ∃ λ (x′ : Γ ∋ σ) → position x′ ≡ x)
infer-var-syntactic {Γ} Γ′ x = Dec.map′
  (Prod.map F.id (λ p → type-of-var (proj₁ p) Γ′ , proj₁ p , proj₂ p))
  (Prod.map F.id proj₂)
  (infer-var Γ x)

mutual

  -- Tries to infer a well-typed form of a raw type.

  infer-ty :
    ∀ {Γ} → Γ ctxt → (σ : Raw-ty) →
    Maybe (∃₂ λ σ′ (σ″ : Γ ⊢ σ′ type) → ⌊ σ″ ⌋ty ≡ ⌊ σ ⌋raw-ty)
  infer-ty Γ′ ⋆      = return (_ , ⋆ , P.refl)
  infer-ty Γ′ (el t) =
    check Γ′ ⋆ t                      >>= λ { (t′ , eq) →
    return (_ , el t′ , P.cong el eq) }
  infer-ty Γ′ (π σ′₁ σ′₂) =
    infer-ty Γ′          σ′₁                     >>= λ { (_ , σ′₁′ , eq₁) →
    infer-ty (Γ′ ▻ σ′₁′) σ′₂                     >>= λ { (_ , σ′₂′ , eq₂) →
    return (_ , π σ′₁′ σ′₂′ , P.cong₂ π eq₁ eq₂) }}

  -- Tries to infer a type for a term. In the case of success a
  -- well-typed term is returned.

  infer :
    ∀ {Γ} → Γ ctxt → (t : Raw) →
    Maybe (∃ λ σ → Γ ⊢ σ type × ∃ λ (t′ : Γ ⊢ σ) → ⌊ t′ ⌋ ≡ ⌊ t ⌋raw)
  infer Γ′ (var x) with infer-var-syntactic Γ′ x
  ... | yes (_ , σ′ , x′ , eq) = return (_ , σ′ , var x′ , P.cong var eq)
  ... | no _                   = ∅
  infer Γ′ (ƛ t)     = ∅
  infer Γ′ (t₁ · t₂) =
    infer Γ′ t₁ >>=
    λ { (._ , π σ′₁ σ′₂ , t₁′ , eq₁) →
            check Γ′ σ′₁ t₂                           >>= λ { (t₂′ , eq₂) →
            return (_ , σ′₂ /⊢t sub t₂′ , t₁′ · t₂′ ,
                    P.cong₂ _·_ eq₁ eq₂) }
      ; _ → ∅
      }
  infer Γ′ (t ∶ σ) =
    infer-ty Γ′ σ             >>= λ { (_ , σ′ , eq) →
    check Γ′ σ′ t             >>= λ { (t′ , eq) →
    return (_ , σ′ , t′ , eq) }}

  -- Tries to type-check a term. In the case of success a well-typed
  -- term is returned.

  check : ∀ {Γ σ} → Γ ctxt → (σ′ : Γ ⊢ σ type) (t : Raw) →
          Maybe (∃ λ (t′ : Γ ⊢ σ) → ⌊ t′ ⌋ ≡ ⌊ t ⌋raw)
  check Γ′ (π σ′₁ σ′₂) (ƛ t) =
    check (Γ′ ▻ σ′₁) σ′₂ t      >>= λ { (t′ , eq) →
    return (ƛ t′ , P.cong ƛ eq) }
  check Γ′ σ′ t =
    infer Γ′ t                                           >>= λ { (_ , τ′ , t′ , eq₁) →
    τ′ ≟-Type σ′                                         >>= λ eq₂ →
    return (P.subst (_⊢_ _) (≅-Type-⇒-≡ eq₂) t′ , (begin
              ⌊ P.subst (_⊢_ _) (≅-Type-⇒-≡ eq₂) t′ ⌋  ≡⟨ ⌊⌋-cong $ drop-subst-⊢ F.id (≅-Type-⇒-≡ eq₂) ⟩
              ⌊ t′ ⌋                                   ≡⟨ eq₁ ⟩
              ⌊ t ⌋raw                                 ∎)) }

-- Tries to establish that the given raw term has the given raw type
-- (in the empty context).

infix 4 _∋?_

_∋?_ : (σ : Raw-ty) (t : Raw) →
       Maybe (∃₂ λ (σ′ : Type ε) (σ″ : ε ⊢ σ′ type) →
              ∃  λ (t′ : ε ⊢ σ′) →
                   ⌊ σ″ ⌋ty ≡ ⌊ σ ⌋raw-ty × ⌊ t′ ⌋ ≡ ⌊ t ⌋raw)
σ ∋? t = infer-ty ε σ                      >>= λ { (σ′ , σ″ , eq₁) →
         check ε σ″ t                      >>= λ { (t′ , eq₂) →
         return (σ′ , σ″ , t′ , eq₁ , eq₂) }}

------------------------------------------------------------------------
-- Examples

private

  σ₁ : Raw-ty
  σ₁ = π ⋆ ⋆

  σ₁′ : Type ε
  σ₁′ = proj₁ $ from-just $ infer-ty ε σ₁

  t₁ : Raw
  t₁ = ƛ (var zero)

  t₁′ : ε ⊢ σ₁′
  t₁′ = proj₁ $ proj₂ $ proj₂ $ from-just $ σ₁ ∋? t₁

  t₂ : ε ▻ (⋆ , _) ⊢ (⋆ , _)
  t₂ = proj₁ $ proj₂ $ proj₂ $ from-just $ infer (ε ▻ ⋆) (var zero)

  t₃ : Raw
  t₃ = (ƛ (var zero) ∶ π ⋆ ⋆) · var zero

  t₃′ : ε ▻ (⋆ , _) ⊢ (⋆ , _)
  t₃′ = proj₁ $ proj₂ $ proj₂ $ from-just $ infer (ε ▻ ⋆) t₃


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

module Light.Library.Relation.Decidable where

open import Light.Level using (Setω ; Level)
open import Light.Variable.Levels
open import Light.Variable.Sets
open import Light.Library.Relation using (Base ; Kind ; Style)
open import Light.Indexed using (Indexed ; thing ; indexed)
open import Light.Package using (Package)

record Dependencies : Setω where

record Library (dependencies : Dependencies) : Setω where
      field base : Base

      open Style (base .Base.style) using (True ; False)
      open Kind (base .Base.kind) using (Index ; Proposition)

      field
            ℓf : Index → Level
            Decidable : ∀ {i} → Proposition i → Set (ℓf i)
      kind : Kind
      kind = record { Index = Indexed Index Proposition ; Proposition = λ i → Decidable (thing i) }
      field
            instance style : Style kind
            if′_then_else_ : ∀ {i} {𝕒 : Proposition i} → Decidable 𝕒 → 𝕓 → 𝕓 → 𝕓
            if_then_else_ :
                  ∀ {i} {𝕒 : Proposition i}
                  → Decidable 𝕒
                  → (∀ ⦃ witness : True 𝕒 ⦄ → 𝕓)
                  → (∀ ⦃ witness : False 𝕒 ⦄ → 𝕓)
                  → 𝕓

            to‐witness : ∀ {i} {𝕒 : Proposition i} {a? : Decidable 𝕒} ⦃ truth : Style.True style a? ⦄ → True 𝕒
            to‐false‐witness : ∀ {i} {𝕒 : Proposition i} {a? : Decidable 𝕒} ⦃ falsehood : Style.False style a? ⦄ → False 𝕒

            ⦃ from‐witness ⦄ : ∀ {i} {𝕒 : Proposition i} {a? : Decidable 𝕒} ⦃ witness : True 𝕒 ⦄ → Style.True style a?
            ⦃ from‐false‐witness ⦄ : ∀ {i} {𝕒 : Proposition i} {a? : Decidable 𝕒} ⦃ witness : False 𝕒 ⦄ → Style.False style a?

            yes : ∀ {i} {𝕒 : Proposition i} → True 𝕒 → Decidable 𝕒
            no : ∀ {i} {𝕒 : Proposition i} → False 𝕒 → Decidable 𝕒
      instance base′ = record { kind = kind ; style = style }

open Library ⦃ ... ⦄ public


{-# OPTIONS --safe #-}
module Cubical.Algebra.CommRingSolver.Solver where

open import Cubical.Foundations.Prelude

open import Cubical.Data.FinData
open import Cubical.Data.Nat using (ℕ)
open import Cubical.Data.Nat.Order using (zero-≤)
open import Cubical.Data.Vec.Base
open import Cubical.Algebra.CommRing
open import Cubical.Algebra.Ring
open import Cubical.Algebra.CommRingSolver.RawAlgebra renaming (⟨_⟩ to ⟨_⟩ᵣ)
open import Cubical.Algebra.CommRingSolver.AlgebraExpression
open import Cubical.Algebra.CommRingSolver.IntAsRawRing
open import Cubical.Algebra.CommRingSolver.HornerForms
open import Cubical.Algebra.CommRingSolver.EvalHom
open import Cubical.Algebra.CommRingSolver.HornerEval

private
  variable
    ℓ : Level

module EqualityToNormalform (R : CommRing ℓ) where
  νR = CommRing→RawℤAlgebra R
  open CommRingStr (snd R)
  open RingTheory (CommRing→Ring R)
  open Eval ℤAsRawRing νR
  open IteratedHornerOperations νR
  open HomomorphismProperties R

  ℤExpr : (n : ℕ) → Type _
  ℤExpr = Expr ℤAsRawRing (fst R)

  normalize : {n : ℕ} → ℤExpr n → IteratedHornerForms νR n
  normalize {n = n} (K r) = Constant n νR r
  normalize {n = n} (∣ k) = Variable n νR k
  normalize (x +' y) =
    (normalize x) +ₕ (normalize y)
  normalize (x ·' y) =
    (normalize x) ·ₕ (normalize y)
  normalize (-' x) =  -ₕ (normalize x)

  isEqualToNormalform :
       {n : ℕ} (e : ℤExpr n) (xs : Vec (fst R) n)
     → eval (normalize e) xs ≡ ⟦ e ⟧ xs
  isEqualToNormalform (K r) [] = refl
  isEqualToNormalform {n = ℕ.suc n} (K r) (x ∷ xs) =
     eval (Constant (ℕ.suc n) νR r) (x ∷ xs)         ≡⟨ refl ⟩
     eval (0ₕ ·X+ Constant n νR r) (x ∷ xs)           ≡⟨ combineCasesEval R 0ₕ (Constant n νR r) x xs ⟩
     eval 0ₕ (x ∷ xs) · x + eval (Constant n νR r) xs ≡⟨ cong (λ u → u · x + eval (Constant n νR r) xs)
                                                             (Eval0H (x ∷ xs)) ⟩
     0r · x + eval (Constant n νR r) xs               ≡⟨ cong
                                                          (λ u → u + eval (Constant n νR r) xs)
                                                          (0LeftAnnihilates _) ⟩
     0r + eval (Constant n νR r) xs                   ≡⟨ +IdL _ ⟩
     eval (Constant n νR r) xs                        ≡⟨ isEqualToNormalform (K r) xs ⟩
     _ ∎

  isEqualToNormalform (∣ zero) (x ∷ xs) =
    eval (1ₕ ·X+ 0ₕ) (x ∷ xs)           ≡⟨ combineCasesEval R 1ₕ 0ₕ x xs ⟩
    eval 1ₕ (x ∷ xs) · x + eval 0ₕ xs   ≡⟨ cong (λ u → u · x + eval 0ₕ xs)
                                              (Eval1ₕ (x ∷ xs)) ⟩
    1r · x + eval 0ₕ xs                 ≡⟨ cong (λ u → 1r · x + u ) (Eval0H xs) ⟩
    1r · x + 0r                        ≡⟨ +IdR _ ⟩
    1r · x                             ≡⟨ ·IdL _ ⟩
    x ∎
  isEqualToNormalform {n = ℕ.suc n} (∣ (suc k)) (x ∷ xs) =
      eval (0ₕ ·X+ Variable n νR k) (x ∷ xs)           ≡⟨ combineCasesEval R 0ₕ (Variable n νR k) x xs ⟩
      eval 0ₕ (x ∷ xs) · x + eval (Variable n νR k) xs ≡⟨ cong (λ u → u · x + eval (Variable n νR k) xs)
                                                              (Eval0H (x ∷ xs)) ⟩
      0r · x + eval (Variable n νR k) xs              ≡⟨ cong (λ u → u + eval (Variable n νR k) xs)
                                                              (0LeftAnnihilates _) ⟩
      0r + eval (Variable n νR k) xs                  ≡⟨ +IdL _ ⟩
      eval (Variable n νR k) xs                       ≡⟨ isEqualToNormalform (∣ k) xs ⟩
      ⟦ ∣ (suc k) ⟧ (x ∷ xs) ∎

  isEqualToNormalform (-' e) [] =
    eval (-ₕ (normalize e)) []  ≡⟨ -EvalDist (normalize e) [] ⟩
    - eval (normalize e) []    ≡⟨ cong -_ (isEqualToNormalform e [] ) ⟩
    - ⟦ e ⟧ [] ∎
  isEqualToNormalform (-' e) (x ∷ xs) =
    eval (-ₕ (normalize e)) (x ∷ xs) ≡⟨ -EvalDist (normalize e) (x ∷ xs) ⟩
    - eval (normalize e) (x ∷ xs)    ≡⟨ cong -_ (isEqualToNormalform e (x ∷ xs) ) ⟩
    - ⟦ e ⟧ (x ∷ xs) ∎

  isEqualToNormalform (e +' e₁) [] =
        eval (normalize e +ₕ normalize e₁) []
      ≡⟨ +Homeval (normalize e) _ [] ⟩
        eval (normalize e) []
        + eval (normalize e₁) []
      ≡⟨ cong (λ u → u + eval (normalize e₁) [])
              (isEqualToNormalform e []) ⟩
        ⟦ e ⟧ []
        + eval (normalize e₁) []
      ≡⟨ cong (λ u → ⟦ e ⟧ [] + u) (isEqualToNormalform e₁ []) ⟩
        ⟦ e ⟧ [] + ⟦ e₁ ⟧ [] ∎
  isEqualToNormalform (e +' e₁) (x ∷ xs) =
        eval (normalize e +ₕ normalize e₁) (x ∷ xs)
      ≡⟨ +Homeval (normalize e) _ (x ∷ xs) ⟩
        eval (normalize e) (x ∷ xs) + eval (normalize e₁) (x ∷ xs)
      ≡⟨ cong (λ u → u + eval (normalize e₁) (x ∷ xs))
              (isEqualToNormalform e (x ∷ xs)) ⟩
        ⟦ e ⟧ (x ∷ xs) + eval (normalize e₁) (x ∷ xs)
      ≡⟨ cong (λ u → ⟦ e ⟧ (x ∷ xs) + u) (isEqualToNormalform e₁ (x ∷ xs)) ⟩
        ⟦ e ⟧ (x ∷ xs) + ⟦ e₁ ⟧ (x ∷ xs) ∎

  isEqualToNormalform (e ·' e₁) [] =
        eval (normalize e ·ₕ normalize e₁) []
      ≡⟨ ·Homeval (normalize e) _ [] ⟩
        eval (normalize e) [] · eval (normalize e₁) []
      ≡⟨ cong (λ u → u · eval (normalize e₁) [])
              (isEqualToNormalform e []) ⟩
        ⟦ e ⟧ [] · eval (normalize e₁) []
      ≡⟨ cong (λ u → ⟦ e ⟧ [] · u) (isEqualToNormalform e₁ []) ⟩
        ⟦ e ⟧ [] · ⟦ e₁ ⟧ [] ∎

  isEqualToNormalform (e ·' e₁) (x ∷ xs) =
        eval (normalize e ·ₕ normalize e₁) (x ∷ xs)
      ≡⟨ ·Homeval (normalize e) _ (x ∷ xs) ⟩
        eval (normalize e) (x ∷ xs) · eval (normalize e₁) (x ∷ xs)
      ≡⟨ cong (λ u → u · eval (normalize e₁) (x ∷ xs))
              (isEqualToNormalform e (x ∷ xs)) ⟩
        ⟦ e ⟧ (x ∷ xs) · eval (normalize e₁) (x ∷ xs)
      ≡⟨ cong (λ u → ⟦ e ⟧ (x ∷ xs) · u) (isEqualToNormalform e₁ (x ∷ xs)) ⟩
        ⟦ e ⟧ (x ∷ xs) · ⟦ e₁ ⟧ (x ∷ xs) ∎

  solve :
    {n : ℕ} (e₁ e₂ : ℤExpr n) (xs : Vec (fst R) n)
    (p : eval (normalize e₁) xs ≡ eval (normalize e₂) xs)
    → ⟦ e₁ ⟧ xs ≡ ⟦ e₂ ⟧ xs
  solve e₁ e₂ xs p =
    ⟦ e₁ ⟧ xs                  ≡⟨ sym (isEqualToNormalform e₁ xs) ⟩
    eval (normalize e₁) xs ≡⟨ p ⟩
    eval (normalize e₂) xs ≡⟨ isEqualToNormalform e₂ xs ⟩
    ⟦ e₂ ⟧ xs ∎

ℤExpr : (R : CommRing ℓ) (n : ℕ)
        → _
ℤExpr R n = EqualityToNormalform.ℤExpr R n

solve : (R : CommRing ℓ)
        {n : ℕ} (e₁ e₂ : ℤExpr R n) (xs : Vec (fst R) n)
        (p : eval (EqualityToNormalform.normalize R e₁) xs ≡ eval (EqualityToNormalform.normalize R e₂) xs)
        → _
solve R = EqualityToNormalform.solve R


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

open import lib.Base
open import lib.PathGroupoid
open import lib.types.Empty
open import lib.types.Nat

module lib.types.TLevel where

⟨_⟩₋₁ : ℕ → ℕ₋₂
⟨ n ⟩₋₁ = S ⟨ n ⟩₋₂

⟨_⟩ : ℕ → ℕ₋₂
⟨ n ⟩ = S (S ⟨ n ⟩₋₂)

infixl 80 _+2+_
_+2+_ : ℕ₋₂ → ℕ₋₂ → ℕ₋₂
⟨-2⟩ +2+ n = n
S m +2+ n = S (m +2+ n)

+2+-unit-r : (m : ℕ₋₂) → m +2+ ⟨-2⟩ == m
+2+-unit-r ⟨-2⟩ = idp
+2+-unit-r (S m) = ap S (+2+-unit-r m)

+2+-βr : (m n : ℕ₋₂) → m +2+ (S n) == S (m +2+ n)
+2+-βr ⟨-2⟩ n = idp
+2+-βr (S m) n = ap S (+2+-βr m n)

+2+-comm : (m n : ℕ₋₂) → m +2+ n == n +2+ m
+2+-comm m ⟨-2⟩ = +2+-unit-r m
+2+-comm m (S n) = +2+-βr m n ∙ ap S (+2+-comm m n)

+2+0 : (n : ℕ₋₂) → n +2+ 0 == S (S n)
+2+0 n = +2+-comm n 0

{- Inequalities -}
infix 40 _<T_
infix 40 _≤T_

data _<T_ : ℕ₋₂ → ℕ₋₂ → Type₀ where
  ltS : {m : ℕ₋₂} → m <T (S m)
  ltSR : {m n : ℕ₋₂} → m <T n → m <T (S n)

_≤T_ : ℕ₋₂ → ℕ₋₂ → Type₀
m ≤T n = Coprod (m == n) (m <T n)

-2<T : (m : ℕ₋₂) → ⟨-2⟩ <T S m
-2<T ⟨-2⟩ = ltS
-2<T (S m) = ltSR (-2<T m)

-2≤T : (m : ℕ₋₂) → ⟨-2⟩ ≤T m
-2≤T ⟨-2⟩ = inl idp
-2≤T (S m) = inr (-2<T m)

<T-trans : {m n k : ℕ₋₂} → m <T n → n <T k → m <T k
<T-trans lt₁ ltS = ltSR lt₁
<T-trans lt₁ (ltSR lt₂) = ltSR (<T-trans lt₁ lt₂)

≤T-refl : {m : ℕ₋₂} → m ≤T m
≤T-refl = inl idp

≤T-trans : {m n k : ℕ₋₂} → m ≤T n → n ≤T k → m ≤T k
≤T-trans {k = k} (inl p₁) lte₂ = transport (λ t → t ≤T k) (! p₁) lte₂
≤T-trans {m = m} lte₁ (inl p₂) = transport (λ t → m ≤T t) p₂ lte₁
≤T-trans (inr lt₁) (inr lt₂) = inr (<T-trans lt₁ lt₂)

<T-ap-S : {m n : ℕ₋₂} → m <T n → S m <T S n
<T-ap-S ltS = ltS
<T-ap-S (ltSR lt) = ltSR (<T-ap-S lt)

≤T-ap-S : {m n : ℕ₋₂} → m ≤T n → S m ≤T S n
≤T-ap-S (inl p) = inl (ap S p)
≤T-ap-S (inr lt) = inr (<T-ap-S lt)

<T-cancel-S : {m n : ℕ₋₂} → S m <T S n → m <T n
<T-cancel-S ltS = ltS
<T-cancel-S (ltSR lt) = <T-trans ltS lt

<T-+2+-l : {m n : ℕ₋₂} (k : ℕ₋₂) → m <T n → (k +2+ m) <T (k +2+ n)
<T-+2+-l ⟨-2⟩ lt = lt
<T-+2+-l (S k) lt = <T-ap-S (<T-+2+-l k lt)

≤T-+2+-l : {m n : ℕ₋₂} (k : ℕ₋₂) → m ≤T n → (k +2+ m) ≤T (k +2+ n)
≤T-+2+-l k (inl p) = inl (ap (λ t → k +2+ t) p)
≤T-+2+-l k (inr lt) = inr (<T-+2+-l k lt)

<T-+2+-r : {m n : ℕ₋₂} (k : ℕ₋₂) → m <T n → (m +2+ k) <T (n +2+ k)
<T-+2+-r k ltS = ltS
<T-+2+-r k (ltSR lt) = ltSR (<T-+2+-r k lt)

≤T-+2+-r : {m n : ℕ₋₂} (k : ℕ₋₂) → m ≤T n → (m +2+ k) ≤T (n +2+ k)
≤T-+2+-r k (inl p) = inl (ap (λ t → t +2+ k) p)
≤T-+2+-r k (inr lt) = inr (<T-+2+-r k lt)

private
  T-get-S : ℕ₋₂ → ℕ₋₂
  T-get-S ⟨-2⟩ = ⟨ 42 ⟩
  T-get-S (S n) = n

  T-S≠⟨-2⟩-type : ℕ₋₂ → Type₀
  T-S≠⟨-2⟩-type ⟨-2⟩ = Empty
  T-S≠⟨-2⟩-type (S n) = Unit

T-S≠⟨-2⟩ : (n : ℕ₋₂) → S n ≠ ⟨-2⟩
T-S≠⟨-2⟩ n p = transport T-S≠⟨-2⟩-type p unit

T-S≠ : (n : ℕ₋₂) → S n ≠ n
T-S≠ ⟨-2⟩ p = T-S≠⟨-2⟩ ⟨-2⟩ p
T-S≠ (S n) p = T-S≠ n (ap T-get-S p)

T-S+2+≠ : (n k : ℕ₋₂) → S (k +2+ n) ≠ n
T-S+2+≠ ⟨-2⟩ k p = T-S≠⟨-2⟩ (k +2+ ⟨-2⟩) p
T-S+2+≠ (S n) k p = T-S+2+≠ n k (ap T-get-S (ap S (! (+2+-βr k n)) ∙ p))

<T-witness : {m n : ℕ₋₂} → (m <T n) → Σ ℕ₋₂ (λ k → S k +2+ m == n)
<T-witness ltS = (⟨-2⟩ , idp)
<T-witness (ltSR lt) = let w' = <T-witness lt in (S (fst w') , ap S (snd w'))

≤T-witness : {m n : ℕ₋₂} → (m ≤T n) → Σ ℕ₋₂ (λ k → k +2+ m == n)
≤T-witness (inl p) = (⟨-2⟩ , p)
≤T-witness (inr lt) = let w' = <T-witness lt in (S (fst w') , snd w')

<T-to-≤T : {m n : ℕ₋₂} → m <T S n → m ≤T n
<T-to-≤T ltS = inl idp
<T-to-≤T (ltSR lt) = inr lt

<T-to-≠ : {m n : ℕ₋₂} → (m <T n) → m ≠ n
<T-to-≠ {m} {n} lt p = T-S+2+≠ m (fst w) (snd w ∙ ! p)
  where w = <T-witness lt

=-to-≮T : {m n : ℕ₋₂} → (m == n) → ¬ (m <T n)
=-to-≮T p lt = <T-to-≠ lt p

<T-to-≯T : {m n : ℕ₋₂} → (m <T n) → ¬ (n <T m)
<T-to-≯T lt gt = =-to-≮T idp (<T-trans lt gt)

<T-to-≱T : {m n : ℕ₋₂} → (m <T n) → ¬ (n ≤T m)
<T-to-≱T lt (inl p) = <T-to-≠ lt (! p)
<T-to-≱T lt (inr gt) = <T-to-≯T lt gt

{-
-2-monotone-< : {m n : ℕ} → (m < n) → (m -2 <T n -2)
-2-monotone-< ltS = ltS
-2-monotone-< (ltSR lt) = ltSR (-2-monotone-< lt)

-2-monotone-≤ : {m n : ℕ} → (m ≤ n) → (m -2 ≤T n -2)
-2-monotone-≤ (inl p) = inl (ap _-2 p)
-2-monotone-≤ (inr lt) = inr (-2-monotone-< lt)
-}

⟨⟩-monotone-< : {m n : ℕ} → (m < n) → (⟨ m ⟩ <T ⟨ n ⟩)
⟨⟩-monotone-< ltS = ltS
⟨⟩-monotone-< (ltSR lt) = ltSR (⟨⟩-monotone-< lt)

⟨⟩-monotone-≤ : {m n : ℕ} → (m ≤ n) → (⟨ m ⟩ ≤T ⟨ n ⟩)
⟨⟩-monotone-≤ (inl p) = inl (ap ⟨_⟩ p)
⟨⟩-monotone-≤ (inr lt) = inr (⟨⟩-monotone-< lt)

minT : ℕ₋₂ → ℕ₋₂ → ℕ₋₂
minT ⟨-2⟩ n = ⟨-2⟩
minT (S m) ⟨-2⟩ = ⟨-2⟩
minT (S m) (S n) = S (minT m n)

minT≤l : (m n : ℕ₋₂) → minT m n ≤T m
minT≤l ⟨-2⟩ n = inl idp
minT≤l (S m) ⟨-2⟩ = -2≤T (S m)
minT≤l (S m) (S n) = ≤T-ap-S (minT≤l m n)

minT≤r : (m n : ℕ₋₂) → minT m n ≤T n
minT≤r ⟨-2⟩ n = -2≤T n
minT≤r (S m) ⟨-2⟩ = inl idp
minT≤r (S m) (S n) = ≤T-ap-S (minT≤r m n)

minT-out : (m n : ℕ₋₂) → Coprod (minT m n == m) (minT m n == n)
minT-out ⟨-2⟩ _ = inl idp
minT-out (S _) ⟨-2⟩ = inr idp
minT-out (S m) (S n) with minT-out m n
minT-out (S m) (S n) | inl p = inl (ap S p)
minT-out (S m) (S n) | inr q = inr (ap S q)

minT-out-l : {m n : ℕ₋₂} → (m ≤T n) → minT m n == m
minT-out-l {m} {n} lte with minT-out m n
minT-out-l lte | inl eqm = eqm
minT-out-l (inl p) | inr eqn = eqn ∙ ! p
minT-out-l {m} {n} (inr lt) | inr eq =
  ⊥-rec (<T-to-≱T (transport (λ k → m <T k) (! eq) lt) (minT≤l m n))


-- Andreas, 2019-10-21, issue #4148, reported by omelkonian

{-# OPTIONS -v impossible:100 #-}

postulate
  A : Set

module M (I : Set) where

  postulate
    P : I → Set

  record R (i : I) : Set where
    constructor mk
    field
      f : P i

open module N = M A

data D : ∀ {i} → R i → Set where
  c : ∀ {i} {t : P i} → D (mk t)

test : ∀ {i} {t : R i} → D t → Set₁
test c = Set

-- WAS: internal error in a sanity check in etaExpandRecord'_

-- Should succeed


module Structure.Relator.Proofs where

import      Data.Either as Either
import      Data.Tuple as Tuple
open import Functional
open import Function.Proofs
open import Logic
open import Logic.Propositional.Proofs.Structures
open import Logic.Propositional
open import Logic.Predicate
import      Lvl
open import Structure.Function
open import Structure.Operator
open import Structure.Setoid
open import Structure.Relator.Properties
open import Structure.Relator
open import Syntax.Transitivity
open import Type

private variable ℓ ℓ₁ ℓ₂ ℓ₃ ℓₗ ℓₗ₁ ℓₗ₂ ℓₗ₃ ℓₗ₄ ℓₑ ℓₑ₁ ℓₑ₂ ℓₑ₃ ℓₑ₄ : Lvl.Level
private variable A B A₁ A₂ B₁ B₂ : Type{ℓ}

[≡]-binaryRelator : ∀ ⦃ equiv : Equiv{ℓₗ}(A) ⦄ → BinaryRelator ⦃ equiv ⦄ (_≡_)
BinaryRelator.substitution [≡]-binaryRelator {x₁} {y₁} {x₂} {y₂} xy1 xy2 x1x2 =
  y₁ 🝖-[ xy1 ]-sym
  x₁ 🝖-[ x1x2 ]
  x₂ 🝖-[ xy2 ]
  y₂ 🝖-end

reflexive-binaryRelator-sub : ∀ ⦃ equiv : Equiv{ℓₗ}(A) ⦄ {_▫_ : A → A → Type{ℓ}} ⦃ refl : Reflexivity(_▫_) ⦄ ⦃ rel : BinaryRelator ⦃ equiv ⦄ (_▫_) ⦄ → ((_≡_) ⊆₂ (_▫_))
_⊆₂_.proof (reflexive-binaryRelator-sub {_▫_ = _▫_}) xy = substitute₂ᵣ(_▫_) xy (reflexivity(_▫_))

module _
  ⦃ equiv-A : Equiv{ℓₑ₁}(A) ⦄
  ⦃ equiv-B : Equiv{ℓₑ₂}(B) ⦄
  {f : A → B} ⦃ func : Function(f) ⦄
  {P : B → Stmt{ℓₗ₃}} ⦃ rel : UnaryRelator(P) ⦄
  where

  [∘]-unaryRelator : UnaryRelator(P ∘ f)
  [∘]-unaryRelator = [↔]-to-[←] relator-function₁ ([∘]-function ⦃ equiv-c = [↔]-equiv ⦄ ⦃ func-f = [↔]-to-[→] relator-function₁ rel ⦄)

module _
  ⦃ equiv-A₁ : Equiv{ℓₑ₁}(A₁) ⦄
  ⦃ equiv-B₁ : Equiv{ℓₑ₂}(B₁) ⦄
  ⦃ equiv-A₂ : Equiv{ℓₑ₃}(A₂) ⦄
  ⦃ equiv-B₂ : Equiv{ℓₑ₄}(B₂) ⦄
  {f : A₁ → A₂} ⦃ func-f : Function(f) ⦄
  {g : B₁ → B₂} ⦃ func-g : Function(g) ⦄
  {_▫_ : A₂ → B₂ → Stmt{ℓₗ}} ⦃ rel : BinaryRelator(_▫_) ⦄
  where

  [∘]-binaryRelator : BinaryRelator(x ↦ y ↦ f(x) ▫ g(y))
  BinaryRelator.substitution [∘]-binaryRelator xy1 xy2 = substitute₂(_▫_) (congruence₁(f) xy1) (congruence₁(g) xy2)

module _
  ⦃ equiv-A : Equiv{ℓₑ}(A) ⦄
  {P : A → Stmt{ℓₗ₁}} ⦃ rel-P : UnaryRelator(P) ⦄
  {▫ : Stmt{ℓₗ₁} → Stmt{ℓₗ₂}} ⦃ rel : Function ⦃ [↔]-equiv ⦄ ⦃ [↔]-equiv ⦄ ▫ ⦄
  where

  unaryRelator-sub : UnaryRelator(▫ ∘ P)
  unaryRelator-sub = [∘]-unaryRelator
    ⦃ equiv-B = [↔]-equiv ⦄
    ⦃ func = [↔]-to-[→] relator-function₁ rel-P ⦄
    ⦃ rel = [↔]-to-[←] (relator-function₁ ⦃ [↔]-equiv ⦄) rel ⦄

module _
  ⦃ equiv-A : Equiv{ℓₑ₁}(A) ⦄
  ⦃ equiv-B : Equiv{ℓₑ₂}(B) ⦄
  {P : A → Stmt{ℓₗ₁}} ⦃ rel-P : UnaryRelator(P) ⦄
  {Q : B → Stmt{ℓₗ₂}} ⦃ rel-Q : UnaryRelator(Q) ⦄
  {_▫_ : Stmt{ℓₗ₁} → Stmt{ℓₗ₂} → Stmt{ℓₗ₃}} ⦃ rel : BinaryOperator ⦃ [↔]-equiv ⦄ ⦃ [↔]-equiv ⦄ ⦃ [↔]-equiv ⦄ (_▫_) ⦄
  where

  binaryRelator-sub : BinaryRelator(x ↦ y ↦ P(x) ▫ Q(y))
  binaryRelator-sub = [∘]-binaryRelator
    ⦃ equiv-A₂ = [↔]-equiv ⦄
    ⦃ equiv-B₂ = [↔]-equiv ⦄
    ⦃ func-f = [↔]-to-[→] relator-function₁ rel-P ⦄
    ⦃ func-g = [↔]-to-[→] relator-function₁ rel-Q ⦄
    ⦃ rel = [↔]-to-[←] (relator-function₂ ⦃ [↔]-equiv ⦄ ⦃ [↔]-equiv ⦄) rel ⦄

module _
  ⦃ equiv-A : Equiv{ℓₑ₁}(A) ⦄
  ⦃ equiv-B : Equiv{ℓₑ₂}(B) ⦄
  {P : A → Stmt{ℓₗ₁}} ⦃ rel-P : UnaryRelator(P) ⦄
  {Q : B → Stmt{ℓₗ₂}} ⦃ rel-Q : UnaryRelator(Q) ⦄
  where

  [→]-binaryRelator : BinaryRelator(x ↦ y ↦ (P(x) → Q(y)))
  [→]-binaryRelator = binaryRelator-sub{_▫_ = _→ᶠ_}

  [↔]-binaryRelator : BinaryRelator(x ↦ y ↦ (P(x) ↔ Q(y)))
  [↔]-binaryRelator = binaryRelator-sub{_▫_ = _↔_}

[→]-unaryRelator : ∀ ⦃ _ : Equiv{ℓₗ₃}(A) ⦄ {P : A → Stmt{ℓₗ₁}}{Q : A → Stmt{ℓₗ₂}} → ⦃ rel-P : UnaryRelator(P) ⦄ → ⦃ rel-Q : UnaryRelator(Q) ⦄ → UnaryRelator(\x → P(x) → Q(x))
UnaryRelator.substitution ([→]-unaryRelator {P = P}{Q = Q}) xy pxqx py = substitute₁(Q) xy (pxqx(substitute₁(P) (symmetry(_≡_) xy) py))

[∀]-unaryRelator : ∀ ⦃ _ : Equiv{ℓₗ₃}(A) ⦄ {P : B → A → Stmt{ℓₗ₁}} → ⦃ rel-P : ∀{x} → UnaryRelator(P(x)) ⦄ → UnaryRelator(\y → ∀{x} → P(x)(y))
UnaryRelator.substitution ([∀]-unaryRelator {P = P}) {x} {a} xy px {b} = substitute₁ (P b) xy px

[∃]-unaryRelator : ∀ ⦃ _ : Equiv{ℓₗ₃}(A) ⦄ {P : B → A → Stmt{ℓₗ₁}} → ⦃ rel-P : ∀{x} → UnaryRelator(P(x)) ⦄ → UnaryRelator(\y → ∃(x ↦ P(x)(y)))
UnaryRelator.substitution ([∃]-unaryRelator {P = P}) xy = [∃]-map-proof (substitute₁(P _) xy)

instance
  const-unaryRelator : ∀{P : Stmt{ℓₗ₁}} → ⦃ _ : Equiv{ℓₗ}(A) ⦄ → UnaryRelator{A = A}(const P)
  UnaryRelator.substitution const-unaryRelator = const id

[¬]-unaryRelator : ∀ ⦃ _ : Equiv{ℓₗ₂}(A) ⦄ {P : A → Stmt{ℓₗ₁}} → ⦃ rel-P : UnaryRelator(P) ⦄ → UnaryRelator(\x → ¬ P(x))
[¬]-unaryRelator {P = P} = [→]-unaryRelator

[∧]-unaryRelator : ∀ ⦃ _ : Equiv{ℓₗ₃}(A) ⦄ {P : A → Stmt{ℓₗ₁}}{Q : A → Stmt{ℓₗ₂}} → ⦃ rel-P : UnaryRelator(P) ⦄ → ⦃ rel-Q : UnaryRelator(Q) ⦄ → UnaryRelator(x ↦ P(x) ∧ Q(x))
UnaryRelator.substitution [∧]-unaryRelator xy = Tuple.map (substitute₁(_) xy) (substitute₁(_) xy)

[∨]-unaryRelator : ∀ ⦃ _ : Equiv{ℓₗ₃}(A) ⦄ {P : A → Stmt{ℓₗ₁}}{Q : A → Stmt{ℓₗ₂}} → ⦃ rel-P : UnaryRelator(P) ⦄ → ⦃ rel-Q : UnaryRelator(Q) ⦄ → UnaryRelator(x ↦ P(x) ∨ Q(x))
UnaryRelator.substitution [∨]-unaryRelator xy = Either.map (substitute₁(_) xy) (substitute₁(_) xy)

binary-unaryRelator : ∀ ⦃ _ : Equiv{ℓₗ₂}(A) ⦄ {P : A → A → Stmt{ℓₗ₁}} → ⦃ rel-P : BinaryRelator(P) ⦄ → UnaryRelator(P $₂_)
UnaryRelator.substitution (binary-unaryRelator {P = P}) xy pxx = substitute₂(P) xy xy pxx

binary-unaryRelatorₗ : ∀ ⦃ _ : Equiv{ℓₗ₁}(A) ⦄ ⦃ _ : Equiv{ℓₗ₂}(B) ⦄ {_▫_ : A → B → Stmt{ℓₗ₃}} → ⦃ rel-P : BinaryRelator(_▫_) ⦄ → ∀{x} → UnaryRelator(x ▫_)
UnaryRelator.substitution binary-unaryRelatorₗ xy x1x2 = substitute₂ _ (reflexivity(_≡_)) xy x1x2

binary-unaryRelatorᵣ : ∀ ⦃ _ : Equiv{ℓₗ₁}(A) ⦄ ⦃ _ : Equiv{ℓₗ₂}(B) ⦄ {_▫_ : A → B → Stmt{ℓₗ₃}} → ⦃ rel-P : BinaryRelator(_▫_) ⦄ → ∀{x} → UnaryRelator(_▫ x)
UnaryRelator.substitution binary-unaryRelatorᵣ xy x1x2 = substitute₂ _ xy (reflexivity(_≡_)) x1x2

binaryRelator-from-unaryRelator : ∀ ⦃ _ : Equiv{ℓₗ₂}(A) ⦄ {_▫_ : A → A → Stmt{ℓₗ₁}} → ⦃ relₗ : ∀{x} → UnaryRelator(_▫ x) ⦄ → ⦃ relᵣ : ∀{x} → UnaryRelator(x ▫_) ⦄ → BinaryRelator(_▫_)
BinaryRelator.substitution binaryRelator-from-unaryRelator xy1 xy2 = substitute₁ _ xy1 ∘ substitute₁ _ xy2

instance
  const-binaryRelator : ∀{P : Stmt{ℓₗ}} → ⦃ equiv-A : Equiv{ℓₗ₁}(A) ⦄ ⦃ equiv-B : Equiv{ℓₗ₂}(B) ⦄ → BinaryRelator{A = A}{B = B}((const ∘ const) P)
  BinaryRelator.substitution const-binaryRelator = (const ∘ const) id

-- TODO: Temporary until substitution is a specialization of congruence
[¬]-binaryRelator : ∀ ⦃ _ : Equiv{ℓₗ₂}(A) ⦄ ⦃ _ : Equiv{ℓₗ₃}(B) ⦄ {P : A → B → Stmt{ℓₗ₁}} → ⦃ rel-P : BinaryRelator(P) ⦄ → BinaryRelator(\x y → ¬ P(x)(y))
BinaryRelator.substitution ([¬]-binaryRelator {P = P}) xy₁ xy₂ npx py = npx(substitute₂(P) (symmetry(_≡_) xy₁) (symmetry(_≡_) xy₂) py)


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

open import lib.Basics

module lib.types.Lift where

⊙Lift : ∀ {i j} → Ptd i → Ptd (lmax i j)
⊙Lift {j = j} ⊙[ A , a ] =  ⊙[ Lift {j = j} A , lift a ]

⊙lift : ∀ {i j} {X : Ptd i} → X ⊙→ ⊙Lift {j = j} X
⊙lift = (lift , idp)

⊙lower : ∀ {i j} {X : Ptd i} → ⊙Lift {j = j} X ⊙→ X
⊙lower = (lower , idp)

lift-equiv : ∀ {i j} {A : Type i} → A ≃ Lift {j = j} A
lift-equiv = equiv lift lower (λ _ → idp) (λ _ → idp)

-- [lower-equiv] is in Equivalences.agda

instance
  Lift-level : ∀ {i j} {A : Type i} {n : ℕ₋₂}
    → has-level n A → has-level n (Lift {j = j} A)
  Lift-level p = equiv-preserves-level lift-equiv {{p}}

⊙lift-equiv : ∀ {i j} {X : Ptd i} → X ⊙≃ ⊙Lift {j = j} X
⊙lift-equiv = (⊙lift , snd lift-equiv)

⊙lower-equiv : ∀ {i j} {X : Ptd i} → ⊙Lift {j = j} X ⊙≃ X
⊙lower-equiv = (⊙lower , snd lower-equiv)

Lift-fmap : ∀ {i j k} {A : Type i} {B : Type j}
  → (A → B) → (Lift {j = k} A → Lift {j = k} B)
Lift-fmap f = lift ∘ f ∘ lower

Lift-fmap-equiv : ∀ {i j k} {A : Type i} {B : Type j}
  → (A → B) ≃ (Lift {j = k} A → Lift {j = k} B)
Lift-fmap-equiv = equiv Lift-fmap (λ f → lower ∘ f ∘ lift) (λ _ → idp) (λ _ → idp)

⊙Lift-fmap : ∀ {i j k} {X : Ptd i} {Y : Ptd j}
  → (X ⊙→ Y) → (⊙Lift {j = k} X ⊙→ ⊙Lift {j = k} Y)
⊙Lift-fmap f = ⊙lift ⊙∘ f ⊙∘ ⊙lower

⊙Lift-fmap-equiv : ∀ {i j k} {X : Ptd i} {Y : Ptd j}
  → (X ⊙→ Y) ≃ (⊙Lift {j = k} X ⊙→ ⊙Lift {j = k} Y)
⊙Lift-fmap-equiv = equiv ⊙Lift-fmap (λ f → ⊙lower ⊙∘ f ⊙∘ ⊙lift)
  (λ {(_ , idp) → idp}) (λ {(_ , idp) → idp})


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

open import HoTT
open import homotopy.FunctionOver
open import groups.ProductRepr
open import cohomology.Theory
open import cohomology.WedgeCofiber

{- For the cohomology group of a suspension ΣX, the group inverse has the
 - explicit form Cⁿ(flip-susp) : Cⁿ(ΣX) → Cⁿ(ΣX).
 -}

module cohomology.InverseInSusp {i} (CT : CohomologyTheory i)
  (n : ℤ) {X : Ptd i} where

open CohomologyTheory CT
open import cohomology.Functor CT
open import cohomology.BaseIndependence CT
open import cohomology.Wedge CT

private
  module CW = CWedge n (⊙Susp X) (⊙Susp X)

  module Subtract = SuspRec {C = fst (⊙Susp X ⊙∨ ⊙Susp X)}
    (winl south)
    (winr south)
    (λ x → ap winl (! (merid x)) ∙ wglue ∙ ap winr (merid x))

  subtract = Subtract.f

  ⊙subtract : ⊙Susp X ⊙→ ⊙Susp X ⊙∨ ⊙Susp X
  ⊙subtract = (subtract , ! (ap winl (merid (pt X))))

  projl-subtract : ∀ σ → projl _ _ (subtract σ) == Susp-flip σ
  projl-subtract = Susp-elim idp idp $
    ↓-='-from-square ∘ vert-degen-square ∘ λ x →
      ap-∘ (projl _ _) subtract (merid x)
      ∙ ap (ap (projl _ _)) (Subtract.merid-β x)
      ∙ ap-∙ (projl _ _) (ap winl (! (merid x))) (wglue ∙ ap winr (merid x))
      ∙ ((∘-ap (projl _ _) winl (! (merid x))
          ∙ ap-idf _)
         ∙2 (ap-∙ (projl _ _) wglue (ap winr (merid x))
             ∙ (Projl.glue-β _ _
                ∙2 (∘-ap (projl _ _) winr (merid x) ∙ ap-cst _ _))))
      ∙ ∙-unit-r _
      ∙ ! (FlipSusp.merid-β x)

  projr-subtract : ∀ σ → projr _ _ (subtract σ) == σ
  projr-subtract = Susp-elim idp idp $
    ↓-∘=idf-in' (projr _ _) subtract ∘ λ x →
      ap (ap (projr _ _)) (Subtract.merid-β x)
      ∙ ap-∙ (projr _ _) (ap winl (! (merid x))) (wglue ∙ ap winr (merid x))
      ∙ ((∘-ap (projr _ _) winl (! (merid x)) ∙ ap-cst _ _)
         ∙2 (ap-∙ (projr _ _) wglue (ap winr (merid x))
             ∙ (Projr.glue-β _ _
                ∙2 (∘-ap (projr _ _) winr (merid x) ∙ ap-idf _))))

  fold-subtract : ∀ σ → fold (subtract σ) == south
  fold-subtract = Susp-elim idp idp $
    ↓-app=cst-in ∘ ! ∘ λ x →
      ∙-unit-r _
      ∙ ap-∘ fold subtract (merid x)
      ∙ ap (ap fold) (Subtract.merid-β x)
      ∙ ap-∙ fold (ap winl (! (merid x))) (wglue ∙ ap winr (merid x))
      ∙ ((∘-ap fold winl (! (merid x)) ∙ ap-idf _)
         ∙2 (ap-∙ fold wglue (ap winr (merid x))
             ∙ (Fold.glue-β
                ∙2 (∘-ap fold winr (merid x) ∙ ap-idf _))))
      ∙ !-inv-l (merid x)

  cancel :
    ×ᴳ-fanin (C-is-abelian n _) (CF-hom n (⊙Susp-flip X)) (idhom _) ∘ᴳ ×ᴳ-diag
    == cst-hom
  cancel =
    ap2 (λ φ ψ → ×ᴳ-fanin (C-is-abelian n _) φ ψ ∘ᴳ ×ᴳ-diag)
        (! (CF-λ= n projl-subtract))
        (! (CF-ident n) ∙ ! (CF-λ= n projr-subtract))
    ∙ transport (λ {(G , φ , ψ) → φ ∘ᴳ ψ == cst-hom})
        (pair= (CW.path) $ ↓-×-in
          (CW.Wedge-in-over ⊙subtract)
          (CW.⊙Wedge-rec-over (⊙idf _) (⊙idf _)
           ▹ ap2 ×ᴳ-fanout (CF-ident n) (CF-ident n)))
        (! (CF-comp n ⊙fold ⊙subtract)
         ∙ CF-λ= n (λ σ → fold-subtract σ ∙ ! (merid (pt X)))
         ∙ CF-cst n)

C-Susp-flip-is-inv :
  CF-hom n (⊙Susp-flip X) == inv-hom (C n (⊙Susp X)) (C-is-abelian _ _)
C-Susp-flip-is-inv = group-hom= $ λ= λ g →
  ! (Group.inv-unique-l (C n (⊙Susp X)) _ g (app= (ap GroupHom.f cancel) g))


{-# OPTIONS --without-K --exact-split --rewriting --overlapping-instances #-}

open import lib.Basics
open import lib.NConnected
open import lib.NType2
open import lib.types.Truncation
open import lib.types.Sigma
open import lib.Equivalence
open import lib.types.Fin
open import lib.types.Coproduct

open import Graphs.Definition
open import Graphs.Properties

open import Coequalizers.Definition
open import Coequalizers.Misc
open import Groups.Definition
open import Coequalizers.PullbackStability
open import Coequalizers.PreserveEquivalence
open import Coequalizers.EdgeCoproduct
open import Graphs.SpanningTreeFinite
open import Graphs.SubTrees

open import Util.Misc
open import Util.DecEq

module NielsenSchreier {i : ULevel} where

{- We build towards the proof of the finite index Nielsen-Schreier theorem in several steps. -}

{- We will first show that subgroups of free higher groups are free higher groupoids. That is, we will show that
   for any family of types X over ⊤ / A, the total space Σ (⊤ / A) X is equivalent to the coequalizer of
   a graph. Moveover, we can give an explicit description of the graph. -}
module SubgroupsOfFreeHigherGroups (A : Type i) (X : (⊤ / A) → Type i) where
  instance
    {- Whenever we need a pair of maps (A × X fhg-base) ⇉ (X fhg-base) we will use the definition below. This
       applies in this module and wherever it is imported. -}
    higher-subgp-gph : Graph (A × X fhg-base) (X fhg-base)
    higher-subgp-gph = record { π₀ = snd ; π₁ = λ {(a , x) → transport X (quot a) x} }

  to-free-higher-gpd : Σ (⊤ / A) X ≃ (X fhg-base) / (A × X fhg-base)
  to-free-higher-gpd =
      Σ (⊤ / A) X
        ≃⟨ pb-equiv ⁻¹ ⟩ -- coequalizers are stable under pullback
      (Σ ⊤ (X ∘ c[_])) / (Σ A (X ∘ c[_] ∘ π₀))
        ≃⟨ Coeq-emap Σ₁-Unit (ide _) (λ _ → idp) (λ _ → idp) ⟩
      (X fhg-base) / (A × X fhg-base)
        ≃∎
      where
        open PullbackStability A ⊤ X -- contains definition of (Σ ⊤ (X ∘ c[_])) / (Σ A (X ∘ c[_] ∘ π₀))

{- We now show that subgroups of free groups are free groupoids. This is a 1-truncated version of
   the result above. -}
module SubGroupsOfFreeGroups (A : Type i) (X : ∥ ⊤ / A ∥₁ → Type i)
  ⦃ Xset : {z : ∥ ⊤ / A ∥₁} → is-set (X z) ⦄ where -- we now need to assume X is 0-truncated.
  open SubgroupsOfFreeHigherGroups A (X ∘ [_]) public -- also defines instance for (X fhg-base) / (A × X fhg-base)

  to-free-gpd : Σ ∥ ⊤ / A ∥₁ X ≃ ∥ X base / (A × X base) ∥₁
  to-free-gpd =
      Σ ∥ ⊤ / A ∥₁ X
        ≃⟨ Σ-emap-r (Trunc-elim λ z → ide _) ⟩
      Σ ∥ ⊤ / A ∥₁ X'
        ≃⟨ TruncRecType.flattening-Trunc _ ⟩
      ∥ Σ (⊤ / A) (X ∘ [_]) ∥₁
        ≃⟨ Trunc-emap to-free-higher-gpd ⟩
      ∥ X base / (A × X base) ∥₁
        ≃∎
      where
        X'-set : ∥ ⊤ / A ∥₁ → hSet i
        X'-set = Trunc-rec (λ z → ((X [ z ]) , Xset))

        X' = fst ∘ X'-set

        instance
          X-≃-gpd : {z : ∥ ⊤ / A ∥₁} → is-gpd (X z ≃ X' z)
          X-≃-gpd {z} = raise-level 0 (≃-level Xset (snd (X'-set z)))

{- A free groupoid with vertex set a definitionally finite set is merely equivalent
   to a free group. -}
strict-fin-free-gpd-is-free-gp :
  (n : ℕ) → (E : Type i) → (Edec : has-dec-eq E) →
  ⦃ gph : Graph E (Fin (S n)) ⦄ ⦃ conn : gph-is-connected gph ⦄ →
  ∥ Σ (Type i) (λ B → ∥ Coeq gph ∥₁ ≃ FreeGroup B) ∥
strict-fin-free-gpd-is-free-gp n E Edec =
  Trunc-fmap from-spanning-tree-fin (spanning-tree E Edec n)
  where
    from-spanning-tree-fin : SpanningTree E (Fin (S n)) (Fin n) (Fin (S n)) →
      Σ (Type i) (λ B → ∥ Fin (S n) / E ∥₁ ≃ FreeGroup B)
    from-spanning-tree-fin st =
      ¬E' , Trunc-emap eq
      where
        open SpanningTree st
        open ExtendSpanningTree E (Fin (S n)) (Fin n) (Fin (S n)) st
        open CoeqCoprodEquiv (Fin (S n)) (Fin n) ¬E'

        eq : Fin (S n) / E ≃ ⊤ / ¬E'
        eq =
          Fin (S n) / E
            ≃⟨ spantree-equiv ⟩
          Fin (S n) / ((Fin n) ⊔ ¬E')
            ≃⟨ edge-coproduct-expand ⟩
          (Fin (S n) / (Fin n)) / ¬E'
            ≃⟨ Coeq-emap (contr-equiv-Unit ⟨⟩) (ide _) (λ _ → idp) (λ _ → idp) ⟩
          ⊤ / ¬E'
            ≃∎

{- We generalise the above so that the vertex set only needs to be equivalent to a
   definitionally finite set. -}
fin-free-gpd-is-free-gp : (n : ℕ) → (E : Type i) → (Edec : has-dec-eq E) →
  (V : Type i) → (Vfin : V ≃ Fin (S n)) →
  ⦃ gph : Graph E V ⦄ ⦃ conn : gph-is-connected gph ⦄ →
  ∥ Σ (Type i) (λ B → ∥ Coeq gph ∥₁ ≃ FreeGroup B) ∥
fin-free-gpd-is-free-gp n E Edec V Vfin =
  Trunc-fmap (λ {(B , e) → B , (e ∘e Trunc-emap coeq-equiv)}) (strict-fin-free-gpd-is-free-gp n E Edec)
  where
    instance
      gph' : Graph E (Fin (S n))
      gph' = record { π₀ = (–> Vfin) ∘ π₀ ; π₁ = (–> Vfin) ∘ π₁ }

    coeq-equiv : (V / E) ≃ (Fin (S n) / E)
    coeq-equiv = Coeq-emap Vfin (ide E) (λ _ → idp) λ _ → idp

    instance
      conn' : gph-is-connected gph'
      conn' = equiv-preserves-level (Trunc-emap coeq-equiv )

{- We can now prove the finite index version of the Nielsen-Schreier theorem. -}
nielsen-schreier-finite-index :
  (A : Type i) (Adec : has-dec-eq A) -- we are given a type A with decidable equality
  (X : (FreeGroup A) → Type i) -- together with a covering space over the free group on A, that is a family of types X ...
  ⦃ conn : is-connected 0 (Σ (FreeGroup A) X) ⦄ -- such that the total space is connected
  (n : ℕ) (Xfin : X (base) ≃ Fin (S n)) → -- here we do the finite index case
  ∥ Σ (Type i) (λ B → Σ (FreeGroup A) X ≃ FreeGroup B) ∥ -- then the total space is a free group

nielsen-schreier-finite-index A Adec X n Xfin =
  Trunc-fmap (λ {(B , e) → B , e ∘e to-free-gpd}) -- we compose the two equivalences from earlier
             (fin-free-gpd-is-free-gp n (A × X base) (×-has-dec-eq Adec (equiv-preserves-dec-eq Xfin Fin-has-dec-eq)) (X base) Xfin)
               -- earlier we showed that free groupoids with finite vertex set are merely equivalent to free groups
  where
    open SubGroupsOfFreeGroups A X -- contains our proof that the subgroup is a free groupoid

    instance -- we use that X is a set and the free groupoid is connected
      Xset : {z : (FreeGroup A)} → is-set (X z)
      Xset {z} = Trunc-elim {P = λ z → is-set (X z)}
        (Coeq-elim (λ z → is-set (X [ z ]))
          (λ {unit → equiv-preserves-level (Xfin ⁻¹) ⦃ Fin-is-set ⦄}) λ _ → prop-has-all-paths-↓) z

      conn2 : is-connected 0 (X base / (A × X base))
      conn2 = equiv-preserves-level e
        where
          e : ∥ Σ (FreeGroup A) X ∥₀ ≃ ∥ X base / (A × X base) ∥₀
          e =
            ∥ Σ (FreeGroup A) X ∥₀
              ≃⟨ Trunc-emap (to-free-gpd ) ⟩
            ∥ ∥ X base / (A × X base) ∥₁ ∥₀
              ≃⟨ Trunc-fuse _ 0 1 ⟩
            ∥ X base / (A × X base) ∥₀
              ≃∎


module Common.Nat where

open import Agda.Builtin.Nat public
  using    ( Nat; zero; suc; _+_; _*_ )
  renaming ( _-_ to _∸_ )

{-# COMPILED_JS Nat function (x,v) { return (x < 1? v.zero(): v.suc(x-1)); } #-}
{-# COMPILED_JS zero 0 #-}
{-# COMPILED_JS suc function (x) { return x+1; } #-}

{-# COMPILED_JS _+_ function (x) { return function (y) { return x+y; }; } #-}
{-# COMPILED_JS _*_ function (x) { return function (y) { return x*y; }; } #-}
{-# COMPILED_JS _∸_ function (x) { return function (y) { return Math.max(0,x-y); }; } #-}

pred : Nat → Nat
pred zero    = zero
pred (suc n) = n