open import Prelude

module Implicits.Substitutions.Lemmas.Type where

open import Implicits.Syntax.Type
open import Implicits.Substitutions

open import Data.Fin.Substitution
open import Data.Fin.Substitution.Lemmas
open import Data.Vec.Properties
open import Extensions.Substitution
open import Data.Star using (Star; ε; _◅_)
open import Data.Product hiding (map)

typeLemmas : TermLemmas Type
typeLemmas = record { termSubst = TypeSubst.typeSubst; app-var = refl ; /✶-↑✶ = Lemma./✶-↑✶ }
  where
    open TypeSubst
    module Lemma {T₁ T₂} {lift₁ : Lift T₁ Type} {lift₂ : Lift T₂ Type} where

      open Lifted lift₁ using () renaming (_↑✶_ to _↑✶₁_; _/✶_ to _/✶₁_)
      open Lifted lift₂ using () renaming (_↑✶_ to _↑✶₂_; _/✶_ to _/✶₂_)

      /✶-↑✶ : ∀ {m n} (σs₁ : Subs T₁ m n) (σs₂ : Subs T₂ m n) →
                          (∀ k x → (simpl (tvar x)) /✶₁ σs₁ ↑✶₁ k ≡ (simpl (tvar x)) /✶₂ σs₂ ↑✶₂ k) →
                          ∀ k t → t /✶₁ σs₁ ↑✶₁ k ≡ t /✶₂ σs₂ ↑✶₂ k
      /✶-↑✶ ρs₁ ρs₂ hyp k (simpl (tvar x)) = hyp k x
      /✶-↑✶ ρs₁ ρs₂ hyp k (simpl (tc c)) = begin
          (simpl $ tc c) /✶₁ ρs₁ ↑✶₁ k
          ≡⟨ TypeApp.tc-/✶-↑✶ _ k ρs₁ ⟩
          (simpl $ tc c)
          ≡⟨ sym $ TypeApp.tc-/✶-↑✶ _ k ρs₂ ⟩
          (simpl $ tc c) /✶₂ ρs₂ ↑✶₂ k ∎
      /✶-↑✶ ρs₁ ρs₂ hyp k (simpl (a →' b)) = begin
          (simpl $ a →' b) /✶₁ ρs₁ ↑✶₁ k
          ≡⟨ TypeApp.→'-/✶-↑✶ _ k ρs₁ ⟩
          simpl ((a /✶₁ ρs₁ ↑✶₁ k) →' (b /✶₁ ρs₁ ↑✶₁ k))
          ≡⟨ cong₂ (λ a b → simpl (a →' b)) (/✶-↑✶ ρs₁ ρs₂ hyp k a) (/✶-↑✶ ρs₁ ρs₂ hyp k b) ⟩
          simpl ((a /✶₂ ρs₂ ↑✶₂ k) →' (b /✶₂ ρs₂ ↑✶₂ k))
          ≡⟨ sym (TypeApp.→'-/✶-↑✶ _ k ρs₂) ⟩
          (simpl $ a →' b) /✶₂ ρs₂ ↑✶₂ k
          ∎
      /✶-↑✶ ρs₁ ρs₂ hyp k (a ⇒ b) = begin
          (a ⇒ b) /✶₁ ρs₁ ↑✶₁ k
          ≡⟨ TypeApp.⇒-/✶-↑✶ _ k ρs₁ ⟩ --
          (a /✶₁ ρs₁ ↑✶₁ k) ⇒ (b /✶₁ ρs₁ ↑✶₁ k)
          ≡⟨ cong₂ _⇒_ (/✶-↑✶ ρs₁ ρs₂ hyp k a) (/✶-↑✶ ρs₁ ρs₂ hyp k b) ⟩
          (a /✶₂ ρs₂ ↑✶₂ k) ⇒ (b /✶₂ ρs₂ ↑✶₂ k)
          ≡⟨ sym (TypeApp.⇒-/✶-↑✶ _ k ρs₂) ⟩
          (a ⇒ b) /✶₂ ρs₂ ↑✶₂ k
          ∎
      /✶-↑✶ ρs₁ ρs₂ hyp k (∀' a) = begin
          (∀' a) /✶₁ ρs₁ ↑✶₁ k
          ≡⟨ TypeApp.∀'-/✶-↑✶ _ k ρs₁ ⟩
          ∀' (a /✶₁ ρs₁ ↑✶₁ (suc k))
          ≡⟨ cong ∀' (/✶-↑✶ ρs₁ ρs₂ hyp (suc k) a) ⟩
          ∀' (a /✶₂ ρs₂ ↑✶₂ (suc k))
          ≡⟨ sym (TypeApp.∀'-/✶-↑✶ _ k ρs₂) ⟩
          (∀' a) /✶₂ ρs₂ ↑✶₂ k
          ∎

open TermLemmas typeLemmas public hiding (var; id)
open AdditionalLemmas typeLemmas public
open TypeSubst using (module Lifted)

-- The above lemma /✶-↑✶ specialized to single substitutions
/-↑⋆ : ∀ {T₁ T₂} {lift₁ : Lift T₁ Type} {lift₂ : Lift T₂ Type} →
    let open Lifted lift₁ using () renaming (_↑⋆_ to _↑⋆₁_; _/_ to _/₁_)
        open Lifted lift₂ using () renaming (_↑⋆_ to _↑⋆₂_; _/_ to _/₂_)
    in
    ∀ {n k} (ρ₁ : Sub T₁ n k) (ρ₂ : Sub T₂ n k) →
    (∀ i x → (simpl (tvar x)) /₁ ρ₁ ↑⋆₁ i ≡ (simpl (tvar x)) /₂ ρ₂ ↑⋆₂ i) →
        ∀ i a → a /₁ ρ₁ ↑⋆₁ i ≡ a /₂ ρ₂ ↑⋆₂ i
/-↑⋆ ρ₁ ρ₂ hyp i a = /✶-↑✶ (ρ₁ ◅ ε) (ρ₂ ◅ ε) hyp i a

-- weakening a simple type gives a simple type
simpl-wk : ∀ {ν} k (τ : SimpleType (k + ν)) → ∃ λ τ' → (simpl τ) / wk ↑⋆ k ≡ simpl τ'
simpl-wk k (tc x) = , refl
simpl-wk k (tvar n) = , var-/-wk-↑⋆ k n
simpl-wk k (x →' x₁) = , refl


{- Formal verification of authenticated append-only skiplists in Agda, version 1.0.

   Copyright (c) 2021 Victor C Miraldo.
   Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.oracle.com/licenses/upl
-}
open import Data.Unit.NonEta
open import Data.Empty
open import Data.Sum
open import Data.Product
open import Data.Product.Properties
open import Data.Fin hiding (_<_; _≤_)
open import Data.Fin.Properties using () renaming (_≟_ to _≟Fin_)
open import Data.Nat renaming (_≟_ to _≟ℕ_; _≤?_ to _≤?ℕ_)
open import Data.Nat.Properties
open import Data.List renaming (map to List-map)
open import Data.List.Properties using (∷-injective; length-map)
open import Data.List.Relation.Unary.Any renaming (map to Any-map)
open import Data.List.Relation.Unary.All renaming (lookup to All-lookup; map to All-map)
open import Data.List.Relation.Unary.All.Properties hiding (All-map)
open import Data.List.Relation.Unary.Any.Properties renaming (map⁺ to Any-map⁺)
open import Data.List.Relation.Binary.Pointwise using (decidable-≡)
open import Data.Bool hiding (_<_; _≤_)
open import Data.Maybe renaming (map to Maybe-map)
open import Function

open import Relation.Binary.PropositionalEquality
open import Relation.Binary.Core
open import Relation.Nullary

-- This module defines the DepRel type, which represents the class of AAOSLs we
-- consider, and proves properties about any DepRel.

module AAOSL.Abstract.DepRel where

 open import AAOSL.Lemmas
 open import AAOSL.Abstract.Hash

 -- TODO-1: make names same as paper (lvlof -> maxlvl, etc.)
 -- The important bit is that we must have a dependency relation
 -- between these indexes.
 record DepRel : Set₁ where
   field
     lvlof      : ℕ → ℕ
     lvlof-z    : lvlof 0 ≡ 0
     lvlof-s    : ∀ m → 0 < lvlof (suc m)

   HopFrom    : ℕ → Set
   HopFrom    = Fin ∘ lvlof

   field
     hop-tgt        : {m : ℕ} → HopFrom m → ℕ

     hop-tgt-inj    : {m : ℕ}{h h' : HopFrom m}
                    → hop-tgt h ≡ hop-tgt h'
                    → h ≡ h'

     hop-<    : {m : ℕ}(h : HopFrom m)
              → hop-tgt h < m

     -- This property requires that any pair of hops is either nested or
     -- non-overlapping. When nonoverlapping, one end may coincide and
     -- when nested, both ends may coincide. As a diagram,
     --
     --                          h₂
     --            ⌜⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⌝
     --            ∣                             ∣
     --            ∣             h₁              ∣
     --            ∣         ⌜⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⌝      |
     --            |         |            |      |
     --         tgt h₂  <I  tgt h₁  ⋯     j₁     j₂
     --                                   ↑
     --                            The only option for j₁
     --                            is right here. It can be
     --                            the same as j₂ though.
     --                            For more intuition on this, check
     --                            Hops.agda
     --
     hops-nested-or-nonoverlapping : ∀{j₁ j₂}{h₁ : HopFrom j₁}{h₂ : HopFrom j₂}
       → hop-tgt h₂ < hop-tgt h₁
       → hop-tgt h₁ < j₂
       → j₁ ≤ j₂

   _≟Hop_ : {s : ℕ}(h l : HopFrom s) → Dec (h ≡ l)
   _≟Hop_ = _≟Fin_


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

open import Fragment.Equational.Theory

module Fragment.Equational.FreeExtension.Synthetic (Θ : Theory) where

open import Fragment.Algebra.Signature
open import Fragment.Algebra.Algebra (Σ Θ) using (Algebra)
open import Fragment.Equational.Model Θ
open import Fragment.Equational.Coproduct Θ
open import Fragment.Equational.FreeExtension.Base Θ
open import Fragment.Algebra.Free (Σ Θ) hiding (_~_)
open import Fragment.Algebra.Homomorphism (Σ Θ)
open import Fragment.Algebra.Quotient (Σ Θ)
open import Fragment.Setoid.Morphism using (_↝_)

open import Level using (Level; _⊔_)
open import Function using (_∘_)

open import Data.Nat using (ℕ)
open import Data.Fin using (Fin)
open import Data.Product using (proj₁; proj₂)
open import Data.Vec using (Vec; []; _∷_; map)
open import Data.Vec.Relation.Binary.Pointwise.Inductive as PW
  using (Pointwise; []; _∷_)

open import Relation.Binary using (Setoid; IsEquivalence)
import Relation.Binary.Reasoning.Setoid as Reasoning
import Relation.Binary.PropositionalEquality as PE

private
  variable
    a b ℓ₁ ℓ₂ ℓ₃ : Level

  module _ (A : Model {a} {ℓ₁}) (n : ℕ) where

    Terms : Algebra
    Terms = Free (Atoms ∥ A ∥/≈ n)

    open module T = Setoid Algebra.∥ Terms ∥/≈ renaming (_≈_ to _~_) using ()

    ∣inl∣ : ∥ A ∥ → T.Carrier
    ∣inl∣ x = atom (sta x)

    infix 4 _≈_

    data _≈_ : T.Carrier → T.Carrier → Set (a ⊔ ℓ₁) where
      refl     : ∀ {x} → x ≈ x
      sym      : ∀ {x y} → x ≈ y → y ≈ x
      trans    : ∀ {x y z} → x ≈ y → y ≈ z → x ≈ z
      inherit  : ∀ {x y} → x ~ y → x ≈ y
      evaluate : ∀ {n xs} → (f : ops (Σ Θ) n)
                 → term f (map ∣inl∣ xs) ≈ ∣inl∣ (A ⟦ f ⟧ xs)
      cong     : ∀ {n} → (f : ops (Σ Θ) n)
                 → ∀ {xs ys} → Pointwise _≈_ xs ys
                 → term f xs ≈ term f ys
      axiom    : ∀ {n} → (eq : eqs Θ n) → (θ : Env Terms n)
                 → ∣ inst Terms θ ∣ (proj₁ (Θ ⟦ eq ⟧ₑ))
                   ≈ ∣ inst Terms θ ∣ (proj₂ (Θ ⟦ eq ⟧ₑ))

    ≈-isEquivalence : IsEquivalence _≈_
    ≈-isEquivalence = record { refl  = refl
                             ; sym   = sym
                             ; trans = trans
                             }

    instance
      ≈-isDenom : IsDenominator Terms _≈_
      ≈-isDenom = record { isEquivalence = ≈-isEquivalence
                         ; isCompatible  = cong
                         ; isCoarser     = inherit
                         }

    module N = Setoid (∥ Terms ∥/ _≈_)

    Syn-models : Models (Terms / _≈_)
    Syn-models eq θ = begin
        ∣ inst (Terms / _≈_) θ ∣ lhs
      ≈⟨ N.sym (lemma {x = lhs}) ⟩
        ∣ inst Terms θ ∣ lhs
      ≈⟨ axiom eq θ ⟩
        ∣ inst Terms θ ∣ rhs
      ≈⟨ lemma {x = rhs} ⟩
        ∣ inst (Terms / _≈_) θ ∣ rhs
      ∎
      where open Reasoning (∥ Terms ∥/ _≈_)

            lhs = proj₁ (Θ ⟦ eq ⟧ₑ)
            rhs = proj₂ (Θ ⟦ eq ⟧ₑ)

            lemma = inst-universal (Terms / _≈_) θ
                      {h = (inc Terms _≈_) ⊙ (inst Terms θ) }
                      (λ x → PE.refl)

    Syn-isModel : IsModel (∥ Terms ∥/ _≈_)
    Syn-isModel = record { isAlgebra = Terms / _≈_ -isAlgebra
                         ; models    = Syn-models
                         }

    Syn : Model
    Syn = record { ∥_∥/≈   = ∥ Terms ∥/ _≈_
                 ; isModel = Syn-isModel
                 }

    ∣inl∣-cong : Congruent ≈[ A ] _≈_ ∣inl∣
    ∣inl∣-cong p = inherit (atom (sta p))

    ∣inl∣-hom : Homomorphic ∥ A ∥ₐ ∥ Syn ∥ₐ ∣inl∣
    ∣inl∣-hom f xs = evaluate f

    ∣inl∣⃗ : ∥ A ∥/≈ ↝ ∥ Syn ∥/≈
    ∣inl∣⃗ = record { ∣_∣      = ∣inl∣
                    ; ∣_∣-cong = ∣inl∣-cong
                    }

    inl : ∥ A ∥ₐ ⟿ ∥ Syn ∥ₐ
    inl = record { ∣_∣⃗    = ∣inl∣⃗
                 ; ∣_∣-hom = ∣inl∣-hom
                 }

    inr : ∥ J n ∥ₐ ⟿ ∥ Syn ∥ₐ
    inr = interp Syn (λ n → atom (dyn n))

    module _
      (X : Model {b} {ℓ₂})
      (f : ∥ A ∥ₐ ⟿ ∥ X ∥ₐ)
      (g : ∥ J n ∥ₐ ⟿ ∥ X ∥ₐ)
      where

      private module X = Setoid ∥ X ∥/≈
      open Reasoning ∥ X ∥/≈

      resid : Terms ⟿ ∥ X ∥ₐ
      resid = subst ∥ X ∥ₐ ∣ f ∣⃗ (λ n → ∣ g ∣ (atom (dyn n)))

      ∣resid∣-map-∣inl∣ : ∀ {m} {xs : Vec ∥ A ∥ m}
                          → Pointwise ≈[ X ]
                                      (map ∣ resid ∣ (map ∣inl∣ xs))
                                      (map ∣ f ∣ xs)
      ∣resid∣-map-∣inl∣ {xs = []}     = []
      ∣resid∣-map-∣inl∣ {xs = x ∷ xs} = X.refl ∷ ∣resid∣-map-∣inl∣

      mutual
        ∣resid∣-args-cong : ∀ {m} {xs ys : Vec ∥ Syn ∥ m}
                            → Pointwise _≈_ xs ys
                            → Pointwise ≈[ X ] (map ∣ resid ∣ xs)
                                               (map ∣ resid ∣ ys)
        ∣resid∣-args-cong []       = []
        ∣resid∣-args-cong (p ∷ ps) = ∣resid∣-cong p ∷ ∣resid∣-args-cong ps

        ∣resid∣-cong : Congruent _≈_ ≈[ X ] ∣ resid ∣
        ∣resid∣-cong refl        = X.refl
        ∣resid∣-cong (sym p)     = X.sym (∣resid∣-cong p)
        ∣resid∣-cong (trans p q) = X.trans (∣resid∣-cong p) (∣resid∣-cong q)
        ∣resid∣-cong (inherit p) = ∣ resid ∣-cong p
        ∣resid∣-cong (evaluate {xs = xs} op) = begin
            ∣ resid ∣ (term op (map ∣inl∣ xs))
          ≈⟨ X.sym (∣ resid ∣-hom op (map ∣inl∣ xs)) ⟩
            X ⟦ op ⟧ (map ∣ resid ∣ (map ∣inl∣ xs))
          ≈⟨ (X ⟦ op ⟧-cong) ∣resid∣-map-∣inl∣ ⟩
            X ⟦ op ⟧ (map ∣ f ∣ xs)
          ≈⟨ ∣ f ∣-hom op xs ⟩
            ∣ f ∣ (A ⟦ op ⟧ xs)
          ∎
        ∣resid∣-cong (cong f {xs = xs} {ys = ys} ps) = begin
            ∣ resid ∣ (term f xs)
          ≈⟨ X.sym (∣ resid ∣-hom f xs) ⟩
            X ⟦ f ⟧ (map ∣ resid ∣ xs)
          ≈⟨ (X ⟦ f ⟧-cong) (∣resid∣-args-cong ps) ⟩
            X ⟦ f ⟧ (map ∣ resid ∣ ys)
          ≈⟨ ∣ resid ∣-hom f ys ⟩
            ∣ resid ∣ (term f ys)
          ∎
        ∣resid∣-cong (axiom eq θ) = begin
            ∣ resid ∣ (∣ inst Terms θ ∣ lhs)
          ≈⟨ inst-assoc Terms θ resid {lhs} ⟩
            ∣ inst ∥ X ∥ₐ (∣ resid ∣ ∘ θ) ∣ lhs
          ≈⟨ ∥ X ∥ₐ-models eq (∣ resid ∣ ∘ θ) ⟩
            ∣ inst ∥ X ∥ₐ (∣ resid ∣ ∘ θ) ∣ rhs
          ≈⟨ X.sym (inst-assoc Terms θ resid {rhs}) ⟩
            ∣ resid ∣ (∣ inst Terms θ ∣ rhs)
          ∎
          where lhs = proj₁ (Θ ⟦ eq ⟧ₑ)
                rhs = proj₂ (Θ ⟦ eq ⟧ₑ)

      _[_,_] : ∥ Syn ∥ₐ ⟿ ∥ X ∥ₐ
      _[_,_] = factor Terms _≈_ resid ∣resid∣-cong

    module _
      {X : Model {b} {ℓ₂}}
      {f : ∥ A ∥ₐ ⟿ ∥ X ∥ₐ}
      {g : ∥ J n ∥ₐ ⟿ ∥ X ∥ₐ}
      where

      private module X = Setoid ∥ X ∥/≈
      open Reasoning ∥ X ∥/≈

      commute₁ : X [ f , g ] ⊙ inl ≗ f
      commute₁ = X.refl

      mutual
        map-commute₂ : ∀ {m} {xs : Vec ∥ J n ∥ m}
                       → Pointwise ≈[ X ]
                                   (map ∣ X [ f , g ] ⊙ inr ∣ xs)
                                   (map ∣ g ∣ xs)
        map-commute₂ {xs = []}     = []
        map-commute₂ {xs = x ∷ xs} = commute₂ ∷ map-commute₂

        commute₂ : X [ f , g ] ⊙ inr ≗ g
        commute₂ {atom (dyn _)} = X.refl
        commute₂ {term op xs}    = begin
            ∣ X [ f , g ] ⊙ inr ∣ (term op xs)
          ≈⟨ X.sym (∣ X [ f , g ] ⊙ inr ∣-hom op xs) ⟩
            X ⟦ op ⟧ (map ∣ X [ f , g ] ⊙ inr ∣ xs)
          ≈⟨ (X ⟦ op ⟧-cong) map-commute₂ ⟩
            X ⟦ op ⟧ (map ∣ g ∣ xs)
          ≈⟨ ∣ g ∣-hom op xs ⟩
            ∣ g ∣ (term op xs)
          ∎

      module _
        {h : ∥ Syn ∥ₐ ⟿ ∥ X ∥ₐ}
        (c₁ : h ⊙ inl ≗ f)
        (c₂ : h ⊙ inr ≗ g)
        where

        mutual
          map-universal : ∀ {m} {xs : Vec ∥ Syn ∥ m}
                          → Pointwise ≈[ X ]
                                      (map ∣ X [ f , g ] ∣ xs)
                                      (map ∣ h ∣ xs)
          map-universal {xs = []}     = []
          map-universal {xs = x ∷ xs} = universal ∷ map-universal

          universal : X [ f , g ] ≗ h
          universal {atom (sta x)} = X.sym c₁
          universal {atom (dyn x)} = begin
              ∣ X [ f , g ] ∣ (atom (dyn x))
            ≈⟨ commute₂ ⟩
              ∣ g ∣ (atom (dyn x))
            ≈⟨ X.sym c₂ ⟩
              ∣ h ∣ (atom (dyn x))
            ∎
          universal {term op xs}   = begin
              ∣ X [ f , g ] ∣ (term op xs)
            ≈⟨ X.sym (∣ X [ f , g ] ∣-hom op xs) ⟩
              X ⟦ op ⟧ (map ∣ X [ f , g ] ∣ xs)
            ≈⟨ (X ⟦ op ⟧-cong) map-universal ⟩
              X ⟦ op ⟧ (map ∣ h ∣ xs)
            ≈⟨ ∣ h ∣-hom op xs ⟩
              ∣ h ∣ (term op xs)
            ∎

    isFrex : IsCoproduct A (J n) Syn
    isFrex =
      record { inl       = inl
             ; inr       = inr
             ; _[_,_]    = _[_,_]
             ; commute₁  = λ {_ _ X f g} → commute₁ {X = X} {f} {g}
             ; commute₂  = λ {_ _ X f g} → commute₂ {X = X} {f} {g}
             ; universal = λ {_ _ X f g h} → universal {X = X} {f} {g} {h}
             }

SynFrex : FreeExtension
SynFrex = record { _[_]        = Syn
                 ; _[_]-isFrex = isFrex
                 }


------------------------------------------------------------------------
-- Fixpoint combinators
------------------------------------------------------------------------

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

module Partiality-monad.Inductive.Fixpoints where

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

open import Bijection equality-with-J using (_↔_)
import Equivalence equality-with-J as Eq
open import Function-universe equality-with-J hiding (id; _∘_)
open import H-level equality-with-J
open import H-level.Closure equality-with-J
open import Monad equality-with-J hiding (sequence)
open import Univalence-axiom equality-with-J

open import Partiality-algebra using (Partiality-algebra)
import Partiality-algebra.Fixpoints as PAF
open import Partiality-algebra.Monotone
open import Partiality-algebra.Omega-continuous
open import Partiality-algebra.Pi as Pi hiding (at)
open import Partiality-monad.Inductive
open import Partiality-monad.Inductive.Monad
open import Partiality-monad.Inductive.Monotone using ([_⊥→_⊥]⊑)
open import Partiality-monad.Inductive.Omega-continuous

------------------------------------------------------------------------
-- The fixpoint combinator machinery instantiated with the partiality
-- monad

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

  open PAF.Fix {P = partiality-algebra A} public
  open [_⊥→_⊥]

  -- A variant of fix.

  fix′ : (A → A ⊥) → A ⊥
  fix′ f = fix (monotone-function (f ∗))

  -- This variant also produces a kind of least fixpoint.

  fix′-is-fixpoint-combinator :
    (f : A → A ⊥) →
    fix′ f ≡ fix′ f >>= f
  fix′-is-fixpoint-combinator f =
    fix′ f                               ≡⟨⟩
    fix (monotone-function (f ∗))        ≡⟨ fix-is-fixpoint-combinator (f ∗) ⟩∎
    fix (monotone-function (f ∗)) >>= f  ∎

  fix′-is-least :
    (f : A → A ⊥) →
    (x : A ⊥) → x >>= f ⊑ x → fix′ f ⊑ x
  fix′-is-least f = fix-is-least (monotone-function (f ∗))

  -- Taking steps that are "twice as large" does not affect the end
  -- result.

  fix′-∘ : (f : A → A ⊥) → fix′ (λ x → f x >>= f) ≡ fix′ f
  fix′-∘ f =
    fix′ (λ x → f x >>= f)                                    ≡⟨⟩

    fix (monotone-function ((λ x → f x >>= f) ∗))             ≡⟨ cong fix (_↔_.to equality-characterisation-monotone λ (x : A ⊥) →

        (x >>= λ y → f y >>= f)                                    ≡⟨ associativity x f f ⟩∎
        x >>= f >>= f                                              ∎) ⟩

    fix (monotone-function (f ∗) ∘⊑ monotone-function (f ∗))  ≡⟨ fix-∘ (monotone-function (f ∗)) ⟩∎

    fix′ f                                                    ∎

  -- Unary Scott induction for fix′.

  fix′-induction₁ :
    ∀ {p}
    (P : A ⊥ → Type p)
    (P⊥ : P never)
    (P⨆ : ∀ s → (∀ n → P (s [ n ])) → P (⨆ s))
    (f : A → A ⊥) →
    (∀ x → P x → P (x >>= f)) →
    P (fix′ f)
  fix′-induction₁ P P⊥ P⨆ f =
    fix-induction₁ P P⊥ P⨆ ([_⊥→_⊥].monotone-function (f ∗))

-- N-ary Scott induction.

module _ {a p} n
  (As : Fin n → Type a)
  (P : (∀ i → As i ⊥) → Type p)
  (P⊥ : P (λ _ → never))
  (P⨆ : ∀ ss → (∀ n → P (λ i → ss i [ n ])) → P (⨆ ∘ ss))
  where

  open PAF.N-ary n As (λ i → partiality-algebra (As i)) P P⊥ P⨆ public

  fix′-induction :
    (fs : ∀ i → As i → As i ⊥) →
    (∀ xs → P xs → P (λ i → xs i >>= fs i)) →
    P (fix′ ∘ fs)
  fix′-induction fs =
    fix-induction (λ i → [_⊥→_⊥].monotone-function (fs i ∗))

------------------------------------------------------------------------
-- The fixpoint combinator machinery instantiated with dependent
-- functions to the partiality monad

module _ {a b} (A : Type a) (B : A → Type b) where

  -- Partial function transformers.

  Trans : Type (a ⊔ b)
  Trans = ((x : A) → B x ⊥) → ((x : A) → B x ⊥)

  -- A partiality algebra.

  Pi : Partiality-algebra (a ⊔ b) (a ⊔ b) ((x : A) → B x)
  Pi = Π A (partiality-algebra ∘ B)

  -- Monotone partial function transformers.

  Trans-⊑ : Type (a ⊔ b)
  Trans-⊑ = [ Pi ⟶ Pi ]⊑

  private

    sanity-check : Trans-⊑ → Trans
    sanity-check = [_⟶_]⊑.function

  -- Partial function transformers that are ω-continuous.

  Trans-ω : Type (a ⊔ b)
  Trans-ω = [ Pi ⟶ Pi ]

module _ {a b} {A : Type a} {B : A → Type b} where

  module Trans-⊑ = [_⟶_]⊑ {P₁ = Pi A B} {P₂ = Pi A B}
  module Trans-ω = [_⟶_]  {P₁ = Pi A B} {P₂ = Pi A B}

  private
    module F = PAF.Fix {P = Pi A B}

  -- Applies an increasing sequence of functions to a value.

  at :
    (∃ λ (f : ℕ → (x : A) → B x ⊥) → ∀ n x → f n x ⊑ f (suc n) x) →
    (x : A) → ∃ λ (f : ℕ → B x ⊥) → ∀ n → f n ⊑ f (suc n)
  at = Pi.at (partiality-algebra ∘ B)

  -- Repeated application of a monotone partial function transformer
  -- to the least element.

  app→ : Trans-⊑ A B → ℕ → ((x : A) → B x ⊥)
  app→ = F.app

  -- The increasing sequence fix→-sequence f consists of the elements
  -- const never, Trans-⊑.function f (const never), and so on. This
  -- sequence is used in the implementation of fix→.

  fix→-sequence : Trans-⊑ A B →
                  Partiality-algebra.Increasing-sequence (Pi A B)
  fix→-sequence = F.fix-sequence

  -- A fixpoint combinator.

  fix→ : Trans-⊑ A B → ((x : A) → B x ⊥)
  fix→ = F.fix

  -- The fixpoint combinator produces fixpoints for ω-continuous
  -- arguments.

  fix→-is-fixpoint-combinator :
    (f : Trans-ω A B) →
    fix→ (Trans-ω.monotone-function f) ≡
    Trans-ω.function f (fix→ (Trans-ω.monotone-function f))
  fix→-is-fixpoint-combinator = F.fix-is-fixpoint-combinator

  -- The result of the fixpoint combinator is pointwise smaller than
  -- or equal to every "pointwise post-fixpoint".

  fix→-is-least :
    (f : Trans-⊑ A B) →
    ∀ g → (∀ x → Trans-⊑.function f g x ⊑ g x) →
      ∀ x → fix→ f x ⊑ g x
  fix→-is-least = F.fix-is-least

  -- Taking steps that are "twice as large" does not affect the end
  -- result.

  fix→-∘ : (f : Trans-⊑ A B) → fix→ (f ∘⊑ f) ≡ fix→ f
  fix→-∘ = F.fix-∘

-- N-ary Scott induction.

module _
  {a b p} n
  (As : Fin n → Type a)
  (Bs : (i : Fin n) → As i → Type b)
  (P : (∀ i → (x : As i) → Bs i x ⊥) → Type p)
  (P⊥ : P (λ _ _ → never))
  (P⨆ : (ss : ∀ i (x : As i) → Increasing-sequence (Bs i x)) →
        (∀ n → P (λ i xs → ss i xs [ n ])) →
        P (λ i xs → ⨆ (ss i xs)))
  (fs : ∀ i → Trans-⊑ (As i) (Bs i)) where

  private
    module N = PAF.N-ary n
                 (λ i → (x : As i) → Bs i x)
                 (λ i → Π (As i) (partiality-algebra ∘ Bs i))
                 P
                 P⊥
                 (λ _ → P⨆ _)
                 fs

  -- Generalised.

  fix→-induction′ :
    (∀ n → P (λ i → app→ (fs i) n) →
           P (λ i → app→ (fs i) (suc n))) →
    P (λ i → fix→ (fs i))
  fix→-induction′ = N.fix-induction′

  -- Basic.

  fix→-induction :
    ((gs : ∀ i (x : As i) → Bs i x ⊥) →
     P gs → P (λ i → Trans-⊑.function (fs i) (gs i))) →
    P (λ i → fix→ (fs i))
  fix→-induction = N.fix-induction

-- Unary Scott induction.

fix→-induction₁ :
  ∀ {a b p} {A : Type a} {B : A → Type b}
  (P : ((x : A) → B x ⊥) → Type p) →
  P (λ _ → never) →
  ((s : (x : A) → Increasing-sequence (B x)) →
   (∀ n → P (λ x → s x [ n ])) →
   P (λ x → ⨆ (s x))) →
  (f : Trans-⊑ A B) →
  (∀ g → P g → P (Trans-⊑.function f g)) →
  P (fix→ f)
fix→-induction₁ P P⊥ P⨆ = PAF.fix-induction₁ P P⊥ (λ _ → P⨆ _)

------------------------------------------------------------------------
-- Some combinators that can be used to construct ω-continuous partial
-- function transformers, for use with fix→

-- A type used by these combinators.

record Partial
         {a b c}
         (A : Type a) (B : A → Type b) (C : Type c) :
         Type (a ⊔ b ⊔ lsuc c) where
  field
    -- A function that is allowed to make "recursive calls" of type
    -- (x : A) → B x ⊥.

    function : ((x : A) → B x ⊥) → C ⊥

    -- The function must be monotone.

    monotone : ∀ {rec₁ rec₂} →
               (∀ x → rec₁ x ⊑ rec₂ x) →
               function rec₁ ⊑ function rec₂

  -- The function can be turned into an increasing sequence.

  sequence : ((x : A) → Increasing-sequence (B x)) →
             Increasing-sequence C
  sequence recs =
      (λ n → function (λ x → recs x [ n ]))
    , (λ n → monotone (λ x → increasing (recs x) n))

  field
    -- The function must be ω-continuous in the following sense.

    ω-continuous : (recs : (x : A) → Increasing-sequence (B x)) →
                   function (⨆ ∘ recs) ≡ ⨆ (sequence recs)

-- The first two arguments of Partial specify the domain and codomain
-- of "recursive calls".

rec : ∀ {a b} {A : Type a} {B : A → Type b} →
      (x : A) → Partial A B (B x)
rec x = record
  { function     = _$ x
  ; monotone     = _$ x
  ; ω-continuous = λ _ → refl
  }

-- Turns certain Partial-valued functions into monotone partial
-- function transformers.

transformer : ∀ {a b} {A : Type a} {B : A → Type b} →
              ((x : A) → Partial A B (B x)) → Trans-⊑ A B
transformer f = record
  { function = λ g     x → function (f x) g
  ; monotone = λ g₁⊑g₂ x → monotone (f x) g₁⊑g₂
  }
  where
  open Partial

-- Turns certain Partial-valued functions into ω-continuous partial
-- function transformers.

transformer-ω : ∀ {a b} {A : Type a} {B : A → Type b} →
                ((x : A) → Partial A B (B x)) → Trans-ω A B
transformer-ω f = record
  { monotone-function = transformer f
  ; ω-continuous      = λ _ → ⟨ext⟩ λ x → ω-continuous (f x) _
  }
  where
  open Partial

-- A fixpoint combinator.

fixP : ∀ {a b} {A : Type a} {B : A → Type b} →
       ((x : A) → Partial A B (B x)) → ((x : A) → B x ⊥)
fixP {A = A} {B} =
  ((x : A) → Partial A B (B x))  ↝⟨ transformer ⟩
  Trans-⊑ A B                    ↝⟨ fix→ ⟩□
  ((x : A) → B x ⊥)              □

-- The fixpoint combinator produces fixpoints.

fixP-is-fixpoint-combinator :
  ∀ {a b} {A : Type a} {B : A → Type b} →
  (f : (x : A) → Partial A B (B x)) →
  fixP f ≡ flip (Partial.function ∘ f) (fixP f)
fixP-is-fixpoint-combinator =
  fix→-is-fixpoint-combinator ∘ transformer-ω

-- The result of the fixpoint combinator is pointwise smaller than
-- or equal to every "pointwise post-fixpoint".

fixP-is-least :
  ∀ {a b} {A : Type a} {B : A → Type b}
  (f : (x : A) → Partial A B (B x)) →
  ∀ g → (∀ x → Partial.function (f x) g ⊑ g x) →
    ∀ x → fixP f x ⊑ g x
fixP-is-least = fix→-is-least ∘ transformer

-- Composition of certain Partial-valued functions.

infixr 40 _∘P_

_∘P_ :
  ∀ {a b} {A : Type a} {B : A → Type b} →
  ((x : A) → Partial A B (B x)) →
  ((x : A) → Partial A B (B x)) →
  ((x : A) → Partial A B (B x))
Partial.function     ((f ∘P g) x) = λ h →
                                      Partial.function (f x) λ y →
                                      Partial.function (g y) h
Partial.monotone     ((f ∘P g) x) = λ hyp →
                                      Partial.monotone (f x) λ y →
                                      Partial.monotone (g y) hyp
Partial.ω-continuous ((f ∘P g) x) = λ recs →
  function (f x) (λ y → function (g y) (⨆ ∘ recs))  ≡⟨ cong (function (f x)) (⟨ext⟩ λ y → ω-continuous (g y) recs) ⟩
  function (f x) (λ y → ⨆ (sequence (g y) recs))    ≡⟨ ω-continuous (f x) (λ y → sequence (g y) recs) ⟩∎
  ⨆ (sequence (f x) λ y → sequence (g y) recs)      ∎
  where
  open Partial

-- Taking steps that are "twice as large" does not affect the end
-- result.

fixP-∘ : ∀ {a b} {A : Type a} {B : A → Type b}
         (f : (x : A) → Partial A B (B x)) →
         fixP (f ∘P f) ≡ fixP f
fixP-∘ f =
  fix→ (transformer (f ∘P f))            ≡⟨⟩
  fix→ (transformer f ∘⊑ transformer f)  ≡⟨ fix→-∘ (transformer f) ⟩∎
  fix→ (transformer f)                   ∎

-- N-ary Scott induction.

module _
  {a b p} n
  (As : Fin n → Type a)
  (Bs : (i : Fin n) → As i → Type b)
  (P : (∀ i (x : As i) → Bs i x ⊥) → Type p)
  (P⊥ : P (λ _ _ → never))
  (P⨆ : (ss : ∀ i (x : As i) → Increasing-sequence (Bs i x)) →
        (∀ n → P (λ i xs → ss i xs [ n ])) →
        P (λ i xs → ⨆ (ss i xs)))
  (fs : ∀ i (x : As i) → Partial (As i) (Bs i) (Bs i x)) where

  -- Generalised.

  fixP-induction′ :
    (∀ n → P (λ i → app→ (transformer (fs i)) n) →
           P (λ i → app→ (transformer (fs i)) (suc n))) →
    P (λ i → fixP (fs i))
  fixP-induction′ =
    fix→-induction′ n As Bs P P⊥ P⨆ (transformer ∘ fs)

  -- Basic.

  fixP-induction :
    ((gs : ∀ i (x : As i) → Bs i x ⊥) →
     P gs → P (λ i xs → Partial.function (fs i xs) (gs i))) →
    P (λ i → fixP (fs i))
  fixP-induction =
    fix→-induction n As Bs P P⊥ P⨆ (transformer ∘ fs)

-- Unary Scott induction.

fixP-induction₁ :
  ∀ {a b p} {A : Type a} {B : A → Type b}
  (P : ((x : A) → B x ⊥) → Type p) →
  P (λ _ → never) →
  ((s : (x : A) → Increasing-sequence (B x)) →
   (∀ n → P (λ x → s x [ n ])) →
   P (λ x → ⨆ (s x))) →
  (f : (x : A) → Partial A B (B x)) →
  (∀ g → P g → P (λ x → Partial.function (f x) g)) →
  P (fixP f)
fixP-induction₁ P P⊥ P⨆ f =
  fix→-induction₁ P P⊥ P⨆ (transformer f)

-- Equality characterisation lemma for Partial.

equality-characterisation-Partial :
  ∀ {a b c} {A : Type a} {B : A → Type b} {C : Type c}
    {f g : Partial A B C} →
  (∀ rec → Partial.function f rec ≡ Partial.function g rec) ↔
  f ≡ g
equality-characterisation-Partial {f = f} {g} =
  (∀ rec → function f rec ≡ function g rec)  ↔⟨ Eq.extensionality-isomorphism bad-ext ⟩
  function f ≡ function g                    ↝⟨ ignore-propositional-component
                                                  (Σ-closure 1
                                                     (implicit-Π-closure ext 1 λ _ →
                                                      implicit-Π-closure ext 1 λ _ →
                                                      Π-closure          ext 1 λ _ →
                                                      ⊑-propositional) λ _ →
                                                     Π-closure ext 1 λ _ →
                                                     ⊥-is-set) ⟩
  (function f , _) ≡ (function g , _)        ↔⟨ Eq.≃-≡ (Eq.↔⇒≃ lemma) ⟩□
  f ≡ g                                      □
  where
  open Partial

  lemma :
    Partial _ _ _
      ↔
    ∃ λ f →
    ∃ λ (_ : ∀ {rec₁ rec₂} → (∀ x → rec₁ x ⊑ _) → f rec₁ ⊑ _) → _
  lemma = record
    { surjection = record
      { logical-equivalence = record
        { to   = λ x → function x , monotone x , ω-continuous x
        ; from = λ { (f , m , ω) → record
                      { function     = f
                      ; monotone     = λ {rec₁ rec₂} → m {rec₁ = rec₁} {rec₂ = rec₂}
                      ; ω-continuous = ω
                      } }
        }
      ; right-inverse-of = λ _ → refl
      }
    ; left-inverse-of = λ _ → refl
    }

-- "Non-recursive" computations can be converted into possibly
-- recursive ones.

non-recursive :
  ∀ {a b c} {A : Type a} {B : A → Type b} {C : Type c} →
  C ⊥ → Partial A B C
non-recursive x = record
  { function     = const x
  ; monotone     = const (x ■)
  ; ω-continuous = λ _ →
      x             ≡⟨ sym ⨆-const ⟩∎
      ⨆ (constˢ x)  ∎
  }

instance

  -- Partial A B is a monad.

  partial-raw-monad : ∀ {a b c} {A : Type a} {B : A → Type b} →
                      Raw-monad (Partial {c = c} A B)
  Raw-monad.return partial-raw-monad x   = non-recursive (return x)
  Raw-monad._>>=_  partial-raw-monad x f = record
    { function     = λ rec →
                       function x rec >>=′ λ y →
                       function (f y) rec
    ; monotone     = λ rec⊑rec →
                       monotone x rec⊑rec >>=-mono λ y →
                       monotone (f y) rec⊑rec
    ; ω-continuous = λ recs →
        (function x (⨆ ∘ recs) >>=′ λ y → function (f y) (⨆ ∘ recs))     ≡⟨ cong₂ _>>=′_ (ω-continuous x recs)
                                                                                (⟨ext⟩ λ y → ω-continuous (f y) recs) ⟩
        (⨆ (sequence x recs) >>=′ λ y → ⨆ (sequence (f y) recs))         ≡⟨ ⨆->>= ⟩

        ⨆ ( (λ n →
               function x (λ z → recs z [ n ]) >>=′ λ y →
               ⨆ (sequence (f y) recs))
          , _
          )                                                              ≡⟨ ⨆>>=⨆≡⨆>>= (sequence x recs)
                                                                                (λ y → sequence (f y) recs) ⟩∎
        ⨆ ( (λ n → function x (λ z → recs z [ n ]) >>=′ λ y →
                   function (f y) (λ z → recs z [ n ]))
          , _
          )                                                              ∎
    }
    where
    open Partial

  partial-monad : ∀ {a b c} {A : Type a} {B : A → Type b} →
                  Monad (Partial {c = c} A B)
  Monad.raw-monad partial-monad = partial-raw-monad

  Monad.left-identity partial-monad _ f =
    _↔_.to equality-characterisation-Partial
      (λ h → left-identity _ (λ y → Partial.function (f y) h))

  Monad.right-identity partial-monad _ =
    _↔_.to equality-characterisation-Partial
      (λ _ → right-identity _)

  Monad.associativity partial-monad x _ _ =
    _↔_.to equality-characterisation-Partial
      (λ h → associativity (Partial.function x h) _ _)


-- This is mostly based on the ``Dependently-typed Functional Programming''
-- lecture from Pierre-Evariste Dagand (17--18, MPRI).
-- ref: https://gitlab.inria.fr/fpottier/mpri-2.4-public/

module lambda.stlc where

open import Relation.Binary.PropositionalEquality
open import Function hiding (id)
open import Data.Product
open import Agda.Builtin.Unit

open import lambda.stack

infixr 50 _⇒_

data type : Set where
  unit : type
  _⇒_ : type → type → type

context : Set
context = stack type

data _⊢_ (Γ : context) : type → Set where
  lam : ∀ {A B} → Γ ▸ A ⊢ B → Γ ⊢ A ⇒ B
  var : ∀ {A} → A ∈ Γ → Γ ⊢ A
  app : ∀ {A B} → Γ ⊢ A ⇒ B → Γ ⊢ A → Γ ⊢ B
  tt : Γ ⊢ unit

module norm where

  infix 30 _⊢ⁿ_
  infix 30 _⊢ᵉ_
  infix 40 _⟶_
  infix 45 _⟦⇒⟧_
  infix 50 _⟦×⟧_

  data _⊢ᵉ_ (Γ : context) : type → Set
  data _⊢ⁿ_ (Γ : context) : type → Set

  data _⊢ⁿ_ (Γ : context) where
    lam : ∀ {A B} → Γ ▸ A ⊢ⁿ B → Γ ⊢ⁿ A ⇒ B
    tt : Γ ⊢ⁿ unit
    ground : Γ ⊢ᵉ unit → Γ ⊢ⁿ unit

  data _⊢ᵉ_ (Γ : context) where
    var : ∀ {A} → A ∈ Γ → Γ ⊢ᵉ A
    app : ∀ {A B} → Γ ⊢ᵉ A ⇒ B → Γ ⊢ⁿ A → Γ ⊢ᵉ B

  ⌊_⌋ⁿ : ∀ {Γ A} → Γ ⊢ⁿ A → Γ ⊢ A
  ⌊_⌋ᵉ : ∀ {Γ A} → Γ ⊢ᵉ A → Γ ⊢ A

  ⌊ lam x ⌋ⁿ = lam ⌊ x ⌋ⁿ
  ⌊ tt ⌋ⁿ = tt
  ⌊ ground x ⌋ⁿ = ⌊ x ⌋ᵉ

  ⌊ var x ⌋ᵉ = var x
  ⌊ app x y ⌋ᵉ = app ⌊ x ⌋ᵉ ⌊ y ⌋ⁿ

  record sem : Set₁ where
    field
      _⊢ : context → Set
      ren : ∀ {Γ Δ} → Γ ⊇ Δ → Δ ⊢ → Γ ⊢

  _⟶_ : (A B : sem) → Set
  A ⟶ B = ∀ {Γ} → Γ ⊢A → Γ ⊢B
    where open sem A renaming (_⊢ to _⊢A)
          open sem B renaming (_⊢ to _⊢B)

  renameⁿ : ∀ {Γ Δ T} → Γ ⊇ Δ → Δ ⊢ⁿ T → Γ ⊢ⁿ T
  renameᵉ : ∀ {Γ Δ T} → Γ ⊇ Δ → Δ ⊢ᵉ T → Γ ⊢ᵉ T

  renameⁿ wk (lam b)       = lam (renameⁿ (keep wk) b)
  renameⁿ wk (ground grnd) = ground (renameᵉ wk grnd)
  renameⁿ wk tt            = tt

  renameᵉ wk (var v)       = var (shift wk v)
  renameᵉ wk (app f s)     = app (renameᵉ wk f) (renameⁿ wk s)

  renameⁿ-id : ∀ {Γ T} → (x : Γ ⊢ⁿ T) → renameⁿ id x ≡ x
  renameᵉ-id : ∀ {Γ T} → (x : Γ ⊢ᵉ T) → renameᵉ id x ≡ x

  renameⁿ-id (lam x) = cong lam (renameⁿ-id x)
  renameⁿ-id tt = refl
  renameⁿ-id (ground x) = cong ground (renameᵉ-id x)

  renameᵉ-id (var x) = cong var (shift-id x)
  renameᵉ-id (app x y) = cong₂ app (renameᵉ-id x) (renameⁿ-id y)

  semⁿ : type → sem
  semⁿ T = record {
    _⊢ = λ Γ → Γ ⊢ⁿ T ;
    ren = renameⁿ }

  semᵉ : type → sem
  semᵉ T = record {
    _⊢ = λ Γ → Γ ⊢ᵉ T ;
    ren = renameᵉ }

  ⟦unit⟧ : sem
  ⟦unit⟧ = semⁿ unit

  ⟦tt⟧ : ∀ {P} → P ⟶ ⟦unit⟧
  ⟦tt⟧ ρ = tt

  _⟦⇒⟧_ : sem → sem → sem
  P ⟦⇒⟧ Q = record {
    _⊢ = λ Γ → ∀ {Δ} → Δ ⊇ Γ → Δ ⊢P → Δ ⊢Q ;
    ren = λ wk₁ k wk₂ → k (wk₂ ∘⊇ wk₁) }
    where open sem P renaming (_⊢ to _⊢P)
          open sem Q renaming (_⊢ to _⊢Q)

  _⟦×⟧_ : sem → sem → sem
  P ⟦×⟧ Q = record { _⊢ = λ Γ → (Γ ⊢P) × (Γ ⊢Q)
                   ; ren = λ { wk (x , y) → ( ren-P wk x , ren-Q wk y ) } }
    where open sem P renaming (_⊢ to _⊢P ; ren to ren-P)
          open sem Q renaming (_⊢ to _⊢Q ; ren to ren-Q)

  ⟦lam⟧ : ∀ {P Q R} → P ⟦×⟧ Q ⟶ R → P ⟶ Q ⟦⇒⟧ R
  ⟦lam⟧ {P} η p = λ wk q → η (ren-P wk p , q)
    where open sem P renaming (ren to ren-P)

  ⟦app⟧ : ∀ {P Q R} → P ⟶ Q ⟦⇒⟧ R → P ⟶ Q → P ⟶ R
  ⟦app⟧ η μ = λ px → η px id (μ px)

  ⟦_⟧ : type → sem
  ⟦ unit ⟧  = ⟦unit⟧
  ⟦ S ⇒ T ⟧ = ⟦ S ⟧ ⟦⇒⟧ ⟦ T ⟧

  ⊤̂ : sem
  ⊤̂ = record { _⊢ = λ _ → ⊤
             ; ren = λ _ _ → tt }

  ⟦_⟧c : (Γ : context) → sem
  ⟦ ε ⟧c     = ⊤̂
  ⟦ Γ ▸ T ⟧c = ⟦ Γ ⟧c ⟦×⟧ ⟦ T ⟧

  _⟦⊢⟧_ : context → type → Set
  Γ ⟦⊢⟧ T = ⟦ Γ ⟧c ⟶ ⟦ T ⟧

  lookup : ∀ {Γ T} → T ∈ Γ → Γ ⟦⊢⟧ T
  lookup here (_ , v)      = v
  lookup (there x) (γ , _) = lookup x γ

  eval : ∀ {Γ T} → Γ ⊢ T → Γ ⟦⊢⟧ T
  eval {Γ} {A ⇒ B} (lam x) = ⟦lam⟧ {⟦ Γ ⟧c} {⟦ A ⟧} {⟦ B ⟧} (eval x)
  eval (var x) = lookup x
  eval {Γ} {B} (app {A} x y) = ⟦app⟧ {⟦ Γ ⟧c} {⟦ A ⟧} {⟦ B ⟧} (eval x) (eval y)
  eval {Γ} tt = ⟦tt⟧ {⟦ Γ ⟧c}

  [_]⊢_ : context → type → Set
  [ Γ ]⊢ T = Γ ⊢⟦T⟧
    where open sem ⟦ T ⟧ renaming (_⊢ to _⊢⟦T⟧)

  [_]⊢c_ : context → context → Set
  [ Γ ]⊢c Δ = Γ ⊢⟦Δ⟧c
    where open sem ⟦ Δ ⟧c renaming (_⊢ to _⊢⟦Δ⟧c)

  reify : ∀ {T Γ} → [ Γ ]⊢ T  → Γ ⊢ⁿ T
  reflect : ∀ {Γ} → (T : type) → Γ ⊢ᵉ T → [ Γ ]⊢ T

  reify {unit} v = v
  reify {A ⇒ B} x = lam (reify (x (skip id) (reflect A (var here))))

  reflect unit x = ground x
  reflect (A ⇒ B) x = λ w s → reflect B (app (ren w x) (reify s))
    where open sem (semᵉ (A ⇒ B))

  reify-id : ∀ {Γ T} → Γ ⟦⊢⟧ T → Γ ⊢ⁿ T
  reify-id {Γ} x = reify (x (id-c Γ))
    where
      open sem
      id-c : ∀ Γ → [ Γ ]⊢c Γ
      id-c ε = tt
      id-c (Γ ▸ T) = ren ⟦ Γ ⟧c (skip id) (id-c Γ) , reflect T (var here)

  norm : ∀ {Γ T} → Γ ⊢ T → Γ ⊢ⁿ T
  norm = reify-id ∘ eval


module proofs where

open import univ
open import cwf
open import Base
open import Nat
open import help

{-
lem-id∘ : {Γ Δ : Con}(σ : Γ ─→ Δ) -> id ∘ σ == σ
lem-id∘ (el < σ , pσ >) = eq \x -> ref

lem-∘id : {Γ Δ : Con}(σ : Γ ─→ Δ) -> σ ∘ id == σ
lem-∘id (el < σ , pσ >) = eq \x -> ref

lem-∘assoc : {Γ Δ Θ Ξ : Con}(σ : Θ ─→ Ξ)(δ : Δ ─→ Θ)(θ : Γ ─→ Δ) ->
             (σ ∘ δ) ∘ θ == σ ∘ (δ ∘ θ)
lem-∘assoc (el < σ , pσ >) (el < δ , pδ >) (el < θ , pθ >) = eq \x -> ref
-}

lem-/∘ : {Γ Δ Θ : Con}(A : Type Γ)(σ : Δ ─→ Γ)(δ : Θ ─→ Δ) ->
         A / σ ∘ δ =Ty A / σ / δ
lem-/∘ A (el < _ , _ >) (el < _ , _ >) = eqTy \x -> refS

{-
lem-//id : {Γ : Con}{A : Type Γ}{u : Elem Γ A} -> u // id =El castElem lem-/id u
lem-//id {Γ}{A}{elem (el < u , pu >)} = eqEl (eq prf)
  where
    prf : (x : El Γ) -> _
    prf x =
      chain> u x
         === _ << u (refS << x) by pu (sym (ref<< x))
         === _ << u (refS << x) by pfi _ _ _
      where open module C11 = Chain _==_ (ref {_}) (trans {_})

lem-//∘ : {Γ Δ Θ : Con}{A : Type Γ}(u : Elem Γ A)(σ : Δ ─→ Γ)(δ : Θ ─→ Δ) ->
          u // σ ∘ δ =El castElem (lem-/∘ A σ δ) (u // σ // δ)
lem-//∘ {Γ}{Δ}{Θ} (elem (el < u , pu >)) σ'@(el < σ , _ >) δ'@(el < δ , _ >) = eqEl (eq prf)
  where
    prf : (x : El Θ) -> _
    prf x =
      chain> u (σ (δ x))
         === _ << u (σ (δ (refS << x))) by pu (p─→ σ' (p─→ δ' (sym (ref<< x))))
         === _ << u (σ (δ (refS << x))) by pfi _ _ _
      where open module C12 = Chain _==_ (ref {_}) (trans {_})

lem-wk∘σ,,u : {Γ Δ : Con}{A : Type Γ}(σ : Δ ─→ Γ)(u : Elem Δ (A / σ)) ->
              wk ∘ (σ ,, u) == σ
lem-wk∘σ,,u (el < σ , pσ >) (elem (el < u , pu >)) = eq \x -> ref

lem-/wk∘σ,,u : {Γ Δ : Con}(A : Type Γ)(σ : Δ ─→ Γ)(u : Elem Δ (A / σ)) ->
               A / wk / (σ ,, u) =Ty A / σ
lem-/wk∘σ,,u A (el < σ , pσ >) (elem (el < u , pu >)) = eqTy \x -> refS

lem-vz/σ,,u : {Γ Δ : Con}{A : Type Γ}(σ : Δ ─→ Γ)(u : Elem Δ (A / σ)) ->
              vz // (σ ,, u) =El castElem (lem-/wk∘σ,,u A σ u) u
lem-vz/σ,,u (el < σ , pσ >) (elem (el < u , pu >)) = eqEl (eq \x -> prf x)
  where
    prf : (x : El _) -> u x == _ << u (refS << x)
    prf x =
      chain> u x
         === _ << u (refS << x) by pu (sym (ref<< x))
         === _ << u (refS << x) by pfi _ _ _
      where open module C15 = Chain _==_ (ref {_}) (trans {_})

lem-σ,,u∘ : {Γ Δ Θ : Con}{A : Type Γ}
            (σ : Δ ─→ Γ)(u : Elem Δ (A / σ))(δ : Θ ─→ Δ) ->
            (σ ,, u) ∘ δ == (σ ∘ δ ,, castElem (lem-/∘ A σ δ) (u // δ))
lem-σ,,u∘ (el < σ , _ >) (elem (el < u , pu >)) δ'@(el < δ , _ >) =
  eq \x -> eq < ref , prf x >
  where
    prf : (x : El _) -> u (δ x) == _ << _ << u (δ (refS << x))
    prf x =
      chain> u (δ x)
         === _ << u (δ (refS << x)) by pu (p─→ δ' (sym (ref<< x)))
         === _ << _ << u (δ (refS << x)) by sym (casttrans _ _ _ _)
      where open module C15 = Chain _==_ (ref {_}) (trans {_})

lem-wk,,vz : {Γ : Con}{A : Type Γ} -> (wk ,, vz) == id {Γ , A}
lem-wk,,vz {Γ}{A} = eq prf
  where
    prf : (x : El (Γ , A)) -> _
    prf (el < x , y >) = ref
-}

lem-Π/ : {Γ Δ : Con}{A : Type Γ}(B : Type (Γ , A))(σ : Δ ─→ Γ) ->
         Π A B / σ =Ty Π (A / σ) (B / (σ ∘ wk ,, castElem (lem-/∘ A σ wk) vz))
lem-Π/ B (el < σ , pσ >) =
  eqTy \x -> eqS < refS , (\y -> pFam B (eq < ref , prf x y >)) >
  where
    postulate prf : (x : El _)(y : El _) -> y == _ << _ << _ << _ << y
--     prf x y =
--       chain> y
--          === _ << _ << y           by sym (castref2 _ _ y)
--          === _ << _ << _ << y      by trans<< _ _ _
--          === _ << _ << _ << _ << y by trans<< _ _ _
--       where open module C16 = Chain _==_ (ref {_}) (trans {_})

{-
lem-β : {Γ : Con}{A : Type Γ}{B : Type (Γ , A)}
	(v : Elem (Γ , A) B)(u : Elem Γ A) ->
	(ƛ v) ∙ u =El v // [ u ]
lem-β {Γ}{A}{B} (elem (el < v , pv >)) (elem (el < u , pu >)) = eqEl (eq \x -> prf x _ _)
  where
    prf : (x : El Γ)(q : _ =S _)(p : _ =S _) ->
	  p << v (el < x , u x >) == v (el < x , q << u (refS << x) >)
    prf x q p =
      chain> p << v (el < x , u x >)
	 === p << q0 << v (el < x , q1 << u (refS << x) >)
	  by p<< p (pv (eqSnd (pu (sym (ref<< x)))))
	 === q2 << v (el < x , q1 << u (refS << x) >)
	  by sym (trans<< p q0 _)
	 === q2 << q3 << v (el < x , q << u (refS << x) >)
	  by p<< q2 (pv (eqSnd (pfi q1 q _)))
	 === v (el < x , q << u (refS << x) >)
	  by castref2 q2 q3 _
      where
	open module C17 = Chain _==_ (ref {_}) (trans {_})
	q0 = _
	q1 = _
	q2 = _
	q3 = _

-}


{-# OPTIONS --cubical --no-import-sorts #-}

module Hit where

-- open import Cubical.Core.Everything
open import Cubical.Foundations.Everything renaming (_⁻¹ to _⁻¹ᵖ)
-- open import Cubical.Foundations.Equiv.HalfAdjoint
-- open import Cubical.Data.Sigma.Properties

-- https://en.wikipedia.org/wiki/Inductive_type#Higher_inductive_types

-- private
--   ℓ : Level

_ : (A B : Type₀) → Type₁
_ = λ a b → a ≡ b

-- https://www.youtube.com/watch?v=AZ8wMIar-_c

-- List becomes a normal form of this
data FreeMonoid' (A : Type₀) : Type₀ where
  [_]' : A → FreeMonoid' A
  ε' : FreeMonoid' A
  _*'_ : FreeMonoid' A → FreeMonoid' A → FreeMonoid' A
  assoc' : ∀ x y z → x *' (y *' z) ≡ (x *' y) *' z

data List (A : Type) : Type where
  []  : List A
  _∷_ : A → List A → List A

-- debug fonts
-- see which fonts are "in effect" with
--  fc-match --verbose
-- https://wiki.archlinux.org/index.php/X_Logical_Font_Description
--   Two different font systems are used by X11:
--   - the older or core X Logical Font Description, XLFD,
--   - and the newer X FreeType, Xft, systems (see An Xft Tutorial for font names format).
-- https://keithp.com/~keithp/render/Xft.tutorial

-- λ-calculus
--   Cockx 2019 - Hack your type theory with rewrite rules
--     https://jesper.sikanda.be/posts/hack-your-type-theory.html
--   email thread from Georgi Lyubenov (24.03.20, 23:28 ff) about sized lambdas "How can I implement naive sized lambdas?"
--   email thread from Joey Eremondi (22.04.20, 06:07 ff) about variable binding "What do you use for variable binding in 2020?"

-- literate agda markdown -> latex
--   https://lists.chalmers.se/pipermail/agda/2019/011286.html
--   https://stackoverflow.com/questions/58339725/literate-agda-in-markdown-format-to-latex-via-pandoc
--   https://jesper.sikanda.be/posts/literate-agda.html
--   Jesper Cockx' mail from 09.07.19, 18:35

-- an example file/paper that shows how to use cubical:
--   /home/christianl/agda/cubical/Cubical/Papers/Synthetic.agda
--     Cubical synthetic homotopy theory
--     Mörtberg and Pujet, „Cubical synthetic homotopy theory“.
--     https://dl.acm.org/doi/abs/10.1145/3372885.3373825

-- the github repository recommends
--  Vezzosi 2019 - Cubical Agda: A Dependently Typed ProgrammingLanguage with Univalence and Higher Inductive Types
--  https://dl.acm.org/doi/pdf/10.1145/3341691

-- there is also the very comprehensive
--   https://arxiv.org/pdf/1911.00580.pdf
--   Martín Hötzel Escardó 2020 - Introduction to Univalent Foundations of Mathematics with Agda
--     clickable html version: https://www.cs.bham.ac.uk/~mhe/HoTT-UF-in-Agda-Lecture-Notes/HoTT-UF-Agda.html
--   https://www.cs.bham.ac.uk/~mhe/HoTT-UF-in-Agda-Lecture-Notes/HoTT-UF-Agda.html#sns
--      A structure identity principle for a standard notion of structure

-- some useful, recent stuff from the mailing list:
-- https://ncatlab.org/nlab/show/archimedean+field
--   An archimedean field is an ordered field in which every element is bounded above by a natural number.
-- Q: why refl {x} and refl {_} {_} {x} behave differently according to context
-- A: (U. Norell 01.05.20, 23:08)
--   pattern reflpv x = refl {x = x}
--   you are not allowed to pattern match on datatype parameters, since they are not stored in the constructor. In a right-hand side you can give the parameters to guide the type checker, but they are thrown out during elaboration.
-- Andreas Nuyts 26.04.20, 22:34
--   a case in a pattern matching definition gets a light grey background when it doesn't hold as a definitional equation, typically because it is a fallthrough case and further matching of the arguments is necessary to establish that none of the prior cases apply
-- Q: what do I assume/not assume when using --cubical?
-- A: (Andreas Abel 24.04.20, 15:04) That is a long story.  One thing is that in --cubical, the identity type is a primitive (not a data), and you cannot match on refl.
-- https://agda.readthedocs.io/en/v2.6.1/tools/command-line-options.html?highlight=infective#consistency-checking-of-options-used
--   An   infective option is an option that if used in one module, must be used in all modules that depend on this module.
--   A  coinfective option is an option that if used in one module, must be used in all modules that this module depends on.
-- (Orestis Melkonian 22.12.19, 21:11) inspect MAlonzo haskell code's variable names with https://github.com/agda/agda-ghc-names
-- (James Wood 22.12.19, 21:28)
--   Note that record field names can also overlap if they are applied directly to a record value.
--   The theory behind this is in bidirectional type checking, and how a term having its type checked (rather than inferred) may be ambiguous without the information from the type.
--   The same mechanism allows λ to be reused for dimension abstraction in cubical Agda (because it is a constructor).
--   In short, overloading is fine in the following cases:
--   - Everything being overloaded is a constructor, and there is an obvious type to check the resulting construction against.
--   - Everything being overloaded is a field, and there is an obvious record type that the field is destructing.
--   Outside of these cases, overloading fails.
--   You might notice also that the agda2-infer-type command will usually fail to infer the type of a construction of which the head is an overloaded constructor.
--   This is because Agda really wants to check the construction, rather than inferring a type for it.
--   It just happens that when the constructor is not overloaded, it's easier to make progress, because that gives just enough to check the construction against.
-- Q: Copattern match in emacs
-- A: (James Wood 05.11.19, 22:41) If I understand the question correctly, it's the usual C-c C-c for case-splitting, but on an empty hole and followed immediately by RET, rather than entering a variable name. The intermediate prompt is “pattern variables to case (empty for split on result)”. It's splitting on the result that you want.
-- Constructor names can overlap; the right one will be chosen on usage depending on the expected type.
--   Note that other kind of names cannot overlap (e.g. definition names, etc..).

-- the cubical std-lib is described in a blog post from Andreas Mörtberg
--   https://homotopytypetheory.org/2018/12/06/cubical-agda/
--   isomorphisms are equivalences (i.e. have contractible fibers)
--   Hedberg’s theorem (types with decidable equality are sets)
--   The main things that the CCHM cubical type theory extends dependent type theory with are:
--   1. An interval pretype
--   2. Kan operations
--   3. Glue types
--   4. Cubical identity types

open import Cubical.Structures.CommRing
open import Cubical.Relation.Nullary.Base -- ¬_
open import Cubical.Relation.Binary.Base
-- open import Cubical.Data.Prod.Base renaming (_×_ to _×'_)
-- NOTE: Cubical.Core.Sigma uses Agda.Builtin.Sigma
-- open import Agda.Builtin.Sigma renaming (_×_ to _×ᵇ_)
open import Cubical.Data.Sum.Base
open import Cubical.Data.Sigma.Base -- Σ-types are defined in Core/Primitives as they are needed for Glue types.

-- PathP : ∀ {ℓ} (A : I → Set ℓ) → A i0 → A i1 → Set ℓ
--
-- We have a variable name in `(λ i → A)` as a hint for case splitting.
--
-- Path : ∀ {ℓ} (A : Set ℓ) → A → A → Set ℓ
-- Path A a b = PathP (λ _ → A) a b
--
--  _≡_ : ∀ {ℓ} {A : Set ℓ} → A → A → Set ℓ
--  _≡_ {A = A} = PathP (λ i → A)

-- Cubical.Foundations.Id
--
-- transport : ∀ (B : A → Type ℓ') {x y : A} → x ≡ y → B x → B y
-- transport B {x} p b = J (λ y p → B y) b p
--
-- _⁻¹ : {x y : A} → x ≡ y → y ≡ x
-- _⁻¹ {x = x} p = J (λ z _ → z ≡ x) refl p
--
-- ap : ∀ {B : Type ℓ'} (f : A → B) → ∀ {x y : A} → x ≡ y → f x ≡ f y
-- ap f {x} = J (λ z _ → f x ≡ f z) refl
--
-- _∙_ : ∀ {x y z : A} → x ≡ y → y ≡ z → x ≡ z
-- _∙_ {x = x} p = J (λ y _ → x ≡ y) p

refl1 : ∀ {ℓ} {A : Type ℓ} {x : A} → Path A x x
refl1 {x = x} = λ i → x

refl2 : ∀ {ℓ} {A : Type ℓ} {x : A} → PathP (λ _ → A) x x
refl2 {x = x} = λ i → x

refl3 : ∀{ℓ} {A : Type ℓ} {x : A} → PathP (λ _ → A) x x
refl3 {x = x} = λ _ → x

sym1 : ∀ {ℓ} {A : Type ℓ} {x y : A} → x ≡ y → y ≡ x
sym1 p = λ i → p (~ i)

cong1 : ∀ {ℓ} {A : Type ℓ} {x y : A} {B : A → Type ℓ}
        (f : (a : A) → B a)
        (p : x ≡ y)
        --------------------------------------------
      → PathP (λ i → B (p i)) (f x) (f y)
cong1 f p i = f (p i)

cong2 : ∀ {ℓ} {A : Type ℓ} {x y : A} {B : A → Type ℓ}
        (f : (a : A) → B a)
        (p : PathP (λ i → A      )    x     y )
        ----------------------------------------------
      → (    PathP (λ i → B (p i)) (f x) (f y))
cong2 f p i = f (p i)

-- see "Propositional truncation" https://homotopytypetheory.org/2018/12/06/cubical-agda for
--  data ∥_∥ {ℓ} (A : Set ℓ) : Set ℓ where
--    ∣_∣ : A → ∥ A ∥
--    squash : ∀ (x y : ∥ A ∥) → x ≡ y
-- see https://planetmath.org/37propositionaltruncation

open import Cubical.HITs.PropositionalTruncation -- for `|_|` and `squash`

test1 : {Carrier : Type} -> ∃[ x ∈ Carrier ] x ≡ x → Carrier
test1 ∣ x , x≡x ∣ = x
-- Goal: Carrier
-- ———— Boundary ——————————————————————————————————————————————
-- i = i0 ⊢ test1 x
-- i = i1 ⊢ test1 y
-- ———— Constraints ———————————————————————————————————————————
-- test1 x = ?0 (i = i0) : Carrier
-- test1 y = ?0 (i = i1) : Carrier
test1 (squash x y i) = {! test1 x!}

test2 : {A : Type} → (x y : A) → x ≡ y → A
-- test2 x y x≡y = {! x≡y!}
test2 x y p = p i0

-- test4 : I → Type
-- test4 i = {!!}

test5a : {A : Type} {P : A → Type} → (x y : A) → x ≡ y → P x → P y
test5a {A} {P} x y p px = transport (λ i → P (p i)) px

test5b : {A : Type}
       → {P : A → Type}
       → (x y : A)
       → PathP (λ _ → A) x y -- x ≡ y
       → P x
       --------------------------
       → P y
test5b {A} {P} x y x≡y Px = transport {A = P x} Px≡Py Px where
  -- transport gives us `P x → P y`, but we need to feed it `P x ≡ P y`
  Px≡Py : P x ≡ P y
  Px≡Py = cong P x≡y
  -- this is almost what `isProp P` would give us
  --   isProp : ∀ {ℓ} → Type ℓ → Type ℓ
  --   isProp A = (x y : A) → x ≡ y
  -- but we need something like `∀(x y : A) → P x ≡ P y`
  --   well, no. `cong P x≡y` gives us `P x ≡ P y`
  -- it is different from non-cubical agda, where `x ≡ y` was sufficient, no matter what P is
  -- there, we could just pattern match on refl and "change" the goal to `P x` instead of `P y`
  -- well, in some sense we are already changing the goal as this approach could be "at the end" of a sequence of equational reasoning
  --   ... just without pattern matching

test5c : {A : Type}
       → {P : A → Type}
       → (x y : A)
       → PathP (λ _ → A) x y -- x ≡ y
       → P x
       --------------------------
       → P y
test5c {A} {P} x y x≡y Px = transport {A = P x} Px≡Py Px where
  -- once again, without `cong`
  Px≡Py : P x ≡ P y
  -- we need to build a path that is `P x` at i0 and `P y` at i1
  -- with `x≡y` we have given a path that is `x` at i0 and `y` at i1
  -- therefore `x≡y(i)` behaves in the correct way and we just plug this into `P`
  Px≡Py i = P (x≡y i)

-- the short version, collapsing all intermediates
test5f : ∀{ℓ ℓ'} {A : Type ℓ} {P : A → Type ℓ'} {x y : A} → (x ≡ y) → (P x) → P y
test5f {P = P} p px = transport (λ i → P (p i)) px

--primitive
--  transp : ∀ {ℓ} (A : (i : I) → Set (ℓ i)) (φ : I) (a : A i0) → A i1

transport1 : ∀{ℓ} {A B : Type ℓ} → A ≡ B → A → B
transport1 p a = transp (λ i → p i) i0 a

test5d : {A : Type} {P : A → Type} → (x y : A) → x ≡ y → P x → P y
test5d {A} {P} x y x≡y Px = transport (cong P x≡y) Px

module Utils where
  private
    variable
      ℓ ℓ' : Level
      A :                               Type ℓ
      B :      A                      → Type ℓ
      C : (a : A) →      B a          → Type ℓ
      D : (a : A) → (b : B a) → C a b → Type ℓ

  cong₁ : ∀ (f : (a : A) → B a)
       → {x : A} {y : A} (p : PathP (λ i → A) x y) -- (p : x ≡ y)
       ----------------------------------
       → PathP (λ i → B (p i)) (f x) (f y)
  cong₁ f p i = f (p i)
  {-# INLINE cong₁ #-}

  cong₂' : ∀ {F : (a : A) → (b : B a) → Type ℓ}
         → (f : (a : A) → (b : B a) → F a b)
         → {x : A    } {y : A    } (p : PathP (λ i → A            ) x y) -- (p : x ≡ y)
         → {u : B x  } {v : B y  } (q : PathP (λ i → B (p i)      ) u v) -- ? u ≡ v given x ≡ y ?
         ---------------------------------------------
         → PathP (λ i → F (p i) (q i)) (f x u) (f y v)
  cong₂' f p q i = f (p i) (q i)
  {-# INLINE cong₂' #-}

  cong₃ : ∀{F : (a : A) → (b : B a) → (c : C a b) → Type ℓ}
        →   (f : (a : A) → (b : B a) → (c : C a b) → F a b c)
        → {x : A    } {y : A    } (p : PathP (λ i → A            ) x y) -- (p : x ≡ y)
        → {u : B x  } {v : B y  } (q : PathP (λ i → B (p i)      ) u v) -- ? u ≡ v given x ≡ y ?
        → {m : C x u} {n : C y v} (r : PathP (λ i → C (p i) (q i)) m n) -- ? m ≡ n given x ≡ y and u ≡ v ?
        --------------------------------------------
        → PathP (λ i → F (p i) (q i) (r i)) (f x u m) (f y v n)
  cong₃ f p q r i = f (p i) (q i) (r i)
  {-# INLINE cong₃ #-}

  cong₄ : ∀{F : (a : A) → (b : B a) → (c : C a b) → (d : D a b c) → Type ℓ}
        →   (f : (a : A) → (b : B a) → (c : C a b) → (d : D a b c) → F a b c d)
        → {x : A      } {y : A      } (p : PathP (λ i → A                  ) x y) -- (p : x ≡ y)
        → {u : B x    } {v : B y    } (q : PathP (λ i → B (p i)            ) u v) -- ? u ≡ v given x ≡ y ?
        → {m : C x u  } {n : C y v  } (r : PathP (λ i → C (p i) (q i)      ) m n) -- ? m ≡ n given x ≡ y and u ≡ v ?
        → {k : D x u m} {l : D y v n} (s : PathP (λ i → D (p i) (q i) (r i)) k l) -- ? k ≡ l given x ≡ y and u ≡ v and m ≡ n?
        --------------------------------------------
        → PathP (λ i → F (p i) (q i) (r i) (s i)) (f x u m k) (f y v n l)
  cong₄ f p q r s i = f (p i) (q i) (r i) (s i)
  {-# INLINE cong₄ #-}

open Utils using (cong₃; cong₄)

-- slide 18 of http://www.cse.chalmers.se/~abela/esslli2016/talkESSLLI3.pdf
fullelim : (A : Type)
         → (C : (x y : A) → (p : x ≡ y) → Type)
         → (M N : A)
         → (P : M ≡ N)
         → (O : (z : A) → C z z refl)
         ------------------------
         → C M N P
fullelim A C M N P O = transport along (O M) where
  along : C M M refl ≡ C M N P
  -- the "obvious" idea would be to show equality on every argument
  -- and then use a brute force `cong₃` to apply these equalities
  along = cong₃ C (refl {x = M}) P refl≡P where
    -- the first two arguments are easy, and the hole of the third argument rewards us with a signature to implement:
    refl≡P : PathP (λ i → M ≡ (P i)) refl P
    -- here, we need to build a path that is refl on i0 and P on i1 just with the things we have given
    --    refl : ∀{ℓ} {A : Type ℓ} {x : A} x ≡ x
    --    refl {x = x} = λ _ → x
    --    _≡_ : ∀ {ℓ} {A : Set ℓ} → A → A → Set ℓ
    --    _≡_ {A = A} = PathP (λ i → A)
    -- therefore, we have
    --    refl : ∀{ℓ} {A : Type ℓ} {x : A} → PathP (λ i → A) M M
    --    refl {x = x} = λ _ → x
    --    P : PathP (λ i → A) M N
    -- Goal: M ≡ P i
    -- Goal: PathP (λ j → A) M (P i)
    refl≡P i = λ j → P (i ∧ j)
    -- I could not come up with the solution myself, but I was able to pick it off ...
    --   ... of `J` under "Kan operations" at https://homotopytypetheory.org/2018/12/06/cubical-agda/
    -- so, how does this work? These are the cases:
    --    i |  j | i∧j | P(i∧j) | λ j → P(i∧j) | λ i → λ j → P(i∧j)
    --   i0 | i0 |  i0  |  M : A | M≡M : M ≡ M   |  p : (M≡M) ≡ (M≡N)
    --   i0 | i1 |  i0  |  M : A |               |    which is "like"
    --   i1 | i0 |  i0  |  M : A | M≡N : M ≡ N   |  p :  refl ≡ P
    --   i1 | i1 |  i1  |  N : A |               |
    -- and indeed, they work out
    -- we could not write `refl ≡ P` but instead have `PathP _ refl P`

fullelim2 : (A : Type)
         → (C : (x y : A) → (p : x ≡ y) → Type)
         → (M N : A)
         → (P : M ≡ N)
         → (O : (z : A) → C z z refl)
         ------------------------
         → C M N P
-- we can unfold `transport` as in the blog post
--   transport : {A B : Type ℓ} → A ≡ B → A → B
--   transport p a = transp (λ i → p i) i0 a
fullelim2 A C M N P O = transp (λ i → along i) i0 (O M) where
  -- and we can directly "set up" the path without using `cong₃`
  along : C M M refl ≡ C M N P
  along i = C (refl {x = M} i) (P i) (λ j →  P (i ∧ j) )

--

-- see favonia: [Agda] Equational Reasoning
--   https://favonia.org/courses/hdtt2020/agda/agda-0305-eqreasoning.agda

-- Kraus 2013 - Generalizations of Hedberg’s Theorem
--   https://www.cs.bham.ac.uk/~mhe/papers/hedberg.pdf
--   Although the identity types in Martin-Löf type theory (MLTT) are defined byone constructor refl and by one eliminator J that matches the constructor, the statement that every identity type has at most one inhabitant is not provable [9].
--   Thus,uniqueness of identity proofs(UIP), or, equivalently, Streicher’s axiom K are principles that have to be assumed, and have often been assumed, as additional rules of MLTT.
--   ...
--   we  do  not  assume  the  principle  of  unique  identity  proofs.
--   However, certain types do satisfy it naturally, and such types are often called h-sets.
--   A sufficient condition for a type to be an h-set, given by Hedberg [8], isthat it has decidable propositional equality.
--   In Section 3, we analyze Hedberg’s original argument, which consists of two steps:
--   1.  A type X is an h-set iff for all x, y : X there is a constant map x = y → x = y.
--   2. If X has decidable equality then such constant endomaps exist
--   Decidable equality means that, for all x and y, we have (x = y) + (x ≠ y).
--   Thus, a natural weakening is ¬¬-separated equality, ¬¬(x = y) → x = y, which occurs often in constructive mathematics.
--   In this case we say that the type X is separated.
--   For example, going beyond MLTT, the reals and the Cantorspace in Bishop mathematics and topos theory are separated.
--   In MLTT, the Cantortype of functions from natural numbers to booleans is separated under the assumption of functional extensionality,
--     ∀ f g : X → Y, (∀ x : X, f x = g x) → f = g.
--   We observe that under functional extensionality, a separated type X is an h-set, because there is always a constant map x = y → x = y.
--   In order to obtain a further characterization of the notion of h-set, we consider truncations
--     (also known as bracket or squash types), written ‖X‖in accordance with recent HoTT notation.
--   The idea is to collapse all inhabitants of X so that ‖X‖ has at most one inhabitant.
--   We refer the reader to the technical development for a precise definition.
-- cites
--   Altenkirch 2012 - On h-Propositional Reflection and Hedberg’s Theorem
--   https://homotopytypetheory.org/2012/11/27/on-h-propositional-reflection-and-hedbergs-theorem/
-- Bracket-Type https://ncatlab.org/nlab/show/bracket+type
--   By contrast, in the paradigm that may be called propositions as some types, every proposition is a type, but not every type is a proposition.
--   The types which are propositions are generally those which “have at most one inhabitant” — in homotopy type theory this is called being of h-level 1 or being a (-1)-type.
--   ...
--   For A a type, the support of A denoted supp(A) or isInhab(A) or τ₋₁ A or ‖A‖₋₁ or ‖A‖ or, lastly, [A],
--     is the higher inductive type defined by the two constructors
--       a : A ⊢ isinhab(a) : supp(A)
--       x : supp(A), y : supp(A) ⊢ inpath(x,y) : (x = y),
--   where in the last sequent on the right we have the identity type.

-- https://github.com/agda/cubical/issues/286
-- It could also be a good idea to axiomatize the notion of a Caucy-complete Archimedean ordered field, as in Auke Booij's thesis, and show that Dedekind reals are an instance. The HoTT reals are then another instance. (Can cubical Agda handle the HoTT reals as a HIIT yet?)

-- https://www.cs.bham.ac.uk/~abb538/thesis-first_submission.pdf
-- Booij 2020 - Analysis in Univalent Type Theory

-- 4.1  Algebraic structure of numbers
--
-- Fields have the property that nonzero numbers have a multiplicative inverse, or more precisely, that
--   (∀ x : F) x ≠ 0 ⇒ (∃ y : F) x · y = 1.
--
-- Remark 4.1.1.
-- If we require the collection of numbers to form a set in the sense of Definition 2.5.4, and satisfy the ring axioms, then multiplicative inverses are unique, so that the above is equivalent to the proposition
--   (Π x : F) x ≠ 0 ⇒ (Σ y : F) x · y = 1.
--
-- Definition 4.1.2.
-- A classical field is a set F with points 0, 1 : F, operations +, · : F → F → F, which is a commutative ring with unit, such that
--   (∀ x : F) x ≠ 0 ⇒ (∃ y : F) x · y = 1.

private
  variable
    ℓ ℓ' ℓ'' : Level

module ClassicalFieldModule where -- NOTE: one might want to put this into another file to omit the name-clashing
  record IsClassicalField {F : Type ℓ}
                          (0f : F) (1f : F) (_+_ : F → F → F) (_·_ : F → F → F) (-_ : F → F) (_⁻¹ᶠ : (x : F) → {{¬(x ≡ 0f)}} → F) : Type ℓ where
    constructor isclassicalfield

    field
      isCommRing : IsCommRing 0f 1f _+_ _·_ -_
      ·-rinv : (x : F) → (p : ¬(x ≡ 0f)) → x · (_⁻¹ᶠ x {{p}}) ≡ 1f
      ·-linv : (x : F) → (p : ¬(x ≡ 0f)) → (_⁻¹ᶠ x {{p}}) · x ≡ 1f

    open IsCommRing {0r = 0f} {1r = 1f} isCommRing public

  record ClassicalField : Type (ℓ-suc ℓ) where
    field
      Carrier : Type ℓ
      0f   : Carrier
      1f   : Carrier
      _+_  : Carrier → Carrier → Carrier
      _·_  : Carrier → Carrier → Carrier
      -_   : Carrier → Carrier
      _⁻¹ᶠ : (x : Carrier) → {{¬(x ≡ 0f)}} → Carrier
      isClassicalField : IsClassicalField 0f 1f _+_ _·_ -_ _⁻¹ᶠ

    infix  9 _⁻¹ᶠ
    infix  8 -_
    infixl 7 _·_
    infixl 6 _+_

    open IsClassicalField isClassicalField public

-- Remark 4.1.3.
-- As in the classical case, by proving that additive and multiplicative inverses are unique, we also obtain the negation and division operations.
--
-- For the reals, the assumption x ≠ 0 does not give us any information allowing us to bound x away from 0, which we would like in order to compute multiplicative inverses.
-- Hence, we give a variation on the denition of fields in which the underlying set comes equipped with an apartness relation #, which satises x # y ⇒ x ≠ y, although the converse implication may not hold.
-- This apartness relation allows us to make appropriate error bounds and compute multiplicative inverses based on the assumption x # 0.
--
-- Definition 4.1.4.
-- - An apartness relation, denoted by #, is an irreflexive symmetric cotransitive relation.
-- - A strict partial order, denoted by <, is an irreflexive transitive cotransitive relation.

IsIrrefl : {ℓ ℓ' : Level} {A : Type ℓ} → (R : Rel A A ℓ') → Type (ℓ-max ℓ ℓ')
IsIrrefl {A = A} R = (a b : A) → R a b → ¬(R b a)

IsCotrans : {ℓ ℓ' : Level} {A : Type ℓ} → (R : Rel A A ℓ') → Type (ℓ-max ℓ ℓ')
IsCotrans {A = A} R = (a b : A) → R a b → (∀(x : A) → (R a x) ⊎ (R x b))

-- NOTE: these `IsX` definitions are in the style of the standard library and do not make `a` and `b` to be implicit arguments
--   when using them, we can just use `_` as in `isIrrefl _ _ a#b`

-- NOTE: module parameters "add" to the contained function's arguments
--   see https://agda.readthedocs.io/en/v2.6.0.1/language/module-system.html#parameterised-modules

-- IsApartnessRel : {ℓ ℓ' : Level} {A : Type ℓ} → (R : Rel A A ℓ') → Type (ℓ-max ℓ ℓ')
-- IsApartnessRel R = IsIrrefl R × BinaryRelation.isSym R × IsCotrans R

record IsApartnessRel {ℓ ℓ' : Level} {A : Type ℓ} (R : Rel A A ℓ') : Type (ℓ-max ℓ ℓ') where
  field
    isIrrefl  : IsIrrefl R
    isSym     : BinaryRelation.isSym R
    isCotrans : IsCotrans R

-- IsStrictPartialOrder : {ℓ ℓ' : Level} {A : Type ℓ} → (R : Rel A A ℓ') → Type (ℓ-max ℓ ℓ')
-- IsStrictPartialOrder R = IsIrrefl R × BinaryRelation.isTrans R × IsCotrans R

record IsStrictPartialOrder {ℓ ℓ' : Level} {A : Type ℓ} (R : Rel A A ℓ') : Type (ℓ-max ℓ ℓ') where
  field
    isIrrefl  : IsIrrefl R
    isTrans   : BinaryRelation.isTrans R
    isCotrans : IsCotrans R

-- Definition 4.1.5.
-- A constructive field is a set F with points 0, 1 : F, binary operations +, · : F → F → F, and a binary relation # such that
-- 1. (F, 0, 1, +, ·) is a commutative ring with unit;
-- 2. x : F has a multiplicative inverse iff x # 0;
-- 3. + is #-extensional, that is, for all w, x, y, z : F
--    w + x # y + z ⇒ w # y ∨ x # z.

{- NOTE: about naming in the standard library
with `isCommRing : IsCommRing 0f 1f _+_ _·_ -_` we import
  is-set        : (x y : Carrier) (x₁ y₁ : x ≡ y) → x₁ ≡ y₁ -- NOTE: this is imported from +-isAbGroup and explicitly hidden from ·-isMonoid
  isCommRing    : IsCommRing 0f 1f _+_ _·_ -_
  isRing        : IsRing 0f 1f _+_ _·_ -_
  +-isAbGroup   : IsAbGroup 0f _+_ -_
  +-assoc       : (x y z : Carrier) → x + (y + z) ≡ x + y + z
  +-identity    : (x : Carrier) → (x + 0f ≡ x) × (0f + x ≡ x)
  +-rid         : (x : Carrier) → x + 0f ≡ x
  +-lid         : (x : Carrier) → 0f + x ≡ x
  +-inv         : (x : Carrier) → (x + - x ≡ 0f) × (- x + x ≡ 0f)
  +-linv        : (x : Carrier) → - x + x ≡ 0f
  +-rinv        : (x : Carrier) → x + - x ≡ 0f
  +-comm        : (x y : Carrier) → x + y ≡ y + x
  +-isSemigroup : IsSemigroup _+_
  +-isMonoid    : IsMonoid 0f _+_
  +-isGroup     : IsGroup 0f _+_ -_
  ·-isSemigroup : IsSemigroup _·_
  ·-isMonoid    : IsMonoid 1f _·_
  ·-comm        : (x y : Carrier) → x · y ≡ y · x
  ·-assoc       : (x y z : Carrier) → x + (y + z) ≡ x + y + z
  ·-identity    : (x : Carrier) → (x · 1f ≡ x) × (1f · x ≡ x)
  ·-lid         : (x : Carrier) → 1f · x ≡ x
  ·-rid         : (x : Carrier) → x · 1f ≡ x
  dist          : (x y z : Carrier) → (x · (y + z) ≡ x · y + x · z) × ((x + y) · z ≡ x · z + y · z)
  ·-rdist-+     : (x y z : Carrier) → x · (y + z) ≡ x · y + x · z
  ·-ldist-+     : (x y z : Carrier) → (x + y) · z ≡ x · z + y · z
there is
  module GroupLemmas (G : Group {ℓ}) where
  record MonoidEquiv (M N : Monoid {ℓ}) : Type ℓ where
  module SemigroupΣTheory {ℓ} where
  module CommRingΣTheory {ℓ} where
  module MonoidΣTheory {ℓ} where
  module MonoidTheory {ℓ} (M' : Monoid {ℓ}) where
  module GroupΣTheory {ℓ} where
  module AbGroupΣTheory {ℓ} where
  module RingΣTheory {ℓ} where
  module Theory (R' : Ring {ℓ}) where
  module PosetReasoning (P : Poset ℓ₀ ℓ₁) where
there is a syntax for https://agda.readthedocs.io/en/latest/language/module-system.html?highlight=module#parameterised-modules
  module SortNat = Sort Nat leqNat
the non-cubical standard library has a lot of machinery that is missing in the cubical-stdlib
  module Function.Base where
    -- Binary application
    _⟨_⟩_ : A → (A → B → C) → B → C
    x ⟨ f ⟩ y = f x y
    -- Composition of a binary function with a unary function
    _on_ : (B → B → C) → (A → B) → (A → A → C)
    _*_ on f = λ x y → f x * f y
    -- Flipped application (aka pipe-forward)
    _|>_ : ∀ {A : Set a} {B : A → Set b} → (a : A) → (∀ a → B a) → B a
    -- Construct an element of the given type by instance search.
    it : {A : Set a} → {{A}} → A
    it {{x}} = x
  module Relation.Binary.Core where
    infix 4 _⇒_ _⇔_ _=[_]⇒_
    -- Implication/containment - could also be written _⊆_.
    -- and corresponding notion of equivalence
    _⇒_ : REL A B ℓ₁ → REL A B ℓ₂ → Set _
    P ⇒ Q = ∀ {x y} → P x y → Q x y
    _⇔_ : REL A B ℓ₁ → REL A B ℓ₂ → Set _
    P ⇔ Q = P ⇒ Q × Q ⇒ P
    -- Generalised implication - if P ≡ Q it can be read as "f preserves P".
    _=[_]⇒_ : Rel A ℓ₁ → (A → B) → Rel B ℓ₂ → Set _
    P =[ f ]⇒ Q = P ⇒ (Q on f)
    -- A synonym for _=[_]⇒_.
    _Preserves_⟶_ : (A → B) → Rel A ℓ₁ → Rel B ℓ₂ → Set _
    f Preserves P ⟶ Q = P =[ f ]⇒ Q
    -- A binary variant of _Preserves_⟶_.
    _Preserves₂_⟶_⟶_ : (A → B → C) → Rel A ℓ₁ → Rel B ℓ₂ → Rel C ℓ₃ → Set _
    _∙_ Preserves₂ P ⟶ Q ⟶ R = ∀ {x y u v} → P x y → Q u v → R (x ∙ u) (y ∙ v)
  module Algebra.Definitions
    Congruent₁ : Op₁ A → Set _
    Congruent₁ f = f Preserves _≈_ ⟶ _≈_
    Congruent₂ : Op₂ A → Set _
    Congruent₂ ∙ = ∙ Preserves₂ _≈_ ⟶ _≈_ ⟶ _≈_
    LeftCongruent : Op₂ A → Set _
    LeftCongruent _∙_ = ∀ {x} → (x ∙_) Preserves _≈_ ⟶ _≈_
    RightCongruent : Op₂ A → Set _
    RightCongruent _∙_ = ∀ {x} → (_∙ x) Preserves _≈_ ⟶ _≈_
the non-cubical standard library makes more use of unicode, e.g. in invˡ and invʳ
for the inverse, we have in the non-cubical standard library
  inverseˡ : LeftInverse ε _⁻¹ _∙_
  inverseˡ = proj₁ inverse
  inverseʳ : RightInverse ε _⁻¹ _∙_
  inverseʳ = proj₂ inverse
  uniqueˡ-⁻¹ : ∀ x y → (x ∙ y) ≈ ε → x ≈ (y ⁻¹)
  uniqueˡ-⁻¹ = Consequences.assoc+id+invʳ⇒invˡ-unique
                setoid ∙-cong assoc identity inverseʳ
  uniqueʳ-⁻¹ : ∀ x y → (x ∙ y) ≈ ε → y ≈ (x ⁻¹)
  uniqueʳ-⁻¹ = Consequences.assoc+id+invˡ⇒invʳ-unique
                setoid ∙-cong assoc identity inverseˡ
interestingly, this follows from just one field `inverse : Inverse ε _⁻¹ _∙_`
  there is `module Algebra.Consequences.Setoid` for such results ("consequences")
there are open pull requests which might have some more information about the "intended" style
  https://github.com/agda/cubical/pull/331
    Algebras and modules #331
  https://github.com/agda/cubical/pull/365
    Mayer-Vietoris and some cohomology groups #365
  https://github.com/agda/cubical/pull/325
    Denial field #325
    A denial field is a commutative ring where every non-zero element has an inverse. See #301 for a discussion of other notions of 'field' in constructive algebra.
    https://github.com/felixwellen/cubical/tree/denial-field
  https://github.com/agda/cubical/issues/301
    Add Field to Cubical.Structures #301
    In the style of Ring, etc. from #284. I think it would be good to have this so we could show ℚ is a field.
  https://github.com/agda/cubical/pull/284
    Newstructures #284
    Adding some new structures (semigroups, groups, abelian groups and rngs) and a new folder of Algebra, in order to produce results about these new Algebraic structures.
  https://github.com/agda/cubical/commit/8cf91aeb49ebf26d4cf4e6795e8b56041e28c7a1
    Rewrite Group, AbGroup, Ring and CommRing to be records instead of nested Sigma types
  https://github.com/agda/cubical/pull/324
    Ideals and quotient rings #324
    - define (left-, right- and two-sided) ideals
    - define quotient by a two-sided ideal
    - construct the ring structure on the quotient
-}

record IsConstructiveField {F : Type ℓ}
                           (0f : F) (1f : F) (_+_ : F → F → F) (_·_ : F → F → F) (-_ : F → F) (_#_ : Rel F F ℓ') (_⁻¹ᶠ : (x : F) → {{x # 0f}} → F) : Type (ℓ-max ℓ ℓ') where
  constructor isconstructivefield

  field
    isCommRing : IsCommRing 0f 1f _+_ _·_ -_
    ·-rinv     : (x : F) → (p : x # 0f) → x · (_⁻¹ᶠ x {{p}}) ≡ 1f
    ·-linv     : (x : F) → (p : x # 0f) → (_⁻¹ᶠ x {{p}}) · x ≡ 1f
    -- ·-inv      : (x : F) → (p : x # 0f) → x · (let instance p = p in (x ⁻¹ᶠ)) ≡ 1f -- this does also work
    -- ·-inv      : (x : F) → ({{p}} : x # 0f) → x · (x ⁻¹ᶠ) ≡ 1f -- this does not do the right thing
    -- ·-inv-back : (x : F) → ∃[ y ∈ F ] (x · y ≡ 1f) → x # 0f
    ·-inv-back : (x y : F) → (x · y ≡ 1f) → x # 0f × y # 0f
    +-#-extensional : (w x y z : F) → (w + x) # (y + z) → (w # y) ⊎ (x # z)
    isApartnessRel : IsApartnessRel _#_

  open IsCommRing {0r = 0f} {1r = 1f} isCommRing public
  open IsApartnessRel isApartnessRel public
    renaming
      ( isIrrefl  to #-irrefl
      ; isSym     to #-sym
      ; isCotrans to #-cotrans )

_ = {!!}

record ConstructiveField : Type (ℓ-suc (ℓ-max ℓ ℓ')) where
  field
    Carrier : Type ℓ
    0f   : Carrier
    1f   : Carrier
    _+_  : Carrier → Carrier → Carrier
    _·_  : Carrier → Carrier → Carrier
    -_   : Carrier → Carrier
    _#_  : Rel Carrier Carrier ℓ'
    _⁻¹ᶠ : (x : Carrier) → {{x # 0f}} → Carrier
    isConstructiveField : IsConstructiveField 0f 1f _+_ _·_ -_ _#_ _⁻¹ᶠ

  infix  9 _⁻¹ᶠ
  infixl 7 _·_
  infix  6 -_
  infixl 5 _+_
  infixl 4 _#_

  open IsConstructiveField isConstructiveField public

open import Cubical.Data.Empty renaming (elim to ⊥-elim) -- `⊥` and `elim`

-- Lemma 4.1.6.
-- For a constructive field (F, 0, 1, +, ·, #), the following hold.
-- 1. 1 # 0.
-- 2. Addition + is #-compatible in the sense that for all x, y, z : F
--    x # y ⇔ x + z # y + z.
-- 3. Multiplication · is #-extensional in the sense that for all w, x, y, z : F
--    w · x # y · z ⇒ w # y ∨ x # z.
module Lemmas-4-6-1 {{F : ConstructiveField {ℓ} {ℓ'}}} where
  --open ConstructiveField {{...}} public -- creates additional `ConstructiveField.foo F` in the "Goal/Have-previews"
  open ConstructiveField F -- works

  open import Cubical.Structures.Ring
  -- NOTE: this also creates additional `Ring.Carrier (makeRing ...)` in the "Goal/Have-previews", except when using C-u C-u C-... then these get normalized fine
  --   can we do something with pragmas here? https://agda.readthedocs.io/en/latest/language/pragmas.html?highlight=INLINE#the-inline-and-noinline-pragmas
  --   or maybe with macros? https://github.com/alhassy/gentle-intro-to-reflection
  -- using this `R` makes it a little better
  R = (makeRing 0f 1f _+_ _·_ -_ is-set +-assoc +-rid +-rinv +-comm ·-assoc ·-rid ·-lid ·-rdist-+ ·-ldist-+)
  open Cubical.Structures.Ring.Theory R
  -- 0-rightNullifies

  -- open Ring {{...}} public

  {-
  module ReflectionTest where
    open import Data.Nat as Nat hiding (_⊓_)
    open import Data.List as List
    open import Data.Char as Char
    open import Data.String as String

    open import Reflection hiding (name; Type)
    open import Reflection.Term as Term public hiding (Type)
    test06 = (quoteTC 0-rightNullifies)

    test07 : _
    unquoteDef test07 = defineFun test07
      [ clause
        ( [] ) -- hidden arguments `{x}`
        ( [] ) -- visible arguments `{y}`
        (quoteTerm 0f) -- the "Term" at the right hand side of the `=`
      ]
    -- {-# INLINE test07 #-}
    -- {-# NOINLINE test07 #-}

    proof07 : test07 ≡ 0f
    proof07 = {!!}
  -}

  {-
  -- NOTE: it might be possible with --overlapping-instances to resolve this "Goal/Have-preview" issue
  --   but it is mentioned that this "might lead to an exponential slowdown in instance resolution and possibly (apparent) looping behaviour"
  --   also the OPTION pragma to apply --overlapping-instances can only be set per file (and not per module)
  --   so it might be a good idea to have a separate "Theory.agda" or "Properties.agda" (as it is called in the standard library)
  -- well, in a test with --overlapping instances, I got the issue that -_ is defined multiple times
  --   to be fair, the result we have implemented might fit better in a more general Ring context
  -- so one might think about this and until then, I guess, the best solution is to just omit multiple instances

  module InstanceTest where
    -- taken from https://agda.readthedocs.io/en/latest/language/instance-arguments.html#defining-type-classes
    -- open import Algebra
    open import Agda.Builtin.Nat
    open import Data.List renaming (List to BList)

    record Monoid {a} (A : Type a) : Type a where
      field
        mempty : A
        _<>_   : A → A → A
    open Monoid {{...}} public

    instance
      ListMonoid : ∀ {a} {A : Type a} → Monoid (BList A)
      -- normal: provide "implementation" in a single term
      --   ListMonoid = record { mempty = []; _<>_ = _++_ }
      -- with copatterns: provide "implementation" with patterns
      mempty {{ListMonoid}} = []
      _<>_   {{ListMonoid}} xs ys = xs ++ ys

    mconcat : ∀ {a} {A : Type a} → {{Monoid A}} → BList A → A
    mconcat [] = mempty
    mconcat (x ∷ xs) = x <> mconcat xs

    sum1 : BList Nat → Nat
    sum1 xs =
      let instance -- NOTE: IMPORTANT: the instance-block needs to be more indented (!) than the `instance` keyword
            NatMonoid : Monoid Nat
            NatMonoid = record { mempty = 0; _<>_ = Agda.Builtin.Nat._+_ }
      in mconcat xs

  itest2 =
    let
      instance
        rr = R
    in {! 0-rightNullifies!}

  -- To restrict an instance to the current module, you can mark it as private. For instance,
  --    https://agda.readthedocs.io/en/latest/language/instance-arguments.html#restricting-instance-search
  -}

  {-
  https://agda.readthedocs.io/en/latest/language/abstract-definitions.html
    Abstract definitions
    Definitions can be marked as abstract, for the purpose of hiding implementation details, or to speed up type-checking of other parts. In essence, abstract definitions behave like postulates, thus, do not reduce/compute. For instance, proofs whose content does not matter could be marked abstract, to prevent Agda from unfolding them (which might slow down type-checking).
  -}

  -- anonymous modules `module _ params where` automatically open themselves
  --   they are not well documented but there is this usage example
  --     Anonymous modules or sections #735
  --     https://github.com/agda/agda/issues/735
  --       I often want to use something like sections in Coq. I usually use parametrized modules but this has at least two issues that Coq does not have:
  --       1) I need to give a name to the module, and there is often no meaningful name  to be given
  --       2) It exports the module, so I cannot define a module in another file with the  same name
  --       the following code
  --         module _ params where
  --           declarations
  --       would be translated to:
  --         private
  --           module Fresh params where
  --             declarations
  --         open Fresh public
  --       where Fresh is a new name.
  --     NOTE: this gives a hint on the use of "sections" in mathematical writing
  --       it suggests, that these introduce variable-but-fixed parameter dependencies to all definitions (in the way the `variable` keyword does)
  --       the difference between an anonymous module and a variable seems to be that the module adds all parameters, where `variable` only adds parameters when used
  --       also, in the module we have more flexibility with opening other modules and hiding names with the `private` keyword, etc.
  --     and there is
  --       https://github.com/agda/agda/issues/1145
  --       Allow multiple layout keywords on the same line #1145

  -- NOTE: IMPORTANT: when in a hole, with C-c C-o one can list "module contents"
  --   if given `F` here, we can see the contents of F
  --   I guess, since every inherited member is included with `open ... public`,
  --     we can continue with just `isConstructiveField`
  --     and even `isCommRing`
  --     and even `isRing`
  --   this greatly helps to investigate available

  {-
  test3 : ∃[ x ∈ Carrier ] x · x ≡ x → Carrier
  test3 ∥_∥.∣ Σx-x·x≡x ∣ = {!!}
  --test ∥_∥.∣ x , x·x≡x ∣ = x
  test3 (∥_∥.squash Σx-x·x≡x₁ Σx-x·x≡x₂ i) = {!!}

  test3b : {A : Type} → ∃[ x ∈ A ] ∃[ y ∈ A ] x ≡ y → A
  test3b ∥_∥.∣ fst₁ , ∥_∥.∣ fst₂ , snd₁ ∣ ∣ = {!!}
  test3b ∥_∥.∣ fst₁ , ∥_∥.squash snd₁ ∥_∥.∣ x ∣ i ∣ = {!!}
  test3b ∥_∥.∣ fst₁ , ∥_∥.squash snd₁ (∥_∥.squash snd₂ ∥_∥.∣ x ∣ i₁) i ∣ = {!!}
  test3b ∥_∥.∣ fst₁ , ∥_∥.squash snd₁ (∥_∥.squash snd₂ (∥_∥.squash snd₃ snd₄ i₂) i₁) i ∣ = {!!}
  test3b (∥_∥.squash ∥_∥.∣ x ∣ ∥_∥.∣ x₁ ∣ i) = {!!}
  test3b (∥_∥.squash ∥_∥.∣ x ∣ (∥_∥.squash x₁ x₂ i₁) i) = {!!}
  test3b (∥_∥.squash (∥_∥.squash x x₂ i₁) x₁ i) = {!!}
  -}

  -- ∃ : ∀ {ℓ ℓ'} (A : Type ℓ) (B : A → Type ℓ') → Type (ℓ-max ℓ ℓ')
  -- ∃ A B = ∥ Σ A B ∥
  -- infix 2 ∃-syntax
  -- ∃-syntax : ∀ {ℓ ℓ'} (A : Type ℓ) (B : A → Type ℓ') → Type (ℓ-max ℓ ℓ')
  -- ∃-syntax = ∃
  -- syntax ∃-syntax A (λ x → B) = ∃[ x ∈ A ] B

  -- Lemma 4.1.6.1
  1f#0f : 1f # 0f
  1f#0f with ·-identity 1f
  -- 1f#0f | 1·1≡1 , _ = ·-inv-back 1f ∣ (1f , 1·1≡1) ∣ -- need additional ∣_∣ resp. \|_\| around the tuple
  1f#0f | 1·1≡1 , _ = fst (·-inv-back _ _ 1·1≡1)

  _ = Cubical.Data.Sigma.Base._×_ -- fst and snd

  a+b-b≡a : ∀ a b → a ≡ (a + b) - b
  -- NOTE: about workflow
  --   one can investigate some single term in the hole (C-c C-.), but not a second one
  --   therefore, we can move a term temporarily into a let-clause and work on another term in the hole
  --   ... is there equational reasoning in --cubical ?
  -- NOTE: Yes! it is in Cubical.Foundations.Id
  a+b-b≡a a b = let P = sym (fst (+-inv b))
                    Q = sym (fst (+-identity a))
                    R = transport (λ i → a ≡ a + P i) Q
                    S = transport (λ i → a ≡ (+-assoc a b (- b)) i ) R
                in S

  +-preserves-≡ : ∀{a b} → ∀ c → a ≡ b → a + c ≡ b + c
  +-preserves-≡ c a≡b i = a≡b i + c

  +-preserves-≡-l : ∀{a b} → ∀ c → a ≡ b → c + a ≡ c + b
  +-preserves-≡-l c a≡b i = c + a≡b i

  a+b≡0→a≡-b : ∀{a b} → a + b ≡ 0f → a ≡ - b
  a+b≡0→a≡-b {a} {b} a+b≡0 = transport
    (λ i →
      let RHS = snd (+-identity (- b))
          LHS₁ : a + (b + - b) ≡ a + 0f
          LHS₁ = +-preserves-≡-l a (fst (+-inv b))
          LHS₂ : (a + b) - b ≡ a
          LHS₂ = transport (λ j →  (+-assoc a b (- b)) j ≡ fst (+-identity a) j) LHS₁
          in  LHS₂ i ≡ RHS i
    ) (+-preserves-≡ (- b) a+b≡0)

  a·-b≡-a·b : ∀ a b → a · (- b) ≡ - a · b
  a·-b≡-a·b a b =
    let P : a · 0f ≡ 0f
        P = 0-rightNullifies a
        Q : a · (- b + b) ≡ 0f
        Q = transport (λ i →  a · snd (+-inv b) (~ i) ≡ 0f) P
        R : a · (- b) + a · b ≡ 0f
        R = transport (λ i → ·-rdist-+ a (- b) b i ≡ 0f) Q
    in a+b≡0→a≡-b R

  a·b-a·c≡a·[b-c] : ∀ a b c → a · b - a · c ≡ a · (b - c)
  a·b-a·c≡a·[b-c] a b c =
    let P : a · b + a · (- c) ≡ a · (b + - c)
        P = sym (·-rdist-+ a b (- c))
        Q : a · b - a · c ≡ a · (b + - c)
        Q = transport (λ i →  a · b + a·-b≡-a·b a c i ≡ a · (b + - c) ) P
    in  Q

  -- Lemma 4.1.6.2
  --   For #-compatibility of +, suppose x # y, that is, (x +z) −z # (y +z) −z.
  --   Then #-extensionality gives (x + z # y + z) ∨ (−z # −z), where the latter case is excluded by irreflexivity of #.
  +-#-compatible : ∀(x y z : Carrier) → x # y → x + z # y + z
  +-#-compatible x y z x#y with
    let P = transport (λ i →  a+b-b≡a x z i # a+b-b≡a y z i ) x#y
    in  +-#-extensional _ _ _ _ P
  ... | inl x+z#y+z = x+z#y+z
  ... | inr  -z#-z  = ⊥-elim (#-irrefl _ _ -z#-z -z#-z)

  -- The other direction is similar.
  +-#-compatible-inv : ∀(x y z : Carrier) → x + z # y + z → x # y
  +-#-compatible-inv _ _ _ x+z#y+z with +-#-extensional _ _ _ _ x+z#y+z
  ... | inl x#y = x#y
  ... | inr z#z = ⊥-elim (#-irrefl _ _ z#z z#z)

  -- _ = {!!} ≃⟨ {!!} ⟩ {!!} ■
  -- open import
  -- _ = {!!} ≡[ i ]⟨ {!!} ⟩ {!!} ∎

  --_≡⟨_⟩_ : (x : A) → x ≡ y → y ≡ z → x ≡ z
  --_ ≡⟨ x≡y ⟩ y≡z = x≡y ∙ y≡z

  -- for a list of unicode symbols in agda, see https://people.inf.elte.hu/divip/AgdaTutorial/Symbols.html
  infix  3 _◼
  infixr 2 _⇒⟨_⟩_

  _⇒⟨_⟩_ : ∀{ℓ ℓ' ℓ''} {Q : Type ℓ'} {R : Type ℓ''} → (P : Type ℓ) → (P → Q) → (Q → R) → (P → R)
  _ ⇒⟨ pq ⟩ qr = qr ∘ pq

  _◼ : ∀{ℓ} (A : Type ℓ) → A → A
  _ ◼ = λ x → x

  f : Carrier → Carrier
  f x with +-inv x
  ... | fst₁ , snd₁ = {!!}

  -- see https://agda.readthedocs.io/en/latest/language/instance-arguments.html
  it : ∀ {a} {A : Type a} → {{A}} → A
  it {{x}} = x -- instance search

  -- Lemma 4.1.6.3
  --   TODO: there is equational resoning in Cubical.Foundations.Id, use this
  --     P = _ ≡⟨ _ ⟩ _ ∎
  --     P = _ ≡[ i ] _ ∎
  --       syntax ≡⟨⟩-syntax x (λ i → B) y = x ≡[ i ]⟨ B ⟩ y
  --   in general this module offers a lot of useful equational machinery
  --     it already makes use of path concatenation with ∙ and ∙∙
  --   there is also Cubical.Foundations.Equiv
  --     P = _ ≃⟨ _ ⟩ _ ■
  ·-#-extensional-case1 : ∀(w x y z : Carrier) → w · x # y · z → w · x # w · z → x # z
  ·-#-extensional-case1 w x y z w·x#y·z w·x#w·z =
    let P : w · x + - w · x # w · z + - w · x
        P =   +-#-compatible _ _ (- (w · x)) w·x#w·z
        Q : 0f # w · (z + - x)
        Q =   transport (λ i →  (fst (+-inv (w · x)) i) # a·b-a·c≡a·[b-c] w z x i) P
        instance -- NOTE: this allows to use ⁻¹ᶠ without an instance argument
          q = #-sym _ _ Q
        -- see https://agda.readthedocs.io/en/latest/language/instance-arguments.html
        it : ∀ {a} {A : Type a} → {{A}} → A
        it {{x}} = x -- instance search
        R : w · (z - x) · (w · (z - x)) ⁻¹ᶠ ≡ 1f
        R =   ·-rinv (w · (z - x)) it -- (#-sym _ _ Q)
        S : (z + - x) · w · (w · (z + - x)) ⁻¹ᶠ ≡ 1f
        S =   transport (λ i → ·-comm w (z - x) i · (w · (z - x)) ⁻¹ᶠ ≡ 1f) R
        T : (z + - x) · (w · (w · (z + - x)) ⁻¹ᶠ) ≡ 1f
        T =   transport (λ i → ·-assoc (z + - x) w ((w · (z + - x)) ⁻¹ᶠ) (~ i) ≡ 1f) S
        U : z - x # 0f
        U =   fst (·-inv-back _ _ T)
        V : z - x + x # 0f + x
        V = (+-#-compatible _ _ x) (fst (·-inv-back _ _ T)) -- (+-#-compatible _ _ x U)
        -- workflow:
        --   start with `transport (λ i → z - x + x # 0f + x) V` in the hole (i.e. a constant transport)
        --   and then fiddle with the path and look what comes out
        W : z + (- x + x) # x
        W =   transport (λ i → +-assoc z (- x) x (~ i) # snd (+-identity x) i) V
        X : z + 0f # x
        X =   transport (λ i → z + snd (+-inv x) i # x) W
    in (#-sym _ _ (transport (λ i → fst (+-identity z) i # x) X))
  ·-#-extensional : ∀(w x y z : Carrier) → w · x # y · z → (w # y) ⊎ (x # z)
  ·-#-extensional w x y z w·x#y·z with #-cotrans _ _ w·x#y·z (w · z)
  ... | inl w·x#w·z =    inr (·-#-extensional-case1 w x y z w·x#y·z w·x#w·z) -- first case
  ... | inr w·z#y·z = let z·w≡z·y = (transport (λ i → ·-comm w z i # ·-comm y z i) w·z#y·z)
                      in inl (·-#-extensional-case1 _ _ _ _ z·w≡z·y z·w≡z·y) -- second case reduced to first case

  -- for an example to use equational reasoning in this topic, see https://github.com/felixwellen/cubical/blob/field/Cubical/Structures/Field.agda
  -- we do not do equational reasoning here but "implicational" reasoning instead
  -- the special syntax should make it more readable and omit giving names to every intermediate step
  -- but giving names to intermediates in a `let` or `where` clause might just be the way it's done
  --   c.f. with more elaborate proofs e.g. in https://www.cs.bham.ac.uk/~mhe/HoTT-UF-in-Agda-Lecture-Notes/HoTT-UF-Agda.html#combining-structures
  -- also see the elaborate proof in https://www.cs.bham.ac.uk/~mhe/HoTT-UF-in-Agda-Lecture-Notes/HoTT-UF-Agda.html#381519
  --   which uses equational reasoning, but still names the single steps i, ii, iii, iv, v, vi, ... and resolves them in a `where` clause
  -- also see https://www.cs.bham.ac.uk/~mhe/HoTT-UF-in-Agda-Lecture-Notes/HoTT-UF-Agda.html#391417
  -- "We consider rings without unit, called rngs, and with unit, called rings."
  -- this work of Martín Escardó was prepared for the
  --   Midlands Graduate School in the Foundations of Computing Science (University of Birmingham, UK, 14 - 18 April, 2019)
  --     http://events.cs.bham.ac.uk/mgs2019/
  --   Introductory Courses:
  --   - Lambda Calculus (course notes)               by Venanzio Capretta (University of Nottingham)
  --   - Category Theory (course notes)               by Thorsten Altenkirch (University of Nottingham)
  --   - Univalent Type Theory in Agda (course notes) by Martín Escardó (University of Birmingham)

  ·-#-extensional-case1' : ∀(w x y z : Carrier) → w · x # y · z → w · x # w · z → x # z
  ·-#-extensional-case1' w x y z w·x#y·z w·x#w·z =
    let
      instance -- this allows to use ⁻¹ᶠ without an instance argument
        w·[z-x]#0f =
          ( w · x         # w ·  z         ⇒⟨ +-#-compatible _ _ (- (w · x)) ⟩
            w · x - w · x # w ·  z - w · x ⇒⟨ transport (λ i →  (fst (+-inv (w · x)) i) # a·b-a·c≡a·[b-c] w z x i) ⟩
                       0f # w · (z - x)    ⇒⟨ #-sym _ _ ⟩
            w ·   (z - x) # 0f             ◼) w·x#w·z
    in (     w  · (z - x) # 0f                      ⇒⟨ (λ _ → ·-rinv (w · (z - x)) it ) ⟩  -- NOTE: "plugging in" the instance did not work, ∴ `it`
             w  · (z - x) · (w · (z - x)) ⁻¹ᶠ  ≡ 1f ⇒⟨ transport (λ i → ·-comm w (z - x) i · (w · (z - x)) ⁻¹ᶠ ≡ 1f) ⟩
        (z - x) ·  w      · (w · (z - x)) ⁻¹ᶠ  ≡ 1f ⇒⟨ transport (λ i → ·-assoc (z - x) w ((w · (z - x)) ⁻¹ᶠ) (~ i) ≡ 1f) ⟩
        (z - x) · (w      · (w · (z - x)) ⁻¹ᶠ) ≡ 1f ⇒⟨ fst ∘ (·-inv-back _ _)  ⟩
           z    - x       # 0f                      ⇒⟨ +-#-compatible _ _ x ⟩
          (z    - x) + x  # 0f + x                  ⇒⟨ transport (λ i → +-assoc z (- x) x (~ i) # snd (+-identity x) i) ⟩
           z + (- x  + x) #      x                  ⇒⟨ transport (λ i → z + snd (+-inv x) i # x) ⟩
           z +      0f    # x                       ⇒⟨ transport (λ i → fst (+-identity z) i # x) ⟩
           z              # x                       ⇒⟨ #-sym _ _ ⟩
           x              # z                       ◼) it -- conceptually we would plug `w·[z-x]#0f` in, but this breaks the very first step

--
-- Lemma 4.1.7.
-- Given a strict partial order < on a set X:
-- 1. we have an apartness relation defined by
--    x # y := (x < y) ∨ (y < x), and
-- 2. we have a preorder defined by
--    x ≤ y := ¬(y < x).
--
-- Definition 4.1.8.
-- Let (A, ≤) be a partial order, and let min, max : A → A → A be binary operators on A. We say that (A, ≤, min, max) is a lattice if min computes greatest lower bounds in the sense that for every x, y, z : A, we have
--   z ≤ min(x,y) ⇔ z ≤ x ∧ z ≤ y,
-- and max computes least upper bounds in the sense that for every x, y, z : A, we have
--   max(x,y) ≤ z ⇔ x ≤ z ∧ y ≤ z.
--
-- Remark 4.1.9.
-- 1. From the fact that (A, ≤, min, max) is a lattice, it does not follow that for every x and y, min(x,y) = x ∨ min(x,y) = y. However, we can characterize min as
--   z < min(x,y) ⇔ z < x ∨ z < y
--   and similarly for max, see Lemma 6.7.1.
-- 2. In a partial order, for two fixed elements a and b, all joins and meets of a, b are equal, so that Lemma 2.6.20 the type of joins and the type of meets are propositions. Hence, providing the maps min and max as in the above definition is equivalent to the showing the existenceof all binary joins and meets.
--
-- The following definition is modified from on The Univalent Foundations Program [89, Definition 11.2.7].
--
-- Definition 4.1.10.
-- An ordered field is a set F together with constants 0, 1, operations +, ·, min, max, and a binary relation < such that:
-- 1. (F, 0, 1, +, ·) is a commutative ring with unit;
-- 2. < is a strict order;
-- 3. x : F has a multiplicative inverse iff x # 0, recalling that # is defined as in Lemma 4.1.7;
-- 4. ≤, as in Lemma 4.1.7, is antisymmetric, so that (F, ≤) is a partial order;
-- 5. (F, ≤, min, max) is a lattice.
-- 6. for all x, y, z, w : F:
--    x + y < z + w ⇒ x < z ∨ y < w, (†)
--    0 < z ∧ x < y ⇒ x z < y z.     (∗)
-- Our notion of ordered fields coincides with The Univalent Foundations Program [89, Definition 11.2.7].
--
-- Lemma 4.1.11.
-- In the presence of the first five axioms of Definition 4.1.10, conditions (†) and (∗) are together equivalent to the condition that for all x, y, z : F,
--  1. x ≤ y ⇔ ¬(y < x),
--  2. x # y ⇔ (x < y) ∨ (y < x)
--  3. x ≤ y ⇔ x + z ≤ y + z,
--  4. x < y ⇔ x + z < y + z,
--  5. 0 < x + y ⇒ 0 < x ∨ 0 < y,
--  6. x < y ≤ z ⇒ x < z,
--  7. x ≤ y < z ⇒ x < z,
--  8. x ≤ y ∧ 0 ≤ z ⇒ x z ≤ y z,
--  9. 0 < z ⇒ (x < y ⇔ x z < y z),
-- 10. 0 < 1.

-- we have in cubical
--   import Cubical.HITs.Rationals.HITQ
--     ℚ as a higher inductive type
--   import Cubical.HITs.Rationals.QuoQ
--     ℚ as a set quotient of ℤ × ℕ₊₁ (as in the HoTT book)
--   import Cubical.HITs.Rationals.SigmaQ
--     ℚ as the set of coprime pairs in ℤ × ℕ₊₁