module MGU where

--open import Agda.Builtin.Nat using () renaming (Nat to ℕ)
open import Agda.Primitive
open import Agda.Builtin.Equality

open import Prelude.Product using (Σ; _,_; fst; snd; _×_; curry; uncurry)
open import Prelude.Equality using (_≡_; eqReasoningStep; _∎; sym; trans; cong)
open import Prelude.Function using (_∘_)
open import Prelude.Empty using (⊥)
open import Prelude.Sum using () renaming (Either to _⊎_)
open import Prelude.Maybe using (Maybe; just; nothing)

∃ : ∀ {a b} {A : Set a} (B : A → Set b) → Set (a ⊔ b)
∃ = Σ _

open import Prelude using (_$_)

open import Relation.Binary
open import Algebra
open import Category.Applicative.Predicate

open import Prolegomenon

--record IsTermSubstitution {ℓᵗ} {ℓ⁼ᵗ} {ℓˢ} {ℓ⁼ˢ}

record IsSetoid {c} {ℓ} {Carrier : Set c} (_≈_ : Rel Carrier ℓ) : Set (c ⊔ ℓ) where
  field
    isEquivalence : IsEquivalence _≈_

  setoid : Setoid c ℓ
  setoid = record { Carrier = Carrier ; _≈_ = _≈_ ; isEquivalence = isEquivalence }

  open Setoid setoid public

open IsSetoid ⦃ … ⦄

open import Algebra.Structures
--open IsMonoid ⦃ … ⦄

record IsTermSubstitution {ℓᵗ} {ℓ⁼ᵗ} {ℓˢ} {ℓ⁼ˢ}
       (Term : Setoid ℓᵗ ℓ⁼ᵗ)
       (Substitution : Monoid ℓˢ ℓ⁼ˢ)
  : Set (ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ) where

  open Setoid Term public using () renaming
    (Carrier to T
    ;_≈_ to _=ᵗ_)

  open Monoid Substitution public using () renaming
    (Carrier to S
    ;_≈_ to _=ˢ_
    ;_∙_ to _∙_
    ;ε to ∅)

  infixl 6 _▹_
  field
    _▹_ : T → S → T
    ▹-=ˢ-to-ᵗ : ∀ {s s′} → s =ˢ s′ → (t : T) → (t ▹ s) =ᵗ (t ▹ s′)
    ▹-=ᵗ-to-=ˢ : ∀ {s s′} → ((t : T) → t ▹ s =ᵗ t ▹ s′) → s =ˢ s′
    ▹-extracts-∙ : (t : T) (s₁ s₂ : S) → t ▹ s₁ ∙ s₂ =ᵗ t ▹ s₁ ▹ s₂

open IsTermSubstitution ⦃ … ⦄

record TermSubstitution ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ : Set (lsuc (ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ)) where
  field
    Term : Setoid ℓᵗ ℓ⁼ᵗ
    Substitution : Monoid ℓˢ ℓ⁼ˢ
    isTermSubstitution : IsTermSubstitution Term Substitution

  open IsTermSubstitution isTermSubstitution public

open import Relation.Unary
import Relation.Binary.Indexed as I

open import Relation.Nullary

record 𝓩ero (A : Set) : Set where
  field
    𝓩 : A

open 𝓩ero ⦃ … ⦄

instance 𝓩eroLevel : 𝓩ero Level
𝓩ero.𝓩 𝓩eroLevel = lzero

open import Agda.Builtin.Nat
instance 𝓩eroNat : 𝓩ero Nat
𝓩ero.𝓩 𝓩eroNat = zero

record IsSeparableInto {sx s x} (SX : Set sx) (S : Set s) (X : Set x) : Set (s ⊔ x ⊔ sx) where
  field
    break : SX → S × X
    combine : S → X → SX
    iso : ∀ sx → uncurry combine (break sx) ≡ sx

record Separableoid sx s x : Set (lsuc (sx ⊔ s ⊔ x)) where
  field
    SX : Set sx
    S : Set s
    X : Set x
    separable : IsSeparableInto SX S X

_AND_ = _×_

NOT_ = ¬_

_NAND_ : ∀ {a b} (A : Set a) (B : Set b) → Set ((a ⊔ b))
_NAND_ A B = (NOT A) × (NOT B)

_OR_ : ∀ {a b} (A : Set a) (B : Set b) → Set ((a ⊔ b))
_OR_ A B = NOT (A NAND B)

_XOR_ : ∀ {a b} (A : Set a) (B : Set b) → Set ((a ⊔ b))
_XOR_ A B = (A OR B) AND (NOT (A AND B))

asdf : (A B : Set) → Dec A → A XOR B → Prelude.Either A (¬ ¬ B)
asdf A B x (¬[¬A×¬B] , ¬[A×B]) with x
asdf A B x (¬[¬A×¬B] , ¬[A×B]) | yes p = _⊎_.left p
asdf A B x (¬[¬A×¬B] , ¬[A×B]) | no ¬p = _⊎_.right (λ {x₁ → ¬[¬A×¬B] (¬p , x₁)})

import Data.Empty
frf : Dec Data.Empty.⊥
frf = no (λ x → x)

{-
record LAW (A : Set) (B : Set) (E : EITHER A B) : Set where
  open import Data.Empty
  open EITHER E
  ¬A→B : ¬ A → ¬ ¬ B
  ¬A→B ¬a ¬b with e
  ... | ei = ei (λ {(a , ¬b) → ¬a a})
-}

record FiniteMembership : Set where

open import Data.List

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

  listMembers : List A → Pred A ℓ
  listMembers [] x₁ = Prelude.⊥′
  listMembers (x ∷ xs) y = (y ≡ x) OR (listMembers xs y)

  _∈L_ : A → List A → Set ℓ
  x ∈L xs = x ∈ listMembers xs

  data _inL_ (y : A) : List A → Set where
    [] : ∀ {xs} → y inL (y ∷ xs)
    _∷_ : ∀ {x xs} → (ys : y inL xs) → y inL (x ∷ xs)

  ListMembers : List A → Pred A lzero
  ListMembers xs = _inL xs

  _∈LL_ : A → List A → Set
  x ∈LL xs = x ∈ ListMembers xs

  toLL : (y : A) (xs : List A) → y ∈L xs → y ∈LL xs
  toLL y [] x = Prelude.⊥′-elim x
  toLL y (x ∷ xs) y∈Lxxs = {!!} -- toLL y xs x₁

record Boolean (True : Set) (False : Set) : Set where
  field
    bool : True XOR False

{-
record L {ℓ} (A : Set ℓ) : Set ℓ where
  inductive
  field
    EMPTY : Set
    empty : L A
    head : A XOR ⊥
    tail : {!!}
-}
{-
    head : A XOR Prelude.⊥
    tail :
    empty : Prelude.⊥′
    list : EITHER Prelude.⊥′ (L A)
-}

record FreeVariableoid ℓᵛ∈ᵗ ℓᵛ∈ˢ⁺ ℓᵛ∈ˢ⁻ ℓᵛ ℓ⁼ᵛ ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ
       ⦃ termSubstitution : TermSubstitution ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ ⦄
  : Set (lsuc (ℓᵛ∈ᵗ ⊔ ℓᵛ∈ˢ⁺ ⊔ ℓᵛ∈ˢ⁻ ⊔ ℓᵛ ⊔ ℓ⁼ᵛ ⊔ ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ)) where
  -- open TermSubstitution termSubstitution
  field
    Variable : Setoid ℓᵛ ℓ⁼ᵛ
    TermStructure : Set

  open Setoid Variable public using () renaming
    (Carrier to V
    ;_≈_ to _=ᵛ_)

  field
    termVariables : T → Pred V ℓᵛ∈ᵗ
    termStructure : T → Set
    substitutionSourceVariables : S → Pred V ℓᵛ∈ˢ⁺
    substitutionTargetVariables : S → Pred V ℓᵛ∈ˢ⁻

  _ᵛ∈ᵗ_ : V → T → Set ℓᵛ∈ᵗ
  _ᵛ∈ᵗ_ v t = v ∈ termVariables t

  field
    termNumberOfVariables : (t : T) → ∃ λ (vs : List V) → (∀ v → (v ∈L vs → v ᵛ∈ᵗ t) × (v ᵛ∈ᵗ t → v ∈L vs))
    elementsDefineVariables : (t : T) → V

  _ᵛ∈ˢ⁺_ : V → S → Set ℓᵛ∈ˢ⁺
  _ᵛ∈ˢ⁺_ v s = v ∈ substitutionSourceVariables s

  _ᵛ∈ˢ⁻_ : V → S → Set ℓᵛ∈ˢ⁻
  _ᵛ∈ˢ⁻_ v s = v ∈ substitutionTargetVariables s

  field
    foo : ∀ {v t s} → v ᵛ∈ᵗ t → ¬ v ᵛ∈ˢ⁺ s → v ᵛ∈ᵗ (t ▹ s)
    isSep : IsSeparableInto T TermStructure V

record CorrectTermSubstitution ℓᵛ∈ᵗ ℓᵛ∈ˢ⁺ ℓᵛ∈ˢ⁻ ℓᵛ ℓ⁼ᵛ ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ
       ⦃ termSubstitution : TermSubstitution ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ ⦄
  : Set (lsuc (ℓᵛ∈ᵗ ⊔ ℓᵛ∈ˢ⁺ ⊔ ℓᵛ∈ˢ⁻ ⊔ ℓᵛ ⊔ ℓ⁼ᵛ ⊔ ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ)) where
  -- open TermSubstitution termSubstitution
  field
    Variable : Setoid ℓᵛ ℓ⁼ᵛ

  open Setoid Variable public using () renaming
    (Carrier to V
    ;_≈_ to _=ᵛ_)

  field
    termVariables : T → Pred V ℓᵛ∈ᵗ
    substitutionSourceVariables : S → Pred V ℓᵛ∈ˢ⁺
    substitutionTargetVariables : S → Pred V ℓᵛ∈ˢ⁻

  _ᵛ∈ᵗ_ : V → T → Set ℓᵛ∈ᵗ
  _ᵛ∈ᵗ_ v t = v ∈ termVariables t

  _ᵛ∈ˢ⁺_ : V → S → Set ℓᵛ∈ˢ⁺
  _ᵛ∈ˢ⁺_ v s = v ∈ substitutionSourceVariables s

  _ᵛ∈ˢ⁻_ : V → S → Set ℓᵛ∈ˢ⁻
  _ᵛ∈ˢ⁻_ v s = v ∈ substitutionTargetVariables s

  field
    foo : ∀ {v t s} → v ᵛ∈ᵗ t → ¬ v ᵛ∈ˢ⁺ s → v ᵛ∈ᵗ (t ▹ s)

  data D : Set where

data IsRight {a b} {A : Set a} {B : Set b} (e : _⊎_ A B) : Set (a ⊔ b) where
  right : B → IsRight e

module MostGeneralMGU where

  record Unificationoid {ℓᵗ} {ℓ⁼ᵗ} {ℓˢ} {ℓ⁼ˢ}
         ⦃ termSubstitution : TermSubstitution ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ ⦄
    : Set (lsuc (ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ)) where
    -- open TermSubstitution termSubstitution
    Property : ∀ {ℓ} → Set (ℓˢ ⊔ lsuc ℓ)
    Property {ℓ} = S → Set ℓ

    Nothing : ∀ {ℓ} → (P : Property {ℓ}) → Set (ℓ ⊔ ℓˢ)
    Nothing P = ∀ s → P s → ⊥

    IsUnifier : (t₁ t₂ : T) → Property
    IsUnifier t₁ t₂ s = t₁ ▹ s =ᵗ t₂ ▹ s

    field
      unify : (t₁ t₂ : T) → Nothing (IsUnifier t₁ t₂) ⊎ ∃ (IsUnifier t₁ t₂)

    unifier : (t₁ t₂ : T) → Maybe S
    unifier t₁ t₂ with unify t₁ t₂
    unifier t₁ t₂ | _⊎_.left x = nothing
    unifier t₁ t₂ | _⊎_.right (x , _) = just x

    unifier-is-correct : (t₁ t₂ : T) → Nothing (IsUnifier t₁ t₂) → unifier t₁ t₂ ≡ nothing
    unifier-is-correct = {!!}

  record IsMostGeneralUnification ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ
         ⦃ termSubstitution : TermSubstitution ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ ⦄
         (unificationoid : Unificationoid ⦃ termSubstitution ⦄)
    : Set (lsuc (ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ)) where
    --open TermSubstitution termSubstitution
    open Unificationoid unificationoid

    _≤_ : (s₋ : S) (s₊ : S) → Set (ℓˢ ⊔ ℓ⁼ˢ)
    _≤_ s₋ s₊ = ∃ λ s → s ∙ s₊ =ˢ s₋

    MostGenerally : ∀ {ℓ} (P : Property {ℓ}) → Property
    MostGenerally P s₊ = ∀ s₋ → P s₋ → s₋ ≤ s₊

    field
      isMguIfUnifier : (t₁ t₂ : T) → (ru : IsRight (unify t₁ t₂)) →
        Prelude.case ru of λ {((right (u , _))) → MostGenerally (IsUnifier t₁ t₂) u}
      --mgu : (t₁ t₂ : T) → Nothing (IsUnifier t₁ t₂) ⊎ ∃ (MostGenerally $ IsUnifier t₁ t₂)

    mgu : (t₁ t₂ : T) → Maybe S
    mgu = unifier

  record MostGeneralUnificationoid ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ
         ⦃ termSubstitution : TermSubstitution ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ ⦄
    : Set (lsuc (ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ)) where
    -- open TermSubstitution termSubstitution
    Property : ∀ {ℓ} → Set (ℓˢ ⊔ lsuc ℓ)
    Property {ℓ} = S → Set ℓ

    Nothing : ∀ {ℓ} → (P : Property {ℓ}) → Set (ℓ ⊔ ℓˢ)
    Nothing P = ∀ s → P s → ⊥

    IsUnifier : (t₁ t₂ : T) → Property
    IsUnifier t₁ t₂ s = t₁ ▹ s =ᵗ t₂ ▹ s

    _≤_ : (s₋ : S) (s₊ : S) → Set (ℓˢ ⊔ ℓ⁼ˢ)
    _≤_ s₋ s₊ = ∃ λ s → s ∙ s₊ =ˢ s₋

    MostGenerally : ∀ {ℓ} (P : Property {ℓ}) → Property
    MostGenerally P s₊ = P s₊ × ∀ s₋ → P s₋ → s₋ ≤ s₊

    field
      mgu : (t₁ t₂ : T) → Nothing (IsUnifier t₁ t₂) ⊎ ∃ (MostGenerally $ IsUnifier t₁ t₂)

--   record PairUnification ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ
--          ⦃ termSubstitution : TermSubstitution ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ ⦄
--     : Set (lsuc (ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ)) where
--     --open TermSubstitution termSubstitution
--     Property : ∀ {ℓ} → Set (ℓˢ ⊔ lsuc ℓ)
--     Property {ℓ} = S × S → Set ℓ

--     Nothing : ∀ {ℓ} → (P : Property {ℓ}) → Set (ℓ ⊔ ℓˢ)
--     Nothing P = ∀ u → P u → ⊥

--     IsUnifier : (t₁ t₂ : T) → Property
--     IsUnifier t₁ t₂ u = let (s₁ , s₂) = u in t₁ ▹ s₁ =ᵗ t₂ ▹ s₂

--     infix 4 _≤_
--     _≤_ : (s₋ : S × S) (s₊ : S × S) → Set (ℓˢ ⊔ ℓ⁼ˢ)
--     _≤_ u₋ u₊ =
--       let s₋₁ , s₋₂ = u₋
--           s₊₁ , s₊₂ = u₊ in
--       ∃ λ s → s ∙ s₊₁ =ˢ s₋₁ × s ∙ s₊₂ =ˢ s₋₂

--     MostGenerally : ∀ {ℓ} (P : Property {ℓ}) → Property
--     MostGenerally P u₊ = P u₊ × ∀ u₋ → P u₋ → u₋ ≤ u₊

-- {-
--   mgu f(x,y) f(y,x)

--   x → w , y → z || x → z , y → w

--   mgu f(x,y) f(g(y),x)

--   x → g(z) || y → z , x → y

--   mgu f(x,y) f(y,z)

--   x → y , y → z || ∅              ≥₂
--   x → w , y → z || y → w          ≥₂
--   x → w , y → v || y → w , z → v  ≥₂
--   y → v || y → x , z → v  ≥₂
--   ∅ || y → x , z → y

--   x → w , y → v , z → v || y → w , z → v
--   x → y , y → f(z) || z → f(z)

--   f(x  , g(x,y)     ,w)
--   x → g(z)
--   w →? v

--   f(g(z), g(g(z),y) ,v)
--   v →? w

--   possible unifers:
--   x → g(z)    , w → v      ||
--   x → g(h(a)) , w → v      || z → h(a)
--   x → g(z)                 || z → h(y) , v → w

-- -}

--     field
--       mgu : (t₁ t₂ : T)
--             → Nothing (IsUnifier t₁ t₂) ⊎ ∃ (MostGenerally (IsUnifier t₁ t₂))

-- -- record Something ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ : Set (lsuc (ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ)) where
-- --   field
-- --     Terminus : Setoid ℓᵗ ℓ⁼ᵗ
-- --     Simplex : Monoid ℓˢ ℓ⁼ˢ

-- --   open Setoid Terminus public using () renaming
-- --     (Carrier to T
-- --     ;_≈_ to _=T=_)

-- --   open Monoid Simplex public using () renaming
-- --     (Carrier to S
-- --     ;_≈_ to _=S=_
-- --     ;_∙_ to _∙_
-- --     ;ε to ∅)

-- --   infixl 6 _▹_
-- --   field
-- --     _▹_ : T → S → T
-- --     -- equivalence of S implies substitutivity in (T,▹)
-- --     ▹-=S=-to-=T= : ∀ {s s′} → s =S= s′ → (t : T) → t ▹ s =T= t ▹ s′
-- --     -- applications of simplexi are equivalent across all termini only when the simplexi are equivalent
-- --     -- that is, equivalence of S is as compact is it can be while still respecting equivalence in (T,▹)
-- --     ▹-=T=-to-=S= : ∀ {s s′} → ((t : T) → t ▹ s =T= t ▹ s′) → s =S= s′
-- --     -- S extracts to ▹ in (T,∙)
-- --     ▹-∙-compositional : (t : T) (s₁ s₂ : S) → t ▹ s₁ ∙ s₂ =T= t ▹ s₁ ▹ s₂

-- -- -- module NotIndexed where

-- -- --   record MGU ℓᵗ ℓ⁼ᵗ ℓˢ ℓ⁼ˢ : Set (lsuc (ℓᵗ ⊔ ℓ⁼ᵗ ⊔ ℓˢ ⊔ ℓ⁼ˢ)) where
-- -- --     infixr 8 _◃_
-- -- --     --infixr 9 _◇_
-- -- --     field
-- -- --       𝓣erm : Setoid ℓᵗ ℓ⁼ᵗ

-- -- --     open Setoid 𝓣erm public using () renaming (Carrier to 𝓣; _≈_ to _=ᵗ_)

-- -- --     field
-- -- --       𝓢ubstition : Semigroup ℓˢ ℓ⁼ˢ -- TODO Monoid?

-- -- --     open Semigroup 𝓢ubstition public using () renaming
-- -- --       (Carrier to 𝓢
-- -- --       ;_≈_ to _=ˢ_
-- -- --       ; _∙_ to _◇_)

-- -- --     --field
-- -- --     --  𝓼ubstitute : RawPApplicative (λ x x₁ → {!!})

-- -- --     _◃'_ : 𝓢 → 𝓣 → 𝓣
-- -- --     _◃'_ = {!!}

-- -- --     field
-- -- --       _◃_ : 𝓢 → 𝓣 → 𝓣
-- -- --       ◃-=ˢ-to-=ᵗ : ∀ {𝒮 𝒮′} → 𝒮 =ˢ 𝒮′ → (𝒯 : 𝓣) → 𝒮 ◃ 𝒯 =ᵗ 𝒮′ ◃ 𝒯
-- -- --       ◃-=ᵗ-to-=ˢ : ∀ {𝒮 𝒮′} → ((𝒯 : 𝓣) → 𝒮 ◃ 𝒯 =ᵗ 𝒮′ ◃ 𝒯) → 𝒮 =ˢ 𝒮′

-- -- --     field

-- -- --       --_◇_ : 𝓢 → 𝓢 → 𝓢
-- -- --       ◇-compositional : (𝒯 : 𝓣) (𝒮₁ 𝒮₂ : 𝓢) → (𝒮₂ ◇ 𝒮₁) ◃ 𝒯 =ᵗ 𝒮₂ ◃ 𝒮₁ ◃ 𝒯

-- -- --     ◇-associative : (𝒮₁ 𝒮₂ 𝒮₃ : 𝓢) → 𝒮₃ ◇ (𝒮₂ ◇ 𝒮₁) =ˢ (𝒮₃ ◇ 𝒮₂) ◇ 𝒮₁
-- -- --     ◇-associative 𝒮₁ 𝒮₂ 𝒮₃ = {!!}

-- -- --     Property : ∀ {ℓ} → Set (ℓˢ ⊔ lsuc ℓ)
-- -- --     Property {ℓ} = 𝓢 → Set ℓ

-- -- --     Nothing : ∀ {ℓ} → (P : Property {ℓ}) → Set (ℓ ⊔ ℓˢ)
-- -- --     Nothing P = ∀ 𝒮 → P 𝒮 → ⊥

-- -- --     Unifies : (𝒯₁ 𝒯₂ : 𝓣) → Property
-- -- --     Unifies 𝒯₁ 𝒯₂ 𝒮 = 𝒮 ◃ 𝒯₁ ≡ 𝒮 ◃ 𝒯₂

-- -- --     _≤_ : (σ₋ : 𝓢) (σ₊ : 𝓢) → Set (ℓˢ ⊔ ℓ⁼ˢ)
-- -- --     _≤_ σ₋ σ₊ = ∃ λ σ → σ ◇ σ₊ =ˢ σ₋

-- -- --     Max : ∀ {ℓ} (P : Property {ℓ}) → Property
-- -- --     Max P σ₊ = P σ₊ × ∀ σ₋ → P σ₋ → σ₋ ≤ σ₊

-- -- --     field
-- -- --       mgu : (𝒯₁ 𝒯₂ : 𝓣) → Nothing (Unifies 𝒯₁ 𝒯₂) ⊎ ∃ λ (σ : 𝓢) → Max (Unifies 𝒯₁ 𝒯₂) σ

-- -- -- -- module Indexed where

-- -- -- --   record MGU ℓⁱ ℓᵛ ℓᵗ ℓˢ : Set (lsuc (ℓⁱ ⊔ ℓᵛ ⊔ ℓᵗ ⊔ ℓˢ)) where
-- -- -- --     field
-- -- -- --       𝓲 : Set ℓⁱ
-- -- -- --       𝓥 : 𝓲 → Set ℓᵛ
-- -- -- --       𝓣 : 𝓲 → Set ℓᵗ

-- -- -- --     _↦_ : (s t : 𝓲) → Set (ℓᵛ ⊔ ℓᵗ)
-- -- -- --     _↦_ s t = 𝓥 s → 𝓣 t

-- -- -- --     infix 14 _≐_
-- -- -- --     _≐_ : {s t : 𝓲} → s ↦ t → s ↦ t → Set (ℓᵛ ⊔ ℓᵗ)
-- -- -- --     _≐_ σ σ′ = ∀ x → σ x ≡ σ′ x

-- -- -- --     field
-- -- -- --       _◃_ : ∀ {s t} → s ↦ t → 𝓣 s → 𝓣 t
-- -- -- --       ≐-◃-identity : {!!}

-- -- -- --     _◇_ : ∀ {r s t : 𝓲} → (σ₂ : s ↦ t) (σ₁ : r ↦ s) → r ↦ t
-- -- -- --     _◇_ σ₂ σ₁ = (σ₂ ◃_) ∘ σ₁

-- -- -- --     field
-- -- -- --       𝓢 : 𝓲 → 𝓲 → Set ℓˢ
-- -- -- --       sub : ∀ {s t} → 𝓢 s t → s ↦ t

-- -- -- -- --     Property : ∀ {ℓ} → 𝓲 → Set (lsuc ℓ ⊔ ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)
-- -- -- -- --     Property {ℓ} s = ∀ {t} → s ↦ t → Set ℓ

-- -- -- -- --     Nothing : ∀ {ℓ m} → (P : Property {ℓ} m) → Set (ℓ ⊔ ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)
-- -- -- -- --     Nothing P = ∀ {n} f → P {n} f → ⊥

-- -- -- -- --     Unifies : ∀ {i} (t₁ t₂ : 𝓣 i) → Property i
-- -- -- -- --     Unifies t₁ t₂ f = f ◃ t₁ ≡ f ◃ t₂

-- -- -- -- --     _≤_ : ∀ {m n n'} (f : m ↦ n) (g : m ↦ n') → Set (ℓⱽ ⊔ ℓᵀ)
-- -- -- -- --     _≤_ f g = ∃ λ f' → f ≐ (f' ◇ g)

-- -- -- -- --     Max : ∀ {ℓ m} (P : Property {ℓ} m) → Property m
-- -- -- -- --     Max P = (λ f → P f × (∀ {n} f' → P {n} f' → f' ≤ f))

-- -- -- -- --     field
-- -- -- -- --       mgu : ∀ {m} (t₁ t₂ : 𝓣 m) →
-- -- -- -- --             Nothing (Unifies t₁ t₂) ⊎ (∃ λ n → ∃ λ (σ : 𝓢 m n) → (Max (Unifies t₁ t₂)) (sub σ))

-- -- -- -- -- -- -- -- open import Prelude.Function
-- -- -- -- -- -- -- -- open import Relation.Binary.HeterogeneousEquality.Core as H using (_≅_ ; ≅-to-≡)
-- -- -- -- -- -- -- -- open import Prelude.Equality

-- -- -- -- -- -- -- -- record MGU-addIndex-to-noIndex* {ℓⁱ ℓⱽ ℓᵀ ℓˢ}
-- -- -- -- -- -- -- --                             (m : MGU-addIndex ℓⁱ ℓⱽ ℓᵀ ℓˢ) : Set (lsuc (ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)) where
-- -- -- -- -- -- -- --   open MGU-addIndex m
-- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- --     𝓥ᵢ : Set (ℓⁱ ⊔ ℓⱽ)
-- -- -- -- -- -- -- --     𝓣ᵢ : Set (ℓⁱ ⊔ ℓᵀ)
-- -- -- -- -- -- -- --     𝓼ᵢ : Set (ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- --     to∃𝓥 : 𝓥ᵢ → ∃ 𝓥
-- -- -- -- -- -- -- --     to∃𝓣 : 𝓣ᵢ → ∃ 𝓣
-- -- -- -- -- -- -- --     to𝓥ᵢ : ∃ 𝓥 → 𝓥ᵢ
-- -- -- -- -- -- -- --     to𝓣ᵢ : ∃ 𝓣 → 𝓣ᵢ
-- -- -- -- -- -- -- --     iso∃𝓥 : (∃𝒱 : ∃ 𝓥) → to∃𝓥 (to𝓥ᵢ ∃𝒱) ≡ ∃𝒱
-- -- -- -- -- -- -- --     iso𝓥ᵢ : (𝒱ᵢ : 𝓥ᵢ) → to𝓥ᵢ (to∃𝓥 𝒱ᵢ) ≡ 𝒱ᵢ
-- -- -- -- -- -- -- --     iso∃𝓣 : (∃𝒯 : ∃ 𝓣) → to∃𝓣 (to𝓣ᵢ ∃𝒯) ≡ ∃𝒯
-- -- -- -- -- -- -- --     iso𝓣ᵢ : (𝒯ᵢ : 𝓣ᵢ) → to𝓣ᵢ (to∃𝓣 𝒯ᵢ) ≡ 𝒯ᵢ
-- -- -- -- -- -- -- --     inj𝓥 : ∀ {a b} → 𝓥 a ≡ 𝓥 b → a ≡ b
-- -- -- -- -- -- -- --     inj𝓣 : ∀ {a b} → 𝓣 a ≡ 𝓣 b → a ≡ b
-- -- -- -- -- -- -- --     mag : (𝓥ᵢ → 𝓣ᵢ) → ∀ {s} t → 𝓥 s → 𝓣 t
-- -- -- -- -- -- -- --     to∃𝓣𝓣 : 𝓣ᵢ → 𝓣ᵢ → ∃ λ i → 𝓣 i × 𝓣 i
-- -- -- -- -- -- -- --     max𝓣ᵢ : ∀ {m} n → (tm : 𝓣 m) → (((𝓣 n → 𝓣 m) × (𝓥 n → 𝓥 m)) ⊎ 𝓣 n)
-- -- -- -- -- -- -- --     max𝓲 : ∀ m n → ((𝓣 n → 𝓣 m) × (𝓥 n → 𝓥 m)) ⊎ ((𝓣 m → 𝓣 n) × (𝓥 m → 𝓥 n))
-- -- -- -- -- -- -- --     apply𝓼 : 𝓣ᵢ → (𝓥ᵢ → 𝓣ᵢ) → ∃ λ s → ∃ λ t → (𝓥 s → 𝓣 t) × 𝓣 s
-- -- -- -- -- -- -- --     to𝓼 : 𝓼ᵢ → ∃ λ s → ∃ λ t → 𝓥 s → 𝓣 t
-- -- -- -- -- -- -- --     to𝓼' : 𝓼ᵢ → 𝓣ᵢ → ∃ λ s → ∃ λ t → (𝓥 s → 𝓣 t) × 𝓣 s
-- -- -- -- -- -- -- --     to◇ : 𝓼ᵢ → 𝓼ᵢ → ∃ λ r → ∃ λ s → ∃ λ t → (𝓥 s → 𝓣 t) × (𝓥 r → 𝓣 s)
-- -- -- -- -- -- -- --     to= : 𝓼ᵢ → 𝓼ᵢ → ∃ λ s → ∃ λ t → (𝓥 s → 𝓣 t) × (𝓥 s → 𝓣 t)
-- -- -- -- -- -- -- --     to𝓼ᵢ : ∀ {r t} → (𝓥 r → 𝓣 t) → 𝓼ᵢ
-- -- -- -- -- -- -- --     {{ eqi }} : Eq 𝓲

-- -- -- -- -- -- -- --   to-noIndex : MGU-noIndex* (ℓⁱ ⊔ ℓⱽ) (ℓⁱ ⊔ ℓᵀ) (ℓⁱ ⊔ ℓˢ)
-- -- -- -- -- -- -- --   to-noIndex = go where
-- -- -- -- -- -- -- --     go : MGU-noIndex* _ _ _
-- -- -- -- -- -- -- --     MGU-noIndex*.𝓥 go = 𝓥ᵢ
-- -- -- -- -- -- -- --     MGU-noIndex*.𝓣 go = 𝓣ᵢ
-- -- -- -- -- -- -- --     MGU-noIndex*.𝓼 go = 𝓼ᵢ -- ∃ λ s → ∃ λ t → s ↦ t
-- -- -- -- -- -- -- --     MGU-noIndex*._≐_ go s1 s2 with to= s1 s2
-- -- -- -- -- -- -- --     ... | (s , t , vstt1 , vstt2) = {!_≐_ vstt1 vstt2!}
-- -- -- -- -- -- -- --     MGU-noIndex*._◃_ go f x with to𝓼' f x
-- -- -- -- -- -- -- --     MGU-noIndex*._◃_ go _ _ | (s , t , f , term) = to𝓣ᵢ $ _ , f ◃ term
-- -- -- -- -- -- -- --     MGU-noIndex*._◇_ go s1 s2 with to◇ s1 s2
-- -- -- -- -- -- -- --     ... | (r , s , t , vstt , vrts) = to𝓼ᵢ $ vstt ◇ vrts
-- -- -- -- -- -- -- --     MGU-noIndex*.mgu go = {!!}
-- -- -- -- -- -- -- -- --     MGU-noIndex*.𝓥 go = {!𝓥ᵢ!}
-- -- -- -- -- -- -- -- --     MGU-noIndex*.𝓣 go = 𝓣ᵢ
-- -- -- -- -- -- -- -- --     MGU-noIndex*.𝓼 go = {!!} -- ∃ λ s → ∃ λ t → s ↦ t -- ∃ λ s → ∃ λ t → 𝓥 s → 𝓣 t
-- -- -- -- -- -- -- -- --     MGU-noIndex*._≐_ go (s₁ , t₁ , f₁) (s₂ , t₂ , f₂) = {!!} -- ∃ λ (s-refl : s₁ ≡ s₂) → ∃ λ (t-refl : t₁ ≡ t₂) → (∀ x → f₁ x ≡ (Prelude.transport (λ i → 𝓥 s₂ → 𝓣 i) (sym t-refl) f₂) (Prelude.transport _ s-refl x))
-- -- -- -- -- -- -- -- --     {-
-- -- -- -- -- -- -- -- --     MGU-noIndex*._◃_ go (s , t , f) 𝒯 = {!_◃_ f!}
-- -- -- -- -- -- -- -- --     ∀ s t → 𝓣ᵢ → (𝓣 s → 𝓣 t) → 𝓣 s
-- -- -- -- -- -- -- -- --     ∀ s t → 𝓣ᵢ → (𝓣 s → 𝓣 t) → 𝓣ᵢ
-- -- -- -- -- -- -- -- --     𝓣 s
-- -- -- -- -- -- -- -- --     -}

-- -- -- -- -- -- -- -- --     MGU-noIndex*._◃_ go (s , t , f) 𝒯 with to∃𝓣 𝒯
-- -- -- -- -- -- -- -- --     MGU-noIndex*._◃_ go (s , t , f) 𝒯 | (s2 , ttt) with s == s2
-- -- -- -- -- -- -- -- --     (go MGU-noIndex*.◃ (.s , t , f)) 𝒯 | s , 𝒯s | (Prelude.yes refl) = to𝓣ᵢ (_ , _◃_ {s} {t} f 𝒯s)
-- -- -- -- -- -- -- -- --     (go MGU-noIndex*.◃ (s , t , f)) 𝒯 | s2 , ttt | (Prelude.no x) = 𝒯

-- -- -- -- -- -- -- -- --     MGU-noIndex*._◇_ go (s , t , f) (p , r , g) with max𝓲 r s
-- -- -- -- -- -- -- -- --     MGU-noIndex*._◇_ go (s , t , f) (p , r , g) | _⊎_.left (T , V) = {!!}
-- -- -- -- -- -- -- -- --     MGU-noIndex*._◇_ go (s , t , f) (p , r , g) | _⊎_.right (T , V) = _ , _ , f ◇ (T ∘ g) -- f ◃_ ∘ (T ∘ g)
-- -- -- -- -- -- -- -- --     {-
-- -- -- -- -- -- -- -- --     = _ , _ , f ◃_ ∘ (λ x → {!max𝓣ᵢ s₁ (g x)!}) -- _ , _ , f ◃_ ∘ (λ x → {!g x!})
-- -- -- -- -- -- -- -- --     -}

-- -- -- -- -- -- -- -- -- {-
-- -- -- -- -- -- -- -- --     MGU-noIndex*._◇_ go (s₁ , t₁ , f) (s₂ , t₂ , g) with t₂ == s₁
-- -- -- -- -- -- -- -- --     MGU-noIndex*._◇_ go (s₁ , t₁ , f) (s₂ , t₂ , g) | (Prelude.yes refl) = s₂ , t₁ , f ◃_ ∘ g
-- -- -- -- -- -- -- -- --     MGU-noIndex*._◇_ go (s₁ , t₁ , f) (s₂ , t₂ , g) | (Prelude.no x) = s₂ , t₂ , g
-- -- -- -- -- -- -- -- -- -}
-- -- -- -- -- -- -- -- --     -- = s₂ , t₁ , {!(f ◃_)!}
-- -- -- -- -- -- -- -- --     MGU-noIndex*.mgu go t₁ t₂ with to∃𝓣𝓣 t₁ t₂
-- -- -- -- -- -- -- -- --     … | i , tt₁ , tt₂ with mgu tt₁ tt₂
-- -- -- -- -- -- -- -- --     MGU-noIndex*.mgu go t₁ t₂ | i , tt₁ , tt₂ | (_⊎_.left x) = {!!}
-- -- -- -- -- -- -- -- --     MGU-noIndex*.mgu go t₁ t₂ | i , tt₁ , tt₂ | (_⊎_.right (i₂ , σ , subσ-refl , ff)) with to∃𝓣 t₁ | to∃𝓣 t₂
-- -- -- -- -- -- -- -- --     MGU-noIndex*.mgu go t₁ t₂ | i , tt₁ , tt₂ | (_⊎_.right (i₂ , σ , subσ-refl , ff)) | (fst₁ , snd₁) | (fst₂ , snd₂) = _⊎_.right ((_ , _ , sub σ) , {!!} , {!!}) -- _⊎_.right ((i , i₂ , sub σ) , {!subσ-refl!} , {!!})

-- -- -- -- -- -- -- -- -- record MGU-noIndex-to-addIndex {ℓⁱ ℓⱽ ℓᵀ ℓˢ}
-- -- -- -- -- -- -- -- --                             (m : MGU-noIndex ℓⱽ ℓᵀ ℓˢ) : Set (lsuc (ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)) where
-- -- -- -- -- -- -- -- --   open MGU-noIndex m
-- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- --     𝓲 : Set ℓⁱ
-- -- -- -- -- -- -- -- --     𝓥ᵢ : 𝓲 → Set ℓⱽ
-- -- -- -- -- -- -- -- --     𝓣ᵢ : 𝓲 → Set ℓᵀ
-- -- -- -- -- -- -- -- --     to𝓥 : ∃ 𝓥ᵢ → 𝓥
-- -- -- -- -- -- -- -- --     to𝓣 : ∃ 𝓣ᵢ → 𝓣
-- -- -- -- -- -- -- -- --     to∃𝓥ᵢ : 𝓥 → ∃ 𝓥ᵢ
-- -- -- -- -- -- -- -- --     to∃𝓣ᵢ : 𝓣 → ∃ 𝓣ᵢ
-- -- -- -- -- -- -- -- --     inj𝓥 : ∀ {a b} → 𝓥ᵢ a ≡ 𝓥ᵢ b → a ≡ b
-- -- -- -- -- -- -- -- --     inj𝓣 : ∀ {a b} → 𝓣ᵢ a ≡ 𝓣ᵢ b → a ≡ b
-- -- -- -- -- -- -- -- --     mag : ∀ {s t} → (𝓥ᵢ s → 𝓣ᵢ t) → 𝓥 → 𝓣
-- -- -- -- -- -- -- -- --     {-
-- -- -- -- -- -- -- -- --     iso∃𝓥 : (∃𝒱 : ∃ 𝓥) → to∃𝓥 (to𝓥ᵢ ∃𝒱) ≡ ∃𝒱
-- -- -- -- -- -- -- -- --     iso𝓥ᵢ : (𝒱ᵢ : 𝓥ᵢ) → to𝓥ᵢ (to∃𝓥 𝒱ᵢ) ≡ 𝒱ᵢ
-- -- -- -- -- -- -- -- --     iso∃𝓣 : (∃𝒯 : ∃ 𝓣) → to∃𝓣 (to𝓣ᵢ ∃𝒯) ≡ ∃𝒯
-- -- -- -- -- -- -- -- --     iso𝓣ᵢ : (𝒯ᵢ : 𝓣ᵢ) → to𝓣ᵢ (to∃𝓣 𝒯ᵢ) ≡ 𝒯ᵢ
-- -- -- -- -- -- -- -- --     mag : (𝓥ᵢ → 𝓣ᵢ) → ∀ {s} t → 𝓥 s → 𝓣 t
-- -- -- -- -- -- -- -- --     -}

-- -- -- -- -- -- -- -- --   to-addIndex : MGU-addIndex ℓⁱ ℓⱽ ℓᵀ ℓˢ
-- -- -- -- -- -- -- -- --   to-addIndex = go where
-- -- -- -- -- -- -- -- --     go : MGU-addIndex ℓⁱ ℓⱽ ℓᵀ ℓˢ
-- -- -- -- -- -- -- -- --     MGU-addIndex.𝓲 go = 𝓲
-- -- -- -- -- -- -- -- --     MGU-addIndex.𝓥 go = 𝓥ᵢ
-- -- -- -- -- -- -- -- --     MGU-addIndex.𝓣 go = 𝓣ᵢ
-- -- -- -- -- -- -- -- --     MGU-addIndex._◃_ go {s} {t} f x = Prelude.transport 𝓣ᵢ {!!} $ snd ∘ to∃𝓣ᵢ $ _◃_ (λ v → to𝓣 (t , f (Prelude.transport 𝓥ᵢ (inj𝓥 {_} {_} {!!}) (snd $ to∃𝓥ᵢ v)))) (to𝓣 (s , x)) -- let Ti , Tit = to∃𝓣ᵢ $ _◃_ (λ v → to𝓣 (t , f {!!})) (to𝓣 (s , x)) in {!!}
-- -- -- -- -- -- -- -- --     MGU-addIndex.𝓢 go = {!!}
-- -- -- -- -- -- -- -- --     MGU-addIndex.sub go = {!!}
-- -- -- -- -- -- -- -- --     MGU-addIndex.mgu go = {!!}

-- -- -- -- -- -- -- -- -- record MGU-addIndex-to-noIndex {ℓⁱ ℓⱽ ℓᵀ ℓˢ}
-- -- -- -- -- -- -- -- --                             (m : MGU-addIndex ℓⁱ ℓⱽ ℓᵀ ℓˢ) : Set (lsuc (ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)) where
-- -- -- -- -- -- -- -- --   open MGU-addIndex m
-- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- --     𝓥ᵢ : Set (ℓⁱ ⊔ ℓⱽ)
-- -- -- -- -- -- -- -- --     𝓣ᵢ : Set (ℓⁱ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- --     to∃𝓥 : 𝓥ᵢ → ∃ 𝓥
-- -- -- -- -- -- -- -- --     to∃𝓣 : 𝓣ᵢ → ∃ 𝓣
-- -- -- -- -- -- -- -- --     to𝓥ᵢ : ∃ 𝓥 → 𝓥ᵢ
-- -- -- -- -- -- -- -- --     to𝓣ᵢ : ∃ 𝓣 → 𝓣ᵢ
-- -- -- -- -- -- -- -- --     iso∃𝓥 : (∃𝒱 : ∃ 𝓥) → to∃𝓥 (to𝓥ᵢ ∃𝒱) ≡ ∃𝒱
-- -- -- -- -- -- -- -- --     iso𝓥ᵢ : (𝒱ᵢ : 𝓥ᵢ) → to𝓥ᵢ (to∃𝓥 𝒱ᵢ) ≡ 𝒱ᵢ
-- -- -- -- -- -- -- -- --     iso∃𝓣 : (∃𝒯 : ∃ 𝓣) → to∃𝓣 (to𝓣ᵢ ∃𝒯) ≡ ∃𝒯
-- -- -- -- -- -- -- -- --     iso𝓣ᵢ : (𝒯ᵢ : 𝓣ᵢ) → to𝓣ᵢ (to∃𝓣 𝒯ᵢ) ≡ 𝒯ᵢ
-- -- -- -- -- -- -- -- --     inj𝓥 : ∀ {a b} → 𝓥 a ≡ 𝓥 b → a ≡ b
-- -- -- -- -- -- -- -- --     inj𝓣 : ∀ {a b} → 𝓣 a ≡ 𝓣 b → a ≡ b
-- -- -- -- -- -- -- -- --     mag : (𝓥ᵢ → 𝓣ᵢ) → ∀ {s} t → 𝓥 s → 𝓣 t

-- -- -- -- -- -- -- -- --   to-noIndex : MGU-noIndex (ℓⁱ ⊔ ℓⱽ) (ℓⁱ ⊔ ℓᵀ) ℓˢ
-- -- -- -- -- -- -- -- --   to-noIndex = go where
-- -- -- -- -- -- -- -- --     go : MGU-noIndex _ _ _
-- -- -- -- -- -- -- -- --     MGU-noIndex.𝓥 go = 𝓥ᵢ
-- -- -- -- -- -- -- -- --     MGU-noIndex.𝓣 go = 𝓣ᵢ
-- -- -- -- -- -- -- -- --     MGU-noIndex._◃_ go 𝓈 𝒯ᵢ with to∃𝓣 𝒯ᵢ | Prelude.graphAt to∃𝓣 𝒯ᵢ | (Prelude.curry $ snd ∘ to∃𝓣 ∘ 𝓈 ∘ to𝓥ᵢ) (fst $ to∃𝓣 𝒯ᵢ) | Prelude.graphAt (Prelude.curry $ snd ∘ to∃𝓣 ∘ 𝓈 ∘ to𝓥ᵢ) (fst $ to∃𝓣 𝒯ᵢ)
-- -- -- -- -- -- -- -- --     … | s , 𝒯s | Prelude.ingraph eq | 𝓈' | Prelude.ingraph refl with cong fst eq
-- -- -- -- -- -- -- -- --     (go MGU-noIndex.◃ 𝓈) 𝒯ᵢ | .(fst (to∃𝓣 𝒯ᵢ)) , 𝒯s | (Prelude.ingraph eq) | _ | (Prelude.ingraph refl) | refl with cong fst eq
-- -- -- -- -- -- -- -- --     (go MGU-noIndex.◃ 𝓈) 𝒯ᵢ | .(fst (to∃𝓣 _ 𝒯ᵢ)) , 𝒯s | (Prelude.ingraph eq) | 𝓈' | (Prelude.ingraph refl) | refl | refl rewrite Prelude.sym eq with _◃_ {_} {{!!}} (λ v → 𝓈' (Prelude.transport 𝓥 (cong fst (sym eq)) {!!})) (snd ∘ to∃𝓣 $ 𝒯ᵢ)
-- -- -- -- -- -- -- -- --     … | dfsf = to𝓣ᵢ (_ , dfsf)
-- -- -- -- -- -- -- -- -- {-
-- -- -- -- -- -- -- -- --     … | s , 𝒯s | Prelude.ingraph eq | 𝓈' | Prelude.ingraph refl with _◃_ {fst (to∃𝓣 𝒯ᵢ)} {s} (λ v → Prelude.transport 𝓣 {!!} (𝓈' (Prelude.transport 𝓥 (cong fst eq) v))) (snd ∘ to∃𝓣 $ 𝒯ᵢ)
-- -- -- -- -- -- -- -- --     … | dd = to𝓣ᵢ (_ , dd)
-- -- -- -- -- -- -- -- -- -}
-- -- -- -- -- -- -- -- --     MGU-noIndex.𝓢 go = {!!}
-- -- -- -- -- -- -- -- --     MGU-noIndex.sub go = {!!}
-- -- -- -- -- -- -- -- --     MGU-noIndex.mgu go = {!!}

-- -- -- -- -- -- -- -- -- -- MGU-addIndex-to-noIndex : ∀ {ℓⁱ ℓⱽ ℓᵀ ℓˢ} →
-- -- -- -- -- -- -- -- -- --                             (m : MGU-addIndex ℓⁱ ℓⱽ ℓᵀ ℓˢ)
-- -- -- -- -- -- -- -- -- --                             (open MGU-addIndex m)
-- -- -- -- -- -- -- -- -- --                             (𝓥ᵢ : Set (ℓⁱ ⊔ ℓⱽ))
-- -- -- -- -- -- -- -- -- --                             (𝓣ᵢ : Set (ℓⁱ ⊔ ℓᵀ))
-- -- -- -- -- -- -- -- -- --                             (to∃𝓥 : 𝓥ᵢ → ∃ 𝓥)
-- -- -- -- -- -- -- -- -- --                             (to∃𝓣 : 𝓣ᵢ → ∃ 𝓣)
-- -- -- -- -- -- -- -- -- --                             (to𝓥ᵢ : ∃ 𝓥 → 𝓥ᵢ)
-- -- -- -- -- -- -- -- -- --                             (to𝓣ᵢ : ∃ 𝓣 → 𝓣ᵢ)
-- -- -- -- -- -- -- -- -- --                             (iso∃𝓥 : (∃𝒱 : ∃ 𝓥) → to∃𝓥 (to𝓥ᵢ ∃𝒱) ≡ ∃𝒱)
-- -- -- -- -- -- -- -- -- --                             (iso𝓥ᵢ : (𝒱ᵢ : 𝓥ᵢ) → to𝓥ᵢ (to∃𝓥 𝒱ᵢ) ≡ 𝒱ᵢ)
-- -- -- -- -- -- -- -- -- --                             (iso∃𝓣 : (∃𝒯 : ∃ 𝓣) → to∃𝓣 (to𝓣ᵢ ∃𝒯) ≡ ∃𝒯)
-- -- -- -- -- -- -- -- -- --                             (iso𝓣ᵢ : (𝒯ᵢ : 𝓣ᵢ) → to𝓣ᵢ (to∃𝓣 𝒯ᵢ) ≡ 𝒯ᵢ)
-- -- -- -- -- -- -- -- -- --                             → MGU-noIndex (ℓⁱ ⊔ ℓⱽ) (ℓⁱ ⊔ ℓᵀ) ℓˢ
-- -- -- -- -- -- -- -- -- -- MGU-addIndex-to-noIndex {ℓⁱ} {ℓⱽ} {ℓᵀ} {ℓˢ} m = go where
-- -- -- -- -- -- -- -- -- --   open MGU-addIndex m
-- -- -- -- -- -- -- -- -- --   go : MGU-noIndex _ _ _
-- -- -- -- -- -- -- -- -- --   go = {!!}

-- -- -- -- -- -- -- -- -- -- --  open import Relation.Binary.HeterogeneousEquality.Core as H using (_≅_ ; ≅-to-≡)
-- -- -- -- -- -- -- -- -- -- {-
-- -- -- -- -- -- -- -- -- --   𝓼 = ∃ 𝓥 → ∃ 𝓣

-- -- -- -- -- -- -- -- -- --   _∶_↦_ : 𝓼 → 𝓲 → 𝓲 → Set (ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- --   _∶_↦_ 𝓈 s t = ((𝒱ₛ : 𝓥 s) → ∃ λ (𝒯ₜ : 𝓣 t) → 𝓈 (s , 𝒱ₛ) ≡ (t , 𝒯ₜ))
-- -- -- -- -- -- -- -- -- -- -}
-- -- -- -- -- -- -- -- -- -- --   go : MGU-noIndex _ _ _
-- -- -- -- -- -- -- -- -- -- --   MGU-noIndex.𝓥 go = ∃ 𝓥
-- -- -- -- -- -- -- -- -- -- --   MGU-noIndex.𝓣 go = ∃ 𝓣
-- -- -- -- -- -- -- -- -- -- --   MGU-noIndex._≐_ go f g = (𝒾 : 𝓲) (𝒱 : 𝓥 𝒾) → f (𝒾 , 𝒱) ≡ g (𝒾 , 𝒱)
-- -- -- -- -- -- -- -- -- -- --   MGU-noIndex._◃_ go 𝓈 (𝒾 , 𝒯) = 𝒾 , _◃_ {𝒾} {𝒾} (λ v → (Prelude.transport _ (≅-to-≡ $ snd (mag (fst (𝓈 (𝒾 , v))) (( (𝒾 ))) (snd (𝓈 (𝒾 , v))))) $
-- -- -- -- -- -- -- -- -- -- --                                                            snd (𝓈 (𝒾 , v)))
-- -- -- -- -- -- -- -- -- -- --                                                           {!!}) {!!}
-- -- -- -- -- -- -- -- -- -- --   -- (snd ∘ (λ (𝒱 : 𝓥 𝒾) → 𝓈 (𝒾 , 𝒱)))
-- -- -- -- -- -- -- -- -- -- --   -- fst (𝓈 (s , {!!})) , _◃_ {s} {fst (𝓈 (s , _))} (λ v → {!𝓈 (s , v)!}) {!𝒯ₛ!}
-- -- -- -- -- -- -- -- -- -- --   -- Have: {s t : 𝓲} → (𝓥 s → 𝓣 t) → 𝓣 s → 𝓣 t
-- -- -- -- -- -- -- -- -- -- --   -- λ (𝒱 : 𝓥 𝒾) → 𝓈 (𝒾 , 𝒱)
-- -- -- -- -- -- -- -- -- -- --   MGU-noIndex.𝓢 go = {!!}
-- -- -- -- -- -- -- -- -- -- --   MGU-noIndex.sub go = {!sub {s} {t}!}
-- -- -- -- -- -- -- -- -- -- --   MGU-noIndex.mgu go = {!!}
-- -- -- -- -- -- -- -- -- -- -- {-
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.𝓥 (MGU-addIndex-to-noIndex m) = let open MGU-addIndex m in ∃ 𝓥
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.𝓣 (MGU-addIndex-to-noIndex m) = let open MGU-addIndex m in ∃ 𝓣
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex._≐_ (MGU-addIndex-to-noIndex m) f g = {!let open MGU-addIndex m in ∀ (s t : 𝓲) → (𝒯ₜ : 𝓣 fst (f (s , 𝓥 s)) ≡ t →  ≡ g → Set !} -- ∀ {s t} → (f g : s ↦ t) → f ≐ g
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex._◃_ (MGU-addIndex-to-noIndex m) = {!let open MGU-addIndex m in _◃_!}
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.𝓢 (MGU-addIndex-to-noIndex m) = {!!}
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.sub (MGU-addIndex-to-noIndex m) = {!!}
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.mgu (MGU-addIndex-to-noIndex m) = {!!}
-- -- -- -- -- -- -- -- -- -- -- -}
-- -- -- -- -- -- -- -- -- -- -- {-
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.𝓥 (MGU-addIndex-to-noIndex record { 𝓲 = 𝓲 ; 𝓥 = 𝓥 ; 𝓣 = 𝓣 ; _◃_ = _◃_ ; ◃ext = ◃ext ; 𝓢 = 𝓢 ; sub = sub ; mgu = mgu }) = ∃ 𝓥
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.𝓣 (MGU-addIndex-to-noIndex record { 𝓲 = 𝓲 ; 𝓥 = 𝓥 ; 𝓣 = 𝓣 ; _◃_ = _◃_ ; ◃ext = ◃ext ; 𝓢 = 𝓢 ; sub = sub ; mgu = mgu }) = ∃ 𝓣
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex._≐_ (MGU-addIndex-to-noIndex m@(record { 𝓲 = 𝓲 ; 𝓥 = 𝓥 ; 𝓣 = 𝓣 ; _◃_ = _◃_ ; ◃ext = ◃ext ; 𝓢 = 𝓢 ; sub = sub ; mgu = mgu })) f g = {!!} -- (i : 𝓲) → MGU-addIndex._≐_ m (λ v → {!snd $ f (i , v)!}) (λ v → {!!})
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex._◃_ (MGU-addIndex-to-noIndex record { 𝓲 = 𝓲 ; 𝓥 = 𝓥 ; 𝓣 = 𝓣 ; _◃_ = _◃_ ; ◃ext = ◃ext ; 𝓢 = 𝓢 ; sub = sub ; mgu = mgu }) = {!!}
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.𝓢 (MGU-addIndex-to-noIndex record { 𝓲 = 𝓲 ; 𝓥 = 𝓥 ; 𝓣 = 𝓣 ; _◃_ = _◃_ ; ◃ext = ◃ext ; 𝓢 = 𝓢 ; sub = sub ; mgu = mgu }) = {!!}
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.sub (MGU-addIndex-to-noIndex record { 𝓲 = 𝓲 ; 𝓥 = 𝓥 ; 𝓣 = 𝓣 ; _◃_ = _◃_ ; ◃ext = ◃ext ; 𝓢 = 𝓢 ; sub = sub ; mgu = mgu }) = {!!}
-- -- -- -- -- -- -- -- -- -- -- MGU-noIndex.mgu (MGU-addIndex-to-noIndex record { 𝓲 = 𝓲 ; 𝓥 = 𝓥 ; 𝓣 = 𝓣 ; _◃_ = _◃_ ; ◃ext = ◃ext ; 𝓢 = 𝓢 ; sub = sub ; mgu = mgu }) = {!!}
-- -- -- -- -- -- -- -- -- -- -- -}

-- -- -- -- -- -- -- -- -- -- -- record MGU⋆ ℓⁱ ℓⱽ ℓᵀ ℓˢ : Set (lsuc (ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ ⊔ ℓˢ)) where
-- -- -- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- -- -- --     𝓲
-- -- -- -- -- -- -- -- -- -- --       : Set ℓⁱ

-- -- -- -- -- -- -- -- -- -- --     𝓥 -- variables
-- -- -- -- -- -- -- -- -- -- --       : 𝓲 → Set ℓⱽ

-- -- -- -- -- -- -- -- -- -- --     𝓣 -- terms
-- -- -- -- -- -- -- -- -- -- --       : 𝓲 → Set ℓᵀ

-- -- -- -- -- -- -- -- -- -- --   -- substitution
-- -- -- -- -- -- -- -- -- -- --   _↦_ : (s t : 𝓲) → Set (ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   _↦_ s t = 𝓥 s → 𝓣 t

-- -- -- -- -- -- -- -- -- -- --   infix 14 _≐_
-- -- -- -- -- -- -- -- -- -- --   _≐_ : {s t : 𝓲} → s ↦ t → s ↦ t → Set (ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   _≐_ f g = ∀ x → f x ≡ g x

-- -- -- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- -- -- --     -- substitution applied to a term
-- -- -- -- -- -- -- -- -- -- --     _◃_ : ∀ {s t} → s ↦ t → 𝓣 s → 𝓣 t
-- -- -- -- -- -- -- -- -- -- --     -- applying extentionally-equal subsitutions preserves equality of terms
-- -- -- -- -- -- -- -- -- -- --     ◃ext : ∀ {s t} {f g : s ↦ t} → f ≐ g → ∀ t → f ◃ t ≡ g ◃ t

-- -- -- -- -- -- -- -- -- -- --   _◇_ : ∀ {r s t : 𝓲} → (f : s ↦ t) (g : r ↦ s) → r ↦ t
-- -- -- -- -- -- -- -- -- -- --   _◇_ f g = (f ◃_) ∘ g

-- -- -- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- -- -- --     𝓢 : 𝓲 → 𝓲 → Set ℓˢ
-- -- -- -- -- -- -- -- -- -- --     sub : ∀ {s t} → 𝓢 s t → s ↦ t

-- -- -- -- -- -- -- -- -- -- --   Property : ∀ {ℓ} → 𝓲 → Set (lsuc ℓ ⊔ ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   Property {ℓ} s = ∀ {t} → s ↦ t → Set ℓ

-- -- -- -- -- -- -- -- -- -- --   Nothing : ∀ {ℓ m} → (P : Property {ℓ} m) → Set (ℓ ⊔ ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   Nothing P = ∀ {n} f → P {n} f → ⊥

-- -- -- -- -- -- -- -- -- -- --   Unifies : ∀ {i} (t₁ t₂ : 𝓣 i) → Property i
-- -- -- -- -- -- -- -- -- -- --   Unifies t₁ t₂ f = f ◃ t₁ ≡ f ◃ t₂

-- -- -- -- -- -- -- -- -- -- --   _≤_ : ∀ {m n n'} (f : m ↦ n) (g : m ↦ n') → Set (ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   _≤_ f g = ∃ λ f' → f ≐ (f' ◇ g)

-- -- -- -- -- -- -- -- -- -- --   Max : ∀ {ℓ m} (P : Property {ℓ} m) → Property m
-- -- -- -- -- -- -- -- -- -- --   Max P = (λ f → P f × (∀ {n} f' → P {n} f' → f' ≤ f))

-- -- -- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- -- -- --     mgu : ∀ {m} (t₁ t₂ : 𝓣 m) →
-- -- -- -- -- -- -- -- -- -- --           Nothing (Unifies t₁ t₂) ⊎ (∃ λ n → ∃ λ (σ : 𝓢 m n) → (Max (Unifies t₁ t₂)) (sub σ))

-- -- -- -- -- -- -- -- -- -- -- record MGU ℓⁱ ℓⱽ ℓᵀ ℓˢ : Set (lsuc (ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ ⊔ ℓˢ)) where
-- -- -- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- -- -- --     𝓲
-- -- -- -- -- -- -- -- -- -- --       : Set ℓⁱ

-- -- -- -- -- -- -- -- -- -- --     𝓥 -- variables
-- -- -- -- -- -- -- -- -- -- --       : 𝓲 → Set ℓⱽ

-- -- -- -- -- -- -- -- -- -- --     𝓣 -- terms
-- -- -- -- -- -- -- -- -- -- --       : 𝓲 → Set ℓᵀ

-- -- -- -- -- -- -- -- -- -- --   -- substitution
-- -- -- -- -- -- -- -- -- -- --   _↦_ : (s t : 𝓲) → Set (ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   _↦_ s t = 𝓥 s → 𝓣 t

-- -- -- -- -- -- -- -- -- -- --   infix 14 _≐_
-- -- -- -- -- -- -- -- -- -- --   _≐_ : {s t : 𝓲} → s ↦ t → s ↦ t → Set (ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   _≐_ f g = ∀ x → f x ≡ g x

-- -- -- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- -- -- --     -- substitution applied to a term
-- -- -- -- -- -- -- -- -- -- --     _◃_ : ∀ {s t} → s ↦ t → 𝓣 s → 𝓣 t
-- -- -- -- -- -- -- -- -- -- --     -- applying extentionally-equal subsitutions preserves equality of terms
-- -- -- -- -- -- -- -- -- -- --     ◃ext : ∀ {s t} {f g : s ↦ t} → f ≐ g → ∀ t → f ◃ t ≡ g ◃ t

-- -- -- -- -- -- -- -- -- -- --   _◇_ : ∀ {r s t : 𝓲} → (f : s ↦ t) (g : r ↦ s) → r ↦ t
-- -- -- -- -- -- -- -- -- -- --   _◇_ f g = (f ◃_) ∘ g

-- -- -- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- -- -- --     𝓢 : 𝓲 → 𝓲 → Set ℓˢ
-- -- -- -- -- -- -- -- -- -- --     sub : ∀ {s t} → 𝓢 s t → s ↦ t
-- -- -- -- -- -- -- -- -- -- --     mgu : ∀ {m} → (s t : 𝓣 m) → Maybe (∃ (𝓢 m))

-- -- -- -- -- -- -- -- -- -- --   Property⋆ : ∀ {ℓ} → 𝓲 → Set (lsuc ℓ ⊔ ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   Property⋆ {ℓ} s = ∀ {t} → s ↦ t → Set ℓ

-- -- -- -- -- -- -- -- -- -- --   Property : ∀ {ℓ} → 𝓲 → Set (lsuc ℓ ⊔ ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   Property {ℓ} s = Σ (Property⋆ {ℓ} s) λ P → ∀ {s f g} → f ≐ g → P {s} f → P g

-- -- -- -- -- -- -- -- -- -- --   Nothing : ∀ {ℓ m} → (P : Property {ℓ} m) → Set (ℓ ⊔ ℓⁱ ⊔ ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   Nothing P = ∀ {n} f → fst P {n} f → ⊥

-- -- -- -- -- -- -- -- -- -- --   Unifies : ∀ {i} (t₁ t₂ : 𝓣 i) → Property i
-- -- -- -- -- -- -- -- -- -- --   Unifies t₁ t₂ = (λ {_} f → f ◃ t₁ ≡ f ◃ t₂) , λ {_} {f} {g} f≐g f◃t₁=f◃t₂ →
-- -- -- -- -- -- -- -- -- -- --       g ◃ t₁
-- -- -- -- -- -- -- -- -- -- --     ≡⟨ sym (◃ext f≐g t₁) ⟩
-- -- -- -- -- -- -- -- -- -- --       f ◃ t₁
-- -- -- -- -- -- -- -- -- -- --     ≡⟨ f◃t₁=f◃t₂ ⟩
-- -- -- -- -- -- -- -- -- -- --       f ◃ t₂
-- -- -- -- -- -- -- -- -- -- --     ≡⟨ ◃ext f≐g t₂ ⟩
-- -- -- -- -- -- -- -- -- -- --       g ◃ t₂
-- -- -- -- -- -- -- -- -- -- --     ∎

-- -- -- -- -- -- -- -- -- -- --   _≤_ : ∀ {m n n'} (f : m ↦ n) (g : m ↦ n') → Set (ℓⱽ ⊔ ℓᵀ)
-- -- -- -- -- -- -- -- -- -- --   _≤_ f g = ∃ λ f' → f ≐ (f' ◇ g)

-- -- -- -- -- -- -- -- -- -- -- {-
-- -- -- -- -- -- -- -- -- -- --   Max : ∀ {ℓ m} (P : Property {ℓ} m) → Property m
-- -- -- -- -- -- -- -- -- -- --   Max P' =
-- -- -- -- -- -- -- -- -- -- --     let open Σ P' using () renaming (fst to P; snd to Peq) in
-- -- -- -- -- -- -- -- -- -- --     let lemma1 : {m : 𝓲} {f : _ ↦ m} {g : _ ↦ m} →
-- -- -- -- -- -- -- -- -- -- --                  f ≐ g →
-- -- -- -- -- -- -- -- -- -- --                  P f × ({n : 𝓲} (f' : _ ↦ n) → P f' → f' ≤ f) →
-- -- -- -- -- -- -- -- -- -- --                  P g × ({n : 𝓲} (f' : _ ↦ n) → P f' → f' ≤ g)
-- -- -- -- -- -- -- -- -- -- --         lemma1 {_} {_} {g} f≐g = λ { (Pf , MaxPf) →
-- -- -- -- -- -- -- -- -- -- --           Peq f≐g Pf , λ {_} →
-- -- -- -- -- -- -- -- -- -- --             let lemma2 : ∀ {n} f' → P {n} f' → ∃ λ f0 → f' ≐ (f0 ◇ g)
-- -- -- -- -- -- -- -- -- -- --                 lemma2 f' Pf' =
-- -- -- -- -- -- -- -- -- -- --                   let f0 = fst (MaxPf f' Pf')
-- -- -- -- -- -- -- -- -- -- --                       f'≐f0◇f = snd (MaxPf f' Pf') in
-- -- -- -- -- -- -- -- -- -- --                   f0 , λ x → trans (f'≐f0◇f x) (cong (f0 ◃_) (f≐g x)) in
-- -- -- -- -- -- -- -- -- -- --             lemma2 } in
-- -- -- -- -- -- -- -- -- -- --     --(λ {_} f → P f × (∀ {n} f' → P {n} f' → f' ≤ f)) , λ {_} {_} {_} → lemma1
-- -- -- -- -- -- -- -- -- -- --     (λ f → P f × (∀ {n} f' → P {n} f' → f' ≤ f)) , lemma1
-- -- -- -- -- -- -- -- -- -- -- -}

-- -- -- -- -- -- -- -- -- -- --   Max : ∀ {ℓ m} (P : Property {ℓ} m) → Property⋆ m
-- -- -- -- -- -- -- -- -- -- --   Max P' =
-- -- -- -- -- -- -- -- -- -- --     let open Σ P' using () renaming (fst to P) in
-- -- -- -- -- -- -- -- -- -- --     (λ f → P f × (∀ {n} f' → P {n} f' → f' ≤ f))

-- -- -- -- -- -- -- -- -- -- --   field
-- -- -- -- -- -- -- -- -- -- -- {-
-- -- -- -- -- -- -- -- -- -- --     mgu-c : ∀ {m} (t₁ t₂ : 𝓣 m) →
-- -- -- -- -- -- -- -- -- -- --             (∃ λ n → ∃ λ σ → fst (Max (Unifies t₁ t₂)) (sub σ) × mgu t₁ t₂ ≡ just (n , σ))
-- -- -- -- -- -- -- -- -- -- --             ⊎ (Nothing (Unifies t₁ t₂)                         × mgu t₁ t₂ ≡ nothing)

-- -- -- -- -- -- -- -- -- -- -- -}
-- -- -- -- -- -- -- -- -- -- --     mgu-c : ∀ {m} (t₁ t₂ : 𝓣 m) →
-- -- -- -- -- -- -- -- -- -- --             (∃ λ n → ∃ λ σ → (Max (Unifies t₁ t₂)) (sub σ) × mgu t₁ t₂ ≡ just (n , σ))
-- -- -- -- -- -- -- -- -- -- --             ⊎ (Nothing (Unifies t₁ t₂)                     × mgu t₁ t₂ ≡ nothing)

-- -- -- -- -- -- -- -- -- -- -- {-
-- -- -- -- -- -- -- -- -- -- --     -- trivial substitution
-- -- -- -- -- -- -- -- -- -- --     -- ▹ : ∀ {s t} → (𝓥 s → 𝓥 t) → s ↦ t

-- -- -- -- -- -- -- -- -- -- --   ≐-cong : ∀ {m n o} {f : m ↦ n} {g} (h : _ ↦ o) → f ≐ g → (h ◇ f) ≐ (h ◇ g)
-- -- -- -- -- -- -- -- -- -- --   ≐-cong h f≐g t = cong (h ◃_) (f≐g t)

-- -- -- -- -- -- -- -- -- -- --   ≐-sym : ∀ {m n} {f : m ↦ n} {g} → f ≐ g → g ≐ f
-- -- -- -- -- -- -- -- -- -- --   ≐-sym f≐g = sym ∘ f≐g
-- -- -- -- -- -- -- -- -- -- -- -}


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

module Interval where

open import Pitch

open import Data.Bool       using (Bool; true; false; _∨_; _∧_; not; if_then_else_)
open import Data.Integer    using (+_; _-_; sign; ∣_∣)
open import Data.Fin        using (toℕ)
open import Data.Nat        using (ℕ; _≡ᵇ_)
open import Data.Nat.DivMod using (_mod_)
open import Data.Product    using (_×_; _,_; Σ; proj₁; proj₂)
open import Data.Sign       using (Sign)

open import Function        using (_∘_)

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

PitchPair : Set
PitchPair = Pitch × Pitch

data Interval : Set where
  interval : ℕ → Interval

infix 4 _==_

_==_ : Interval → Interval → Bool
(interval a) == (interval b) = a ≡ᵇ b

intervalWithinOctave : Interval → Interval
intervalWithinOctave (interval i) = interval (toℕ (i mod chromaticScaleSize))

SignedInterval : Set
SignedInterval = Sign × Interval

-- Names for intervals
per1  = interval 0
min2  = interval 1
maj2  = interval 2
min3  = interval 3
maj3  = interval 4
per4  = interval 5
aug4  = interval 6
per5  = interval 7
min6  = interval 8
maj6  = interval 9
min7  = interval 10
maj7  = interval 11
per8  = interval 12
min9  = interval 13
maj9  = interval 14
min10 = interval 15
maj10 = interval 16

isConsonant : Interval → Bool
isConsonant iv =
  (i == per1)  ∨
  (i == min3)  ∨
  (i == maj3)  ∨
  (i == per5)  ∨
  (i == min6)  ∨
  (i == maj6)  ∨
  (i == per8)
  where i = intervalWithinOctave iv

isDissonant : Interval → Bool
isDissonant = not ∘ isConsonant

isPerfect : Interval → Bool
isPerfect iv =
  (i == per1)  ∨
  (i == per4)  ∨
  (i == per5)  ∨
  (i == per8)
  where i = intervalWithinOctave iv

isUnison : Interval → Bool
isUnison i = i == per1

isThird : Interval → Bool
isThird i = (i == min3) ∨ (i == maj3)

-- Half or whole step; ignores key for now.
isStep : Interval → Bool
isStep i =
  (i == min2)  ∨
  (i == maj2)

PitchInterval : Set
PitchInterval = Pitch × Interval

pitchIntervalToPitchPair : PitchInterval → PitchPair
pitchIntervalToPitchPair (p , interval n) = (p , transposePitch (+ n)  p)

secondPitch : PitchInterval → Pitch
secondPitch = proj₂ ∘ pitchIntervalToPitchPair

pitchPairToSignedInterval : PitchPair → SignedInterval
pitchPairToSignedInterval (pitch p , pitch q) =
  let d = (+ q) - (+ p)
  in sign d , interval ∣ d ∣

-- Assumes p ≤ q
pitchPairToPitchInterval : PitchPair → PitchInterval
pitchPairToPitchInterval pq = proj₁ pq , proj₂ (pitchPairToSignedInterval pq)

stepUp : Pitch → Pitch → Bool
stepUp p q with pitchPairToSignedInterval (p , q)
... | Sign.- , _ = false
... | Sign.+ , i = isStep i

stepDown : Pitch → Pitch → Bool
stepDown p q with pitchPairToSignedInterval (p , q)
... | Sign.- , i = isStep i
... | Sign.+ , _ = false

-- Check if q is a passing tone between p and r
-- The interval between end points need to be a 3rd
isPassingTone : Pitch → Pitch → Pitch → Bool
isPassingTone p q r =
  (stepUp p q ∧ stepUp q r) ∨ (stepDown p q ∧ stepDown q r) ∨
  (isThird (proj₂ (pitchPairToSignedInterval (p , r))))

moveUp : Pitch → Pitch → Bool
moveUp p q with pitchPairToSignedInterval (p , q)
... | Sign.- , _ = false
... | Sign.+ , i = true

moveDown : Pitch → Pitch → Bool
moveDown p q with pitchPairToSignedInterval (p , q)
... | Sign.- , i = true
... | Sign.+ , _ = false

-- Check if q is left by step in the opposite direction from its approach
isOppositeStep : Pitch → Pitch → Pitch → Bool
isOppositeStep p q r = (moveUp p q ∧ stepDown q r) ∨ (moveDown p q ∧ stepUp q r)


open import OutsideIn.Prelude
open import OutsideIn.X
module OutsideIn.Proof.Soundness(x : X) where
  open import Data.Vec hiding (map; _>>=_)

  open X(x)
  import OutsideIn.Environments as EV
  import OutsideIn.Expressions as E
  import OutsideIn.TypeSchema as TS
  import OutsideIn.TopLevel as TL
  import OutsideIn.Constraints as CN
  import OutsideIn.Inference as I
  import OutsideIn.Inference.Separator as S
  import OutsideIn.Inference.Prenexer as P
  import OutsideIn.Inference.ConstraintGen as CG
  import OutsideIn.Inference.Solver as SL
  open EV(x)
  open E(x)
  open CN(x)
  open TS(x)
  open TL(x)
  open I(x)
  open CG(x)
  open S(x)
  open SL(x)
  open P(x)
  open import Relation.Binary.PropositionalEquality renaming ([_] to inspectC)
  module Ax-f = Functor(axiomscheme-is-functor)
  module QC-f = Functor(qconstraint-is-functor)
  module Exp-f {r}{s} = Functor(expression-is-functor₂ {r}{s})
  module TS-f {n} = Functor(type-schema-is-functor {n})
  module pn-m {n} = Monad(PlusN-is-monad{n})
  module Vec-f {n} = Functor(vec-is-functor {n})

  open Monad(type-is-monad)
  open Functor(is-functor)

  type-substitute : ∀{a b} → (a → Type b) → Type a → Type b
  type-substitute f t = (join ∘ map f) t

  applyAll : ∀{tv}(n : ℕ) → Type tv → Type (tv ⨁ n)
  applyAll zero x = x
  applyAll (suc n) x = applyAll n (appType (map suc x) (unit zero))

  constraint-substitute : ∀{a b} → (a → Type b) → QConstraint a → QConstraint b
  constraint-substitute f t = constraint-types (type-substitute f) t

  mutual
    data _,_,_⊢_∶_ {ev tv : Set}(Q : AxiomScheme tv)(Qg : QConstraint tv)(Γ : Environment ev tv): {r : Shape} → Expression ev tv r →  Type tv → Set where
      TyEq : ∀ {τ₁ τ₂}{r}{e : Expression ev tv r}
           → Q , Qg , Γ ⊢ e ∶ τ₁
           → Q , Qg ⊩ (τ₁ ∼ τ₂)
           → Q , Qg , Γ ⊢ e ∶ τ₂
      Abst : ∀ {τ₁ τ₂}{r}{e : Expression (Ⓢ ev) tv r}
           → Q , Qg , (⟨ ∀′ 0 · ε ⇒ τ₁ ⟩, Γ) ⊢ e ∶ τ₂
           → Q , Qg , Γ ⊢ λ′ e ∶ funType τ₁ τ₂
      Appl : ∀ {τ₁ τ₂}{r₁ r₂}{e₁ : Expression ev tv r₁}{e₂ : Expression ev tv r₂}
           → Q , Qg , Γ ⊢ e₁ ∶ funType τ₁ τ₂
           → Q , Qg , Γ ⊢ e₂ ∶ τ₁
           → Q , Qg , Γ ⊢ (e₁ · e₂) ∶ τ₂
      Let1 : ∀ {τ₁ τ₂}{r₁ r₂}{e₁ : Expression ev tv r₁}{e₂ : Expression (Ⓢ ev) tv r₂}
           → Q , Qg , Γ ⊢ e₁ ∶ τ₁
           → Q , Qg , (⟨ ∀′ 0 · ε ⇒ τ₁ ⟩, Γ) ⊢ e₂ ∶ τ₂
           → Q , Qg , Γ ⊢ (let₁ e₁ in′ e₂) ∶ τ₂
      Let2 : ∀ {τ₁ τ₂}{r₁ r₂}{e₁ : Expression ev tv r₁}{e₂ : Expression (Ⓢ ev) tv r₂}
           → Q , Qg , Γ ⊢ e₁ ∶ τ₁
           → Q , Qg , (⟨ ∀′ 0 · ε ⇒ τ₁ ⟩, Γ) ⊢ e₂ ∶ τ₂
           → Q , Qg , Γ ⊢ (let₂ e₁ ∷ τ₁ in′ e₂) ∶ τ₂
      Let3 : ∀ {n}{τ₁ τ₂}{r₁ r₂}{Qv}{e₁ : Expression ev (tv ⨁ n) r₁}{e₂ : Expression (Ⓢ ev) tv r₂}
           → Ax-f.map (pn-m.unit {n}) Q , QC-f.map (pn-m.unit {n}) Qg ∧ Qv , TS-f.map (pn-m.unit {n}) ∘ Γ ⊢ e₁ ∶ τ₁
           → Q , Qg , (⟨ ∀′ n · Qv ⇒ τ₁ ⟩, Γ) ⊢ e₂ ∶ τ₂
           → Q , Qg , Γ ⊢ (let₃ n · e₁ ∷ Qv ⇒ τ₁ in′ e₂) ∶ τ₂
      VarN : ∀ {n}{v}{τ}{Qv}
           → (θ : (tv ⨁ n) → Type tv)
           → Γ v ≡ ∀′ n · Qv ⇒ τ
           → Q , Qg ⊩ constraint-substitute θ Qv
           → Q , Qg , Γ ⊢ (Var {_}{_}{Regular} v) ∶ (τ >>= θ )
      DCn1 : ∀ {n}{l}{v}{K}{args}
           → (θ : (tv ⨁ n) → Type tv)
           → Γ v ≡ (DC∀ n · args ⟶ K)
           → Q , Qg , Γ ⊢ (Var {_}{_}{Datacon l} v) ∶ (applyAll n (unit K) >>= θ)
      DCn2 : ∀ {a b}{l}{v}{K}{args}{Qv}
           → (θ : (tv ⨁ a) ⨁ b → Type tv)
           → Γ v ≡ (DC∀′ a , b · Qv ⇒ args ⟶ K)
           → Q , Qg ⊩ constraint-substitute θ Qv
           → Q , Qg , Γ ⊢ (Var {_}{_}{Datacon l} v) ∶ (map (pn-m.unit {b}) (applyAll a (unit K)) >>= θ)
      Case : ∀ {τ}{r r′}{e : Expression ev tv r}{alts : Alternatives ev tv r′} {n : ℕ}
           → (T : tv)
           → (θ : (tv ⨁ n) → Type tv)
           → Q , Qg , Γ ⊢ e ∶ (applyAll n (unit T) >>= θ)
           → AltsType {n = n} Q Qg Γ θ T alts τ
           → Q , Qg , Γ ⊢ case e of alts ∶ τ

    data AltsType {ev tv : Set}{n : ℕ}(Q : AxiomScheme tv)(Qg : QConstraint tv)(Γ : Environment ev tv)( θ : (tv ⨁ n) → Type tv)(T : tv)
                : {r : Shape} → Alternatives ev tv r → Type tv → Set where
       NoAlts : ∀ {τ} → AltsType Q Qg Γ θ T esac τ
       OneAlt : ∀ {τ}{a}{r₁ r₂}{p : Name ev (Datacon a)}{e : Expression (ev ⨁ a) tv r₁}{as : Alternatives ev tv r₂}{vs}
              → AltsType {n = n} Q Qg Γ θ T as τ
              → Γ p ≡ DC∀ n · vs ⟶ T
              → Q , Qg , addAll (Vec-f.map (λ x → x >>= θ) vs) Γ ⊢ e ∶ τ
              → AltsType Q Qg Γ θ T ((p →′ e) ∣ as) τ
       GATAlt : ∀ {x}{τ}{a}{r₁ r₂}{p : Name ev (Datacon a)}{e : Expression (ev ⨁ a) tv r₁}{as : Alternatives ev tv r₂}{vs}{Qv}
              → let θ′ :  PlusN x (PlusN n tv) → Type (PlusN x tv)
                    θ′ v = sequence-PlusN {Type}{x} ⦃ type-is-monad ⦄ (Functor.map (pn-m.is-functor {x}) θ v)
                    Q′  = Ax-f.map (pn-m.unit {x}) Q
                    Qg′ = QC-f.map (pn-m.unit {x}) Qg
             in AltsType {n = n} Q Qg Γ θ T as τ
              → Γ p ≡ DC∀′ n , x · Qv ⇒ vs ⟶ T
              → Q′ , Qg′ ∧ (constraint-substitute θ′ Qv) ,  addAll (Vec-f.map (λ x → x >>= θ′) vs) (TS-f.map (pn-m.unit {x}) ∘ Γ)
                ⊢ Exp-f.map (pn-m.unit {x}) e ∶ map (pn-m.unit {x}) τ
              → AltsType Q Qg Γ θ T ((p →′ e) ∣ as) τ

  data _,_,_⊢′_ {ev tv : Set}(Q : AxiomScheme tv)(Qg : QConstraint tv)(Γ : Environment ev tv):  Program ev tv → Set where
    Empty : Q , Qg , Γ ⊢′ end
    Bind : {r : Shape}{n : ℕ}{e : Expression ev tv r}{p : Program (Ⓢ ev) tv}{Qv Q₁ : QConstraint (tv ⨁ n)}{τ : Type (tv ⨁ n)}
          → let Q′  = Ax-f.map (pn-m.unit {n}) Q
                Qg′ = QC-f.map (pn-m.unit {n}) Qg
                e′  = Exp-f.map (pn-m.unit {n}) e
         in Q′ , Qv ∧ Qg′ ⊩ Q₁
          → Q′ , Q₁ , (TS-f.map (pn-m.unit {n}) ∘ Γ) ⊢ e′ ∶ τ
          → Q , Qg , (⟨ ∀′ n · Qv ⇒ τ  ⟩, Γ) ⊢′ p
          → Q , Qg , Γ ⊢′ bind₁ e , p
    BindA : {r : Shape}{n : ℕ}{e : Expression ev (tv ⨁ n) r}{p : Program (Ⓢ ev) tv}{Qv Q₁ : QConstraint (tv ⨁ n)}{τ : Type (tv ⨁ n)}
          → let Q′  = Ax-f.map (pn-m.unit {n}) Q
                Qg′ = QC-f.map (pn-m.unit {n}) Qg
         in Q′ , Qv ∧ Qg′ ⊩ Q₁
          → Q′ , Q₁ , (TS-f.map (pn-m.unit {n}) ∘ Γ) ⊢ e ∶ τ
          → Q , Qg , (⟨ ∀′ n · Qv ⇒ τ ⟩, Γ) ⊢′ p
          → Q , Qg , Γ ⊢′ bind₂ n · e ∷ Qv ⇒ τ , p

  open import Data.Empty

  -- I know that Γ (N v) == ∀′ α · q₁ ⇒ τ₁
  -- I know that gen≡

  soundness-lemma : ∀{r}{n}{ev}{tv}{Γ : Environment ev tv}{e : Expression ev tv r}{r′}{τ}{C}{C′}{C′′}{n′}{Q}{Qg}{Qr}{θ}{eq : Eq (tv ⨁ n)}{Cext}
                  → Γ ► e ∶ τ ↝ C → C prenex: n , C′ → C′ separate: r′ , C′′ →  Q , Qg , n solv► C′′ ↝ n′ , Qr , θ
                  →  let Q′  = Ax-f.map (pn-m.unit {n′}) Q
                         Qg′ = QC-f.map (pn-m.unit {n′}) Qg
                         e′  = Exp-f.map (pn-m.unit {n′}) e
                      in ∃ (λ Qe → (Q′ , Qe , TS-f.map (pn-m.unit {n′}) ∘ Γ ⊢ e′ ∶ map (pn-m.unit {n′}) τ)
                            × Q′ , (Qg′ ∧ Qr) ⊩ Qe  )
{-  soundness-lemma (VarCon₁ Γv≡∀n·q⇒t) pnx sep sol = {!_ , VarN ? ? ? , ?!} -}
{-  soundness-lemma (App {C₁ = C₁}{C₂} Pe₁ Pe₂) pnx sep sol with prenex (C₁ ∧′ C₂) | prenex (C₂ ∧′ C₁)
  ... | f₁ , C₁′  , p₁  | f₂ , C₂′  , p₂  with separate C₁′ | separate C₂′
  ... | r₁ , C₁′′ , p₁′ | r₂ , C₂′′ , p₂′ with soundness-lemma Pe₁ p₁ p₁′ {!!} | soundness-lemma Pe₂ p₂ p₂′ {!!}
  ... | Q₁ , P₁   , P₁′ | Q₂ , P₂ , P₂′  = Q₁ ∧ (Q₂ ∧ {!!}) , Appl P₁ P₂ , {!!}  -}
  soundness-lemma {n = suc (suc .(na + nb))} {e = λ′ e′} {τ = τ}
                  (Abs {C = Ⅎ Ⅎ (C ∧′ rest)} p)
                  (PN-Ext (PN-Ext (PN-∧ {._}{._}{na}{nb} p₁ p₂)))
                  (Separate (Simpl-∧ p₁s p₂s) (Implic-∧ p₁i p₂i)) (SOLVE simpl impls) with soundness-lemma {e = Exp-f.map (suc ∘ suc) e′} p p₁ (Separate {!p₁s!} {!!}) {!!}
  ... | Q₁ , t , e = {!!}
  soundness-lemma (_) pnx sep sol = {!!}

{-
  soundness-proof : ∀ {ev}{tv}{eq : Eq tv}{Q}{Γ : Environment ev tv}{p} → Q , Γ ► p →  Q , ε , Γ ⊢′ p
  soundness-proof (Empty) = Empty
  soundness-proof (Bind _ _ _ _ _) = Bind {!!} {!!} {!!}
  soundness-proof (BindA _ _ _ _ _) = BindA {!!} {!!} {!!}
-}


{-# OPTIONS --safe #-}

module Definition.Conversion.Whnf where

open import Definition.Untyped
open import Definition.Typed
open import Definition.Conversion

open import Tools.Product

mutual
  -- Extraction of neutrality from algorithmic equality of neutrals.
  ne~↑! : ∀ {t u A Γ l}
       → Γ ⊢ t ~ u ↑! A ^ l
       → Neutral t × Neutral u
  ne~↑! (var-refl x₁ x≡y) = var _ , var _
  ne~↑! (app-cong x x₁) = let _ , q , w = ne~↓! x
                         in  ∘ₙ q , ∘ₙ w
  ne~↑! (natrec-cong x x₁ x₂ x₃) = let _ , q , w = ne~↓! x₃
                                  in  natrecₙ q , natrecₙ w
  ne~↑! (Emptyrec-cong x x₁) = Emptyrecₙ , Emptyrecₙ
  ne~↑! (Id-cong X x x₁) = let _ , nt , nu = ne~↓! X in Idₙ nt , Idₙ nu
  ne~↑! (Id-ℕ X x) = let _ , nt , nu = ne~↓! X in Idℕₙ nt , Idℕₙ nu
  ne~↑! (Id-ℕ0 X) = let _ , nt , nu = ne~↓! X in Idℕ0ₙ nt , Idℕ0ₙ nu
  ne~↑! (Id-ℕS x X) = let _ , nt , nu = ne~↓! X in IdℕSₙ nt , IdℕSₙ nu
  ne~↑! (Id-U X x) = let _ , nt , nu = ne~↓! X in IdUₙ nt , IdUₙ nu
  ne~↑! (Id-Uℕ X) = let _ , nt , nu = ne~↓! X in IdUℕₙ nt , IdUℕₙ nu
  ne~↑! (Id-UΠ x X) = let _ , nt , nu = ne~↓! X in IdUΠₙ nt , IdUΠₙ nu
  ne~↑! (cast-cong X x x₁ x₂ x₃) = let _ , nt , nu = ne~↓! X in castₙ nt , castₙ nu
  ne~↑! (cast-ℕ X x x₁ x₂) = let _ , nt , nu = ne~↓! X in castℕₙ nt , castℕₙ nu
  ne~↑! (cast-ℕℕ X x x₁) = let _ , nt , nu = ne~↓! X in castℕℕₙ nt , castℕℕₙ nu
  ne~↑! (cast-Π x X x₁ x₂ x₃) = let _ , nt , nu = ne~↓! X in castΠₙ nt , castΠₙ nu
  ne~↑! (cast-Πℕ x x₁ x₂ x₃) = castΠℕₙ , castΠℕₙ
  ne~↑! (cast-ℕΠ x x₁ x₂ x₃) = castℕΠₙ , castℕΠₙ
  ne~↑! (cast-ΠΠ%! x x₁ x₂ x₃ x₄) = castΠΠ%!ₙ , castΠΠ%!ₙ
  ne~↑! (cast-ΠΠ!% x x₁ x₂ x₃ x₄) = castΠΠ!%ₙ , castΠΠ!%ₙ

  ne~↓! : ∀ {t u A Γ l}
        → Γ ⊢ t ~ u ↓! A ^ l
        → Whnf A × Neutral t × Neutral u
  ne~↓! ([~] A D whnfB k~l) = whnfB , ne~↑! k~l

-- Extraction of WHNF from algorithmic equality of terms in WHNF.
whnfConv↓Term : ∀ {t u A Γ l}
              → Γ ⊢ t [conv↓] u ∷ A ^ l
              → Whnf A × Whnf t × Whnf u
whnfConv↓Term (ℕ-ins x) = let _ , neT , neU = ne~↓! x
                          in ℕₙ , ne neT , ne neU
-- whnfConv↓Term (Empty-ins x) = let _ , neT , neU = ne~↓% x
--                           in Emptyₙ , ne neT , ne neU
whnfConv↓Term (ne x) = let wA , nt , nu = ne~↓! x in wA , ne nt , ne nu
whnfConv↓Term (ne-ins t u x x₁) =
  let _ , neT , neU = ne~↓! x₁
  in ne x , ne neT , ne neU
whnfConv↓Term (ℕ-refl x) = Uₙ , ℕₙ , ℕₙ
whnfConv↓Term (Empty-refl x x₁) = Uₙ , Emptyₙ , Emptyₙ
whnfConv↓Term (Π-cong _ _ _ _ _ _ x x₁ x₂) = Uₙ , Πₙ , Πₙ
whnfConv↓Term (∃-cong _ x x₁ x₂) = Uₙ , ∃ₙ , ∃ₙ
whnfConv↓Term (U-refl _ _) = Uₙ , Uₙ , Uₙ
whnfConv↓Term (zero-refl x) = ℕₙ , zeroₙ , zeroₙ
whnfConv↓Term (suc-cong x) = ℕₙ , sucₙ , sucₙ
whnfConv↓Term (η-eq _ _ x x₁ x₂ y y₁ x₃) = Πₙ , functionWhnf y , functionWhnf y₁

-- Extraction of WHNF from algorithmic equality of types in WHNF.
whnfConv↓ : ∀ {A B rA Γ}
          → Γ ⊢ A [conv↓] B ^ rA
          → Whnf A × Whnf B
whnfConv↓ (U-refl _ _) = Uₙ , Uₙ
whnfConv↓ (univ x₂) = let _ , A , B = whnfConv↓Term x₂ in A , B


import Parametric.Syntax.Type as Type
import Parametric.Syntax.Term as Term
import Parametric.Syntax.MType as MType

module Parametric.Syntax.MTerm
    {Base : Type.Structure}
    (Const : Term.Structure Base)
  where

open Type.Structure Base
open MType.Structure Base
open Term.Structure Base Const

-- Our extension points are sets of primitives, indexed by the
-- types of their arguments and their return type.

-- We want different types of constants; some produce values, some produce
-- computations. In all cases, arguments are assumed to be values (though
-- individual primitives might require thunks).

ValConstStructure : Set₁
ValConstStructure = ValType → Set

CompConstStructure : Set₁
CompConstStructure = CompType → Set

CbnToCompConstStructure : CompConstStructure → Set
CbnToCompConstStructure CompConst = ∀ {τ} → Const τ → CompConst (cbnToCompType τ)

CbvToCompConstStructure : CompConstStructure → Set
CbvToCompConstStructure CompConst = ∀ {τ} → Const τ → CompConst (cbvToCompType τ)

module
  Structure
    (ValConst : ValConstStructure) (CompConst : CompConstStructure)
    (cbnToCompConst : CbnToCompConstStructure CompConst)
    (cbvToCompConst : CbvToCompConstStructure CompConst) where
  mutual
    -- Analogues of Terms
    Vals : ValContext → ValContext → Set
    Vals Γ = DependentList (Val Γ)

    Comps : ValContext → List CompType → Set
    Comps Γ = DependentList (Comp Γ)

    data Val Γ : (τ : ValType) → Set where
      vVar : ∀ {τ} (x : ValVar Γ τ) → Val Γ τ
      -- XXX Do we need thunks? The draft in the paper doesn't have them.
      -- However, they will start being useful if we deal with CBN source
      -- languages.
      vThunk : ∀ {τ} → Comp Γ τ → Val Γ (U τ)
      vConst : ∀ {τ} →
        (c : ValConst τ) →
        Val Γ τ

    data Comp Γ : (τ : CompType) → Set where
      cConst : ∀ {τ} →
        (c : CompConst τ) →
        Comp Γ τ

      cForce : ∀ {τ} → Val Γ (U τ) → Comp Γ τ
      cReturn : ∀ {τ} (v : Val Γ τ) → Comp Γ (F τ)
      {-
      -- Originally, M to x in N. But here we have no names!
      _into_ : ∀ {σ τ} →
        (e₁ : Comp Γ (F σ)) →
        (e₂ : Comp (σ •• Γ) τ) →
        Comp Γ τ
      -}
      -- The following constructor is the main difference between CBPV and this
      -- monadic calculus. This is better for the caching transformation.
      _into_ : ∀ {σ τ} →
        (e₁ : Comp Γ (F σ)) →
        (e₂ : Comp (σ •• Γ) (F τ)) →
        Comp Γ (F τ)
      cAbs : ∀ {σ τ} →
        (t : Comp (σ •• Γ) τ) →
        Comp Γ (σ ⇛ τ)
      cApp : ∀ {σ τ} →
        (s : Comp Γ (σ ⇛ τ)) →
        (t : Val Γ σ) →
        Comp Γ τ

  weaken-val : ∀ {Γ₁ Γ₂ τ} →
    (Γ₁≼Γ₂ : Γ₁ ≼≼ Γ₂) →
    Val Γ₁ τ →
    Val Γ₂ τ

  weaken-comp : ∀ {Γ₁ Γ₂ τ} →
    (Γ₁≼Γ₂ : Γ₁ ≼≼ Γ₂) →
    Comp Γ₁ τ →
    Comp Γ₂ τ

  weaken-val Γ₁≼Γ₂ (vVar x) = vVar (weaken-val-var Γ₁≼Γ₂ x)
  weaken-val Γ₁≼Γ₂ (vThunk x) = vThunk (weaken-comp Γ₁≼Γ₂ x)
  weaken-val Γ₁≼Γ₂ (vConst c) = vConst c
  weaken-comp Γ₁≼Γ₂ (cConst c) = cConst c
  weaken-comp Γ₁≼Γ₂ (cForce x) = cForce (weaken-val Γ₁≼Γ₂ x)
  weaken-comp Γ₁≼Γ₂ (cReturn v) = cReturn (weaken-val Γ₁≼Γ₂ v)
  weaken-comp Γ₁≼Γ₂ (_into_ {σ} c c₁) = (weaken-comp Γ₁≼Γ₂ c) into (weaken-comp (keep σ •• Γ₁≼Γ₂) c₁)
  weaken-comp Γ₁≼Γ₂ (cAbs {σ} c) = cAbs (weaken-comp (keep σ •• Γ₁≼Γ₂) c)
  weaken-comp Γ₁≼Γ₂ (cApp s t) = cApp (weaken-comp Γ₁≼Γ₂ s) (weaken-val Γ₁≼Γ₂ t)

  fromCBN : ∀ {Γ τ} (t : Term Γ τ) → Comp (fromCBNCtx Γ) (cbnToCompType τ)
  fromCBN (const c) = cConst (cbnToCompConst c)
  fromCBN (var x) = cForce (vVar (fromVar cbnToValType x))
  fromCBN (app s t) = cApp (fromCBN s) (vThunk (fromCBN t))
  fromCBN (abs t) = cAbs (fromCBN t)

  -- To satisfy termination checking, we'd need to inline fromCBV and weaken: fromCBV needs to produce a term in a bigger context.
  -- But let's ignore that.
  {-# TERMINATING #-}
  fromCBV : ∀ {Γ τ} (t : Term Γ τ) → Comp (fromCBVCtx Γ) (cbvToCompType τ)

  fromCBV (const c) = cConst (cbvToCompConst c)
  fromCBV (app s t) =
    (fromCBV s) into
      (fromCBV (weaken (drop _ • ≼-refl) t) into
        cApp (cForce (vVar (vThat vThis))) (vVar vThis))
  -- Values
  fromCBV (var x) = cReturn (vVar (fromVar cbvToValType x))
  fromCBV (abs t) = cReturn (vThunk (cAbs (fromCBV t)))

  -- This reflects the CBV conversion of values in TLCA '99, but we can't write
  -- this because it's partial on the Term type. Instead, we duplicate thunking
  -- at the call site.
  {-
  fromCBVToVal : ∀ {Γ τ} (t : Term Γ τ) → Val (fromCBVCtx Γ) (cbvToValType τ)
  fromCBVToVal (var x) = vVar (fromVar cbvToValType x)
  fromCBVToVal (abs t) = vThunk (cAbs (fromCBV t))

  fromCBVToVal (const c args) = {!!}
  fromCBVToVal (app t t₁) = {!!} -- Not a value
  -}


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties of First
------------------------------------------------------------------------

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

module Data.List.Relation.Unary.First.Properties where

open import Data.Empty
open import Data.Fin using (suc)
open import Data.List.Base as List using (List; []; _∷_)
open import Data.List.Relation.Unary.All as All using (All; []; _∷_)
open import Data.List.Relation.Unary.Any as Any using (here; there)
open import Data.List.Relation.Unary.First
import Data.Sum as Sum
open import Function
open import Relation.Binary.PropositionalEquality as P using (_≡_; refl; _≗_)
open import Relation.Unary
open import Relation.Nullary.Negation

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

module _ {a b p q} {A : Set a} {B : Set b} {P : Pred B p} {Q : Pred B q} where

  map⁺ : {f : A → B} → First (P ∘′ f) (Q ∘′ f) ⊆ First P Q ∘′ List.map f
  map⁺ [ qfx ]        = [ qfx ]
  map⁺ (pfxs ∷ pqfxs) = pfxs ∷ map⁺ pqfxs

  map⁻ : {f : A → B} → First P Q ∘′ List.map f ⊆ First (P ∘′ f) (Q ∘′ f)
  map⁻ {f} {[]}     ()
  map⁻ {f} {x ∷ xs} [ qfx ]       = [ qfx ]
  map⁻ {f} {x ∷ xs} (pfx ∷ pqfxs) = pfx ∷ map⁻ pqfxs

------------------------------------------------------------------------
-- (++)

module _ {a p q} {A : Set a} {P : Pred A p} {Q : Pred A q} where

  ++⁺ : ∀ {xs ys} → All P xs → First P Q ys → First P Q (xs List.++ ys)
  ++⁺ []         pqys = pqys
  ++⁺ (px ∷ pxs) pqys = px ∷ ++⁺ pxs pqys

  ⁺++ : ∀ {xs} → First P Q xs → ∀ ys → First P Q (xs List.++ ys)
  ⁺++ [ qx ]      ys = [ qx ]
  ⁺++ (px ∷ pqxs) ys = px ∷ ⁺++ pqxs ys

------------------------------------------------------------------------
-- Relationship to All

module _ {a p q} {A : Set a} {P : Pred A p} {Q : Pred A q} where

  All⇒¬First : P ⊆ ∁ Q → All P ⊆ ∁ (First P Q)
  All⇒¬First p⇒¬q []         ()
  All⇒¬First p⇒¬q (px ∷ pxs) [ qx ]   = ⊥-elim (p⇒¬q px qx)
  All⇒¬First p⇒¬q (_ ∷ pxs)  (_ ∷ hf) = All⇒¬First p⇒¬q pxs hf

  First⇒¬All : Q ⊆ ∁ P → First P Q ⊆ ∁ (All P)
  First⇒¬All q⇒¬p [ qx ]     (px ∷ pxs) = q⇒¬p qx px
  First⇒¬All q⇒¬p (_ ∷ pqxs) (_ ∷ pxs)  = First⇒¬All q⇒¬p pqxs pxs

------------------------------------------------------------------------
-- Irrelevance

  unique-index : ∀ {xs} → P ⊆ ∁ Q → (f₁ f₂ : First P Q xs) → index f₁ ≡ index f₂
  unique-index p⇒¬q [ _ ]    [ _ ]    = refl
  unique-index p⇒¬q [ qx ]   (px ∷ _) = ⊥-elim (p⇒¬q px qx)
  unique-index p⇒¬q (px ∷ _) [ qx ]   = ⊥-elim (p⇒¬q px qx)
  unique-index p⇒¬q (_ ∷ f₁) (_ ∷ f₂) = P.cong suc (unique-index p⇒¬q f₁ f₂)

  irrelevant : P ⊆ ∁ Q → Irrelevant P → Irrelevant Q → Irrelevant (First P Q)
  irrelevant p⇒¬q p-irr q-irr [ qx₁ ]    [ qx₂ ]    = P.cong [_] (q-irr qx₁ qx₂)
  irrelevant p⇒¬q p-irr q-irr [ qx₁ ]    (px₂ ∷ f₂) = ⊥-elim (p⇒¬q px₂ qx₁)
  irrelevant p⇒¬q p-irr q-irr (px₁ ∷ f₁) [ qx₂ ]    = ⊥-elim (p⇒¬q px₁ qx₂)
  irrelevant p⇒¬q p-irr q-irr (px₁ ∷ f₁) (px₂ ∷ f₂) =
    P.cong₂ _∷_ (p-irr px₁ px₂) (irrelevant p⇒¬q p-irr q-irr f₁ f₂)

------------------------------------------------------------------------
-- Decidability

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

  first? : Decidable P → Decidable (First P (∁ P))
  first? P? xs = Sum.toDec
               $ Sum.map₂ (All⇒¬First contradiction)
               $ first (Sum.fromDec ∘ P?) xs

------------------------------------------------------------------------
-- Conversion to Any

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

  fromAny∘toAny≗id : ∀ {xs} → fromAny {Q = P} {x = xs} ∘′ toAny ≗ id
  fromAny∘toAny≗id [ qx ]      = refl
  fromAny∘toAny≗id (px ∷ pqxs) = P.cong (_ ∷_) (fromAny∘toAny≗id pqxs)

  toAny∘fromAny≗id : ∀ {xs} → toAny {Q = P} ∘′ fromAny {x = xs} ≗ id
  toAny∘fromAny≗id (here px) = refl
  toAny∘fromAny≗id (there v) = P.cong there (toAny∘fromAny≗id v)

------------------------------------------------------------------------
-- Equivalence between the inductive definition and the view

module _ {a p q} {A : Set a} {P : Pred A p} {Q : Pred A q} where

  toView : ∀ {as} → First P Q as → FirstView P Q as
  toView [ qx ] = [] ++ qx ∷ _
  toView (px ∷ pqxs) with toView pqxs
  ... | pxs ++  qy ∷ ys = (px ∷ pxs) ++ qy ∷ ys

  fromView : ∀ {as} → FirstView P Q as → First P Q as
  fromView (pxs ++ qy ∷ ys) = ++⁺ pxs [ qy ]


module Oscar.Class where

open import Oscar.Data.Equality
open import Oscar.Function
open import Oscar.Relation
open import Oscar.Level
open import Oscar.Function
open import Oscar.Relation

-- instance EquivalenceProp : ∀ {a} {A : Set a} → Equivalence (_≡_ {a} {A})
-- EquivalenceProp = {!!}

-- instance EquivalenceProp1 : ∀ {a} {A : Set a} {b} {B : A → Set b} → Equivalence (_≡̇_ {a} {A} {b} {B})
-- Equivalence.reflexivity EquivalenceProp1 x x₁ = refl

-- import Oscar.Data.Term.internal.SubstituteAndSubstitution FunctionName as ⋆

-- instance SemigroupFinTerm : Semigroup _⊸_ _≡̇_
-- Semigroup.equivalence SemigroupFinTerm = it
-- Semigroup._◇_ SemigroupFinTerm = ⋆._◇_ -- (Semifunctor._◃ SemifunctorFinTerm g) ∘ f --
-- Semigroup.◇-associativity SemigroupFinTerm = {!!}

-- instance Semigroup⋆ : ∀ {a} {A : Set a} {x} {X : A → Set x} → Semigroup (_⟨ X ⟩→_) _≡̇_
-- Semigroup.equivalence Semigroup⋆ = it
-- Semigroup._◇_ Semigroup⋆ g f = g ∘ f
-- Semigroup.◇-associativity Semigroup⋆ = {!!}

-- instance SemifunctorFinTerm : Semifunctor _⊸_ _≡̇_ (_⟨ Term ⟩→_) _≡̇_ id
-- Semifunctor.domain SemifunctorFinTerm = it
-- Semifunctor.codomain SemifunctorFinTerm = it
-- SemifunctorFinTerm Semifunctor.◃ = ⋆._◃_
-- Semifunctor.◃-extensionality SemifunctorFinTerm = ⋆.◃-extensionality
-- Semifunctor.◃-associativity SemifunctorFinTerm = ⋆.◃-associativity

-- -- sufficient for all my substitutions (Term, Formula, etc.)
-- _◂_ : ∀ {m n a} {A : Nat → Set a} ⦃ _ : Semifunctor _⊸_ _≡̇_ (_⟨ A ⟩→_) _≡̇_ id ⦄ → m ⊸ n → m ⟨ A ⟩→ n
-- _◂_ ⦃ semifunctor ⦄ f x = Semifunctor._◃ semifunctor f x

-- -- even more general, handling all pointwise (semi)functors
-- _◂'_ : ∀ {a} {A : Set a} {m n : A} {b} {B : A → Set b} {c} {C : A → Set c} {d} {D : A → Set d}
--          ⦃ _ : Semifunctor (λ m n → B m → C n) _≡̇_ _⟨ D ⟩→_ _≡̇_ id ⦄ → (B m → C n) → m ⟨ D ⟩→ n
-- _◂'_ ⦃ semifunctor ⦄ f x = Semifunctor._◃ semifunctor f x

-- record UsableSemifunctor {a} {A : Set a} {b} (B : A → Set b) {c} (C : A → Set c) {d} (D : A → Set d) : Set (a ⊔ b ⊔ c ⊔ d) where
--   field
--     ⦃ semifunctor ⦄ : Semifunctor (λ m n → B m → C n) _≡̇_ _⟨ D ⟩→_ _≡̇_ id

--   _◄_ : ∀ {m n} → (B m → C n) → m ⟨ D ⟩→ n
--   _◄_ f x = Semifunctor._◃ semifunctor f x

-- open UsableSemifunctor ⦃ … ⦄

-- instance UsableSemifunctor⋆ : ∀ {a} {A : Set a} {b} {B : A → Set b} {c} {C : A → Set c} {d} {D : A → Set d}
--                               -- { _≋₁_ : ∀ {m n} → (B m → C n) → (B m → C n) → Set (b ⊔ c) }
--                               -- ⦃ _ : ∀ {m n} → Equivalence (_≋₁_ {m} {n}) ⦄
--                               -- ⦃ _ : Semifunctor (λ m n → B m → C n) _≋₁_ _⟨ D ⟩→_ _≡̇_ id ⦄ → UsableSemifunctor B C D
--                               ⦃ _ : Semifunctor (λ m n → B m → C n) _≡̇_ _⟨ D ⟩→_ _≡̇_ id ⦄
--                               → UsableSemifunctor B C D
-- UsableSemifunctor.semifunctor (UsableSemifunctor⋆ ⦃ semi ⦄) = semi

-- {-
-- instance UsableSemifunctorFinTermTerm : UsableSemifunctor Fin Term Term
-- UsableSemifunctor.semifunctor UsableSemifunctorFinTermTerm = it
-- -}

-- -- obviously won't work unless we supply _≋₁_
-- _◂''_ : ∀ {a} {A : Set a} {m n : A} {b} {B : A → Set b} {c} {C : A → Set c} {d} {D : A → Set d}
--         ( _≋₁_ : ∀ {m n} → (B m → C n) → (B m → C n) → Set (b ⊔ c) )
--         ⦃ _ : ∀ {m n} → Equivalence (_≋₁_ {m} {n}) ⦄
--         ⦃ _ : Semifunctor (λ m n → B m → C n) _≋₁_ _⟨ D ⟩→_ _≡̇_ id ⦄ → (B m → C n) → m ⟨ D ⟩→ n
-- _◂''_ _ ⦃ _ ⦄ ⦃ semifunctor ⦄ f x = Semifunctor._◃ semifunctor f x

-- foo : ∀ {m n} → m ⊸ n → Term m → Term n
-- foo {m} {n} f x = f ◄ x

-- --foo f x = f ◂ x
-- --foo f x = _◂''_ _≡̇_ f x

-- -- record Ṁonoid {a} {A : Set a} {b} (B : A → Set b) {c} (C : A → Set c) : Set (a ⊔ b ⊔ c) where
-- --   field
-- --     ε̇ : ∀ {m} → B m → C m
-- --     _◇̇_ : ∀ {l m n} → (g : B m → C n) (f : B l → C m) → B l → C n
-- --     ◇̇-left-identity : ∀ {m n} → (f : B m → C n) → ε̇ ◇̇ f ≡̇ f
-- --     ◇̇-right-identity : ∀ {m n} → (f : B m → C n) → f ◇̇ ε̇ ≡̇ f
-- --     ◇̇-associativity : ∀ {k l m n} (f : B k → C l) (g : B l → C m) (h : B m → C n) → h ◇̇ (g ◇̇ f) ≡̇ (h ◇̇ g) ◇̇ f

-- -- record Monoid {a} {A : Set a} {b} (_↠_ : A → A → Set b) : Set (a ⊔ b) where
-- --   field
-- --     ε : ∀ {m} → m ↠ m
-- --     _◇_ : ∀ {l m n} → m ↠ n → l ↠ m → l ↠ n
-- --     ◇-left-identity : ∀ {m n} → (f : m ↠ n) → ε ◇ f ≡ f
-- --     ◇-right-identity : ∀ {m n} → (f : m ↠ n) → f ◇ ε ≡ f
-- --     ◇-associativity : ∀ {k l m n} (f : k ↠ l) (g : l ↠ m) (h : m ↠ n) → h ◇ (g ◇ f) ≡ (h ◇ g) ◇ f

-- -- record Substitution {a} {A : Set a} {b} (B : A → Set b) {c} (C : A → Set c) : Set (a ⊔ b ⊔ c) where
-- --   field
-- --     ε : ∀ {m} → B m → C m
-- --     _◇_ : ∀ {l m n} → (g : B m → C n) (f : B l → C m) → B l → C n
-- --     ◇-left-identity : ∀ {m n} → (f : B m → C n) → ε ◇ f ≡̇ f
-- --     ◇-right-identity : ∀ {m n} → (f : B m → C n) → f ◇ ε ≡̇ f
-- --     ◇-associativity : ∀ {k l m n} (f : B k → C l) (g : B l → C m) (h : B m → C n) → h ◇ (g ◇ f) ≡̇ (h ◇ g) ◇ f

-- -- record Substitution {a} {A : Set a} {b} (B : A → Set b) {c} (C : A → Set c) : Set (a ⊔ b ⊔ c) where
-- --   field
-- --     ε : ∀ {m} → B m → C m
-- --     _◇_ : ∀ {l m n} → (g : B m → C n) (f : B l → C m) → B l → C n
-- --     ◇-left-identity : ∀ {m n} → (f : B m → C n) → ε ◇ f ≡̇ f
-- --     ◇-right-identity : ∀ {m n} → (f : B m → C n) → f ◇ ε ≡̇ f
-- --     ◇-associativity : ∀ {k l m n} (f : B k → C l) (g : B l → C m) (h : B m → C n) → h ◇ (g ◇ f) ≡̇ (h ◇ g) ◇ f

-- -- record Substitution {a} {A : Set a} {b} (B : A → Set b) {c} (C : A → Set c) : Set (a ⊔ b ⊔ c) where
-- --   field
-- --     ε : ∀ {m} → B m → C m
-- --     _◇_ : ∀ {l m n} → (g : B m → C n) (f : B l → C m) → B l → C n
-- --     ◇-left-identity : ∀ {m n} → (f : B m → C n) → ε ◇ f ≡̇ f
-- --     ◇-right-identity : ∀ {m n} → (f : B m → C n) → f ◇ ε ≡̇ f
-- --     ◇-associativity : ∀ {k l m n} (f : B k → C l) (g : B l → C m) (h : B m → C n) → h ◇ (g ◇ f) ≡̇ (h ◇ g) ◇ f


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

module A2 where

open import Data.Nat
open import Data.Empty
open import Data.Unit
open import Data.Sum
open import Data.Product

infix  4  _≡_   -- propositional equality
infixr 10 _◎_
infixr 30 _⟷_

------------------------------------------------------------------------------
-- Our own version of refl that makes 'a' explicit

data _≡_ {ℓ} {A : Set ℓ} : (a b : A) → Set ℓ where
  refl : (a : A) → (a ≡ a)

sym : ∀ {ℓ} {A : Set ℓ} {a b : A} → (a ≡ b) → (b ≡ a)
sym {a = a} {b = .a} (refl .a) = refl a

trans : ∀ {ℓ} {A : Set ℓ} {a b c : A} → (a ≡ b) → (b ≡ c) → (a ≡ c)
trans {a = a} {b = .a} {c = .a} (refl .a) (refl .a) = refl a

------------------------------------------------------------------------------
{--
Types are higher groupoids:
- 0 is empty
- 1 has one element and one path refl
- sum type is disjoint union; paths are component wise
- product type is cartesian product; paths are pairs of paths
--}

mutual

  data U : Set where
    ZERO  : U
    ONE   : U
    PLUS  : U → U → U
    TIMES : U → U → U
    ID    : {t₁ t₂ : U} → (t₁ ⟷ t₂) → ⟦ t₁ ⟧ → ⟦ t₂ ⟧ → U

  ⟦_⟧ : U → Set
  ⟦ ZERO ⟧        = ⊥
  ⟦ ONE ⟧         = ⊤
  ⟦ PLUS t t' ⟧   = ⟦ t ⟧ ⊎ ⟦ t' ⟧
  ⟦ TIMES t t' ⟧  = ⟦ t ⟧ × ⟦ t' ⟧
  ⟦ ID {t₁} {t₂} c v₁ v₂ ⟧ = Paths {t₁} {t₂} c v₁ v₂

  data _⟷_ : U → U → Set where
    -- semiring axioms
    unite₊  : {t : U} → PLUS ZERO t ⟷ t
    uniti₊  : {t : U} → t ⟷ PLUS ZERO t
    swap₊   : {t₁ t₂ : U} → PLUS t₁ t₂ ⟷ PLUS t₂ t₁
    assocl₊ : {t₁ t₂ t₃ : U} → PLUS t₁ (PLUS t₂ t₃) ⟷ PLUS (PLUS t₁ t₂) t₃
    assocr₊ : {t₁ t₂ t₃ : U} → PLUS (PLUS t₁ t₂) t₃ ⟷ PLUS t₁ (PLUS t₂ t₃)
    unite⋆  : {t : U} → TIMES ONE t ⟷ t
    uniti⋆  : {t : U} → t ⟷ TIMES ONE t
    swap⋆   : {t₁ t₂ : U} → TIMES t₁ t₂ ⟷ TIMES t₂ t₁
    assocl⋆ : {t₁ t₂ t₃ : U} → TIMES t₁ (TIMES t₂ t₃) ⟷ TIMES (TIMES t₁ t₂) t₃
    assocr⋆ : {t₁ t₂ t₃ : U} → TIMES (TIMES t₁ t₂) t₃ ⟷ TIMES t₁ (TIMES t₂ t₃)
    distz   : {t : U} → TIMES ZERO t ⟷ ZERO
    factorz : {t : U} → ZERO ⟷ TIMES ZERO t
    dist    : {t₁ t₂ t₃ : U} →
              TIMES (PLUS t₁ t₂) t₃ ⟷ PLUS (TIMES t₁ t₃) (TIMES t₂ t₃)
    factor  : {t₁ t₂ t₃ : U} →
              PLUS (TIMES t₁ t₃) (TIMES t₂ t₃) ⟷ TIMES (PLUS t₁ t₂) t₃
    -- equivalence relation and 2 combinators
    id⟷    : {t : U} → t ⟷ t
    sym⟷   : {t₁ t₂ : U} → (t₁ ⟷ t₂) → (t₂ ⟷ t₁)
    _◎_     : {t₁ t₂ t₃ : U} → (t₁ ⟷ t₂) → (t₂ ⟷ t₃) → (t₁ ⟷ t₃)
    _⊕_     : {t₁ t₂ t₃ t₄ : U} →
              (t₁ ⟷ t₃) → (t₂ ⟷ t₄) → (PLUS t₁ t₂ ⟷ PLUS t₃ t₄)
    _⊗_     : {t₁ t₂ t₃ t₄ : U} →
              (t₁ ⟷ t₃) → (t₂ ⟷ t₄) → (TIMES t₁ t₂ ⟷ TIMES t₃ t₄)
   -- and one level up
{-
    lid : {t₁ t₂ : U} {v₁ : ⟦ t₁ ⟧} {v₂ : ⟦ t₂ ⟧} {c : t₁ ⟷ t₂} →
          ID (id⟷ ◎ c) v₁ v₂ ⟷ ID c v₁ v₂
    rid : {t₁ t₂ : U} {v₁ : ⟦ t₁ ⟧} {v₂ : ⟦ t₂ ⟧} {c : t₁ ⟷ t₂} →
          ID (c ◎ id⟷) v₁ v₂ ⟷ ID c v₁ v₂
    linv : {t₁ t₂ : U} {v : ⟦ t₂ ⟧} {c : t₁ ⟷ t₂} →
           ID ((sym⟷ c) ◎ c) v v ⟷ ID {t₂} {t₂} id⟷ v v
    rinv : {t₁ t₂ : U} {v : ⟦ t₁ ⟧} {c : t₁ ⟷ t₂} →
           ID (c ◎ (sym⟷ c)) v v ⟷ ID {t₁} {t₁} id⟷ v v
    invinv : {t₁ t₂ : U} {v₁ : ⟦ t₁ ⟧} {v₂ : ⟦ t₂ ⟧} {c : t₁ ⟷ t₂} →
           ID (sym⟷ (sym⟷ c)) v₁ v₂ ⟷ ID {t₁} {t₂} c v₁ v₂
    assoc : {t₁ t₂ t₃ t₄ : U}
            {v₁ : ⟦ t₁ ⟧} {v₂ : ⟦ t₂ ⟧} {v₃ : ⟦ t₃ ⟧} {v₄ : ⟦ t₄ ⟧}
            {c₁ : t₁ ⟷ t₂} {c₂ : t₂ ⟷ t₃} {c₃ : t₃ ⟷ t₄} →
            ID (c₁ ◎ (c₂ ◎ c₃)) v₁ v₄ ⟷ ID ((c₁ ◎ c₂) ◎ c₃) v₁ v₄
-}
-- add the other direction for lid, rid, linv, rinv, invinv, and assoc

  data Unite₊ {t : U} : ⊥ ⊎ ⟦ t ⟧ → ⟦ t ⟧ → Set where
    val_unite₊ : (v₁ : ⊥ ⊎ ⟦ t ⟧) → (v₂ : ⟦ t ⟧) → Unite₊ v₁ v₂

  Paths : {t₁ t₂ : U} → (t₁ ⟷ t₂) → ⟦ t₁ ⟧ → ⟦ t₂ ⟧ → Set
  Paths unite₊ (inj₁ ())
  Paths {PLUS ZERO t} {.t} unite₊ (inj₂ v) v' = (v ≡ v') × Unite₊ {t} (inj₂ v) v'
  Paths uniti₊ v (inj₁ ())
  Paths uniti₊ v (inj₂ v') = (v ≡ v')
  Paths swap₊ (inj₁ v) (inj₁ v') = ⊥
  Paths swap₊ (inj₁ v) (inj₂ v') = (v ≡ v')
  Paths swap₊ (inj₂ v) (inj₁ v') = (v ≡ v')
  Paths swap₊ (inj₂ v) (inj₂ v') = ⊥
  Paths assocl₊ (inj₁ v) (inj₁ (inj₁ v')) = (v ≡ v')
  Paths assocl₊ (inj₁ v) (inj₁ (inj₂ v')) = ⊥
  Paths assocl₊ (inj₁ v) (inj₂ v') = ⊥
  Paths assocl₊ (inj₂ (inj₁ v)) (inj₁ (inj₁ v')) = ⊥
  Paths assocl₊ (inj₂ (inj₁ v)) (inj₁ (inj₂ v')) = (v ≡ v')
  Paths assocl₊ (inj₂ (inj₁ v)) (inj₂ v') = ⊥
  Paths assocl₊ (inj₂ (inj₂ v)) (inj₁ v') = ⊥
  Paths assocl₊ (inj₂ (inj₂ v)) (inj₂ v') = (v ≡ v')
  Paths assocr₊ (inj₁ (inj₁ v)) (inj₁ v') = (v ≡ v')
  Paths assocr₊ (inj₁ (inj₁ v)) (inj₂ v') = ⊥
  Paths assocr₊ (inj₁ (inj₂ v)) (inj₁ v') = ⊥
  Paths assocr₊ (inj₁ (inj₂ v)) (inj₂ (inj₁ v')) = (v ≡ v')
  Paths assocr₊ (inj₁ (inj₂ v)) (inj₂ (inj₂ v')) = ⊥
  Paths assocr₊ (inj₂ v) (inj₁ v') = ⊥
  Paths assocr₊ (inj₂ v) (inj₂ (inj₁ v')) = ⊥
  Paths assocr₊ (inj₂ v) (inj₂ (inj₂ v')) = (v ≡ v')
  Paths unite⋆ (tt , v) v' = (v ≡ v')
  Paths uniti⋆ v (tt , v') = (v ≡ v')
  Paths swap⋆ (v₁ , v₂) (v₂' , v₁') = (v₁ ≡ v₁') × (v₂ ≡ v₂')
  Paths assocl⋆ (v₁ , (v₂ , v₃)) ((v₁' , v₂') , v₃') =
    (v₁ ≡ v₁') × (v₂ ≡ v₂') × (v₃ ≡ v₃')
  Paths assocr⋆ ((v₁ , v₂) , v₃) (v₁' , (v₂' , v₃')) =
    (v₁ ≡ v₁') × (v₂ ≡ v₂') × (v₃ ≡ v₃')
  Paths distz (() , v)
  Paths factorz ()
  Paths dist (inj₁ v₁ , v₃) (inj₁ (v₁' , v₃')) = (v₁ ≡ v₁') × (v₃ ≡ v₃')
  Paths dist (inj₁ v₁ , v₃) (inj₂ (v₂' , v₃')) = ⊥
  Paths dist (inj₂ v₂ , v₃) (inj₁ (v₁' , v₃')) = ⊥
  Paths dist (inj₂ v₂ , v₃) (inj₂ (v₂' , v₃')) = (v₂ ≡ v₂') × (v₃ ≡ v₃')
  Paths factor (inj₁ (v₁ , v₃)) (inj₁ v₁' , v₃') =
    (v₁ ≡ v₁') × (v₃ ≡ v₃')
  Paths factor (inj₁ (v₁ , v₃)) (inj₂ v₂' , v₃') = ⊥
  Paths factor (inj₂ (v₂ , v₃)) (inj₁ v₁' , v₃') = ⊥
  Paths factor (inj₂ (v₂ , v₃)) (inj₂ v₂' , v₃') =
    (v₂ ≡ v₂') × (v₃ ≡ v₃')
  Paths {t} id⟷ v v' = (v ≡ v')
  Paths (sym⟷ c) v v' = PathsB c v v'
  Paths (_◎_ {t₁} {t₂} {t₃} c₁ c₂) v v' =
    Σ[ u ∈ ⟦ t₂ ⟧ ] (Paths c₁ v u × Paths c₂ u v')
  Paths (c₁ ⊕ c₂) (inj₁ v) (inj₁ v') = Paths c₁ v v'
  Paths (c₁ ⊕ c₂) (inj₁ v) (inj₂ v') = ⊥
  Paths (c₁ ⊕ c₂) (inj₂ v) (inj₁ v') = ⊥
  Paths (c₁ ⊕ c₂) (inj₂ v) (inj₂ v') = Paths c₂ v v'
  Paths (c₁ ⊗ c₂) (v₁ , v₂) (v₁' , v₂') =
    Paths c₁ v₁ v₁' × Paths c₂ v₂ v₂'
{-
  Paths lid (v , refl .v , p) q = (p ≡ q)
  Paths rid (v , p , refl .v) q = (p ≡ q)
  Paths (linv {t₁} {v = v} {c}) (w , proj₂ , proj₃) y = {!!}
  Paths rinv (proj₁ , proj₂ , proj₃) y = {!!}
  Paths (invinv {c = c}) x y = {!!}
  Paths assoc (v₂ , proj₂ , v₃ , proj₄ , proj₅) (w₃ , (w₂ , proj₈ , proj₉) , proj₁₀) = v₂ ≡ w₂ × v₃ ≡ w₃
-}

  PathsB : {t₁ t₂ : U} → (t₁ ⟷ t₂) → ⟦ t₂ ⟧ → ⟦ t₁ ⟧ → Set
  PathsB unite₊ v (inj₁ ())
  PathsB unite₊ v (inj₂ v') = (v ≡ v')
  PathsB uniti₊ (inj₁ ())
  PathsB uniti₊ (inj₂ v) v' = (v ≡ v')
  PathsB swap₊ (inj₁ v) (inj₁ v') = ⊥
  PathsB swap₊ (inj₁ v) (inj₂ v') = (v ≡ v')
  PathsB swap₊ (inj₂ v) (inj₁ v') = (v ≡ v')
  PathsB swap₊ (inj₂ v) (inj₂ v') = ⊥
  PathsB assocl₊ (inj₁ (inj₁ v)) (inj₁ v') = (v ≡ v')
  PathsB assocl₊ (inj₁ (inj₁ v)) (inj₂ v') = ⊥
  PathsB assocl₊ (inj₁ (inj₂ v)) (inj₁ v') = ⊥
  PathsB assocl₊ (inj₁ (inj₂ v)) (inj₂ (inj₁ v')) = (v ≡ v')
  PathsB assocl₊ (inj₁ (inj₂ v)) (inj₂ (inj₂ v')) = ⊥
  PathsB assocl₊ (inj₂ v) (inj₁ v') = ⊥
  PathsB assocl₊ (inj₂ v) (inj₂ (inj₁ v')) = ⊥
  PathsB assocl₊ (inj₂ v) (inj₂ (inj₂ v')) = (v ≡ v')
  PathsB assocr₊ (inj₁ v) (inj₁ (inj₁ v')) = (v ≡ v')
  PathsB assocr₊ (inj₁ v) (inj₁ (inj₂ v')) = ⊥
  PathsB assocr₊ (inj₁ v) (inj₂ v') = ⊥
  PathsB assocr₊ (inj₂ (inj₁ v)) (inj₁ (inj₁ v')) = ⊥
  PathsB assocr₊ (inj₂ (inj₁ v)) (inj₁ (inj₂ v')) = (v ≡ v')
  PathsB assocr₊ (inj₂ (inj₁ v)) (inj₂ v') = ⊥
  PathsB assocr₊ (inj₂ (inj₂ v)) (inj₁ v') = ⊥
  PathsB assocr₊ (inj₂ (inj₂ v)) (inj₂ v') = (v ≡ v')
  PathsB unite⋆ v (tt , v') = (v ≡ v')
  PathsB uniti⋆ (tt , v) v' = (v ≡ v')
  PathsB swap⋆ (v₁ , v₂) (v₂' , v₁') = (v₁ ≡ v₁') × (v₂ ≡ v₂')
  PathsB assocl⋆ ((v₁ , v₂) , v₃) (v₁' , (v₂' , v₃')) =
    (v₁ ≡ v₁') × (v₂ ≡ v₂') × (v₃ ≡ v₃')
  PathsB assocr⋆ (v₁ , (v₂ , v₃)) ((v₁' , v₂') , v₃') =
    (v₁ ≡ v₁') × (v₂ ≡ v₂') × (v₃ ≡ v₃')
  PathsB distz ()
  PathsB factorz (() , v)
  PathsB dist (inj₁ (v₁ , v₃)) (inj₁ v₁' , v₃') =
    (v₁ ≡ v₁') × (v₃ ≡ v₃')
  PathsB dist (inj₁ (v₁ , v₃)) (inj₂ v₂' , v₃') = ⊥
  PathsB dist (inj₂ (v₂ , v₃)) (inj₁ v₁' , v₃') = ⊥
  PathsB dist (inj₂ (v₂ , v₃)) (inj₂ v₂' , v₃') =
    (v₂ ≡ v₂') × (v₃ ≡ v₃')
  PathsB factor (inj₁ v₁ , v₃) (inj₁ (v₁' , v₃')) =
    (v₁ ≡ v₁') × (v₃ ≡ v₃')
  PathsB factor (inj₁ v₁ , v₃) (inj₂ (v₂' , v₃')) = ⊥
  PathsB factor (inj₂ v₂ , v₃) (inj₁ (v₁' , v₃')) = ⊥
  PathsB factor (inj₂ v₂ , v₃) (inj₂ (v₂' , v₃')) =
    (v₂ ≡ v₂') × (v₃ ≡ v₃')
  PathsB {t} id⟷ v v' = (v ≡ v')
  PathsB (sym⟷ c) v v' = Paths c v v'
  PathsB (_◎_ {t₁} {t₂} {t₃} c₁ c₂) v v' =
    Σ[ u ∈ ⟦ t₂ ⟧ ] (PathsB c₂ v u × PathsB c₁ u v')
  PathsB (c₁ ⊕ c₂) (inj₁ v) (inj₁ v') = PathsB c₁ v v'
  PathsB (c₁ ⊕ c₂) (inj₁ v) (inj₂ v') = ⊥
  PathsB (c₁ ⊕ c₂) (inj₂ v) (inj₁ v') = ⊥
  PathsB (c₁ ⊕ c₂) (inj₂ v) (inj₂ v') = PathsB c₂ v v'
  PathsB (c₁ ⊗ c₂) (v₁ , v₂) (v₁' , v₂') =
    PathsB c₁ v₁ v₁' × PathsB c₂ v₂ v₂'
{-
  PathsB rid q (a , p , refl .a) = q ≡ p
  PathsB lid x y = {!!}
  PathsB linv x y = {!!}
  PathsB rinv = {!!}
  PathsB invinv = {!!}
  PathsB assoc = {!!}
-}
------------------------------------------------------------------------------
-- Examples...

BOOL : U
BOOL = PLUS ONE ONE

BOOL² : U
BOOL² = TIMES BOOL BOOL

FALSE : ⟦ BOOL ⟧
FALSE = inj₂ tt

TRUE : ⟦ BOOL ⟧
TRUE = inj₁ tt

e₁ : ⟦ ID {BOOL²} {BOOL²} id⟷ (FALSE , TRUE) (FALSE , TRUE) ⟧
e₁ = refl (FALSE , TRUE)

e₂ : ⟦ ID {BOOL²} {BOOL²} (id⟷ ⊗ id⟷) (FALSE , TRUE) (FALSE , TRUE) ⟧
e₂ = (refl FALSE , refl TRUE)
---------------


------------------------------------------------------------------------
-- The partiality monad's monad instance, defined via an adjunction
------------------------------------------------------------------------

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

module Partiality-monad.Inductive.Monad.Adjunction where

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

open import Adjunction equality-with-J
open import Bijection equality-with-J using (_↔_)
open import Category equality-with-J
open import Function-universe equality-with-J hiding (id; _∘_)
open import Functor equality-with-J
open import H-level equality-with-J
open import H-level.Closure equality-with-J

open import Partiality-algebra as PA hiding (id; _∘_)
open import Partiality-algebra.Category as PAC
import Partiality-algebra.Properties as PAP
open import Partiality-monad.Inductive as PI
  using (_⊥; partiality-algebra; initial)
open import Partiality-monad.Inductive.Eliminators
import Partiality-monad.Inductive.Monad as PM
import Partiality-monad.Inductive.Omega-continuous as PO

-- A forgetful functor from partiality algebras to sets.

Forget : ∀ {a p q} {A : Type a} →
         PAC.precategory p q A ⇨ precategory-Set p ext
functor Forget =
    (λ P → T P , T-is-set P)
  , Morphism.function
  , refl
  , refl
  where
  open Partiality-algebra

-- The precategory of pointed ω-cpos.

ω-CPPO : ∀ p q → Precategory (lsuc (p ⊔ q)) (p ⊔ q)
ω-CPPO p q = PAC.precategory p q ⊥₀

-- Pointed ω-cpos.

ω-cppo : ∀ p q → Type (lsuc (p ⊔ q))
ω-cppo p q = Partiality-algebra p q ⊥₀

-- If there is a function from B to the carrier of P, then P
-- can be converted to a partiality algebra over B.

convert : ∀ {a b p q} {A : Type a} {B : Type b} →
          (P : Partiality-algebra p q A) →
          (B → Partiality-algebra.T P) →
          Partiality-algebra p q B
convert P f = record
  { T                       = T
  ; partiality-algebra-with = record
    { _⊑_               = _⊑_
    ; never             = never
    ; now               = f
    ; ⨆                 = ⨆
    ; antisymmetry      = antisymmetry
    ; T-is-set-unused   = T-is-set-unused
    ; ⊑-refl            = ⊑-refl
    ; ⊑-trans           = ⊑-trans
    ; never⊑            = never⊑
    ; upper-bound       = upper-bound
    ; least-upper-bound = least-upper-bound
    ; ⊑-propositional   = ⊑-propositional
    }
  }
  where
  open Partiality-algebra P

-- A lemma that removes convert from certain types.

drop-convert :
  ∀ {a p q p′ q′} {A : Type a} {X : ω-cppo p q} {Y : ω-cppo p′ q′}
    {f : A → _} {g : A → _} →
  Morphism (convert X f) (convert Y g) → Morphism X Y
drop-convert m = record
  { function     = function
  ; monotone     = monotone
  ; strict       = strict
  ; now-to-now   = λ x → ⊥-elim x
  ; ω-continuous = ω-continuous
  }
  where
  open Morphism m

-- Converts partiality algebras to ω-cppos.

drop-now : ∀ {a p q} {A : Type a} →
           Partiality-algebra p q A → ω-cppo p q
drop-now P = convert P ⊥-elim

-- The function drop-now does not modify ω-cppos.

drop-now-constant :
  ∀ {p q} {P : ω-cppo p q} →
  drop-now P ≡ P
drop-now-constant =
  cong (λ now → record { partiality-algebra-with =
                           record { now = now } })
       (⟨ext⟩ λ x → ⊥-elim x)

-- Converts types to ω-cppos.

Partial⊚ : ∀ {ℓ} → Type ℓ → ω-cppo ℓ ℓ
Partial⊚ = drop-now ∘ partiality-algebra

private

  -- A lemma.

  Partial⊙′ :
    let open Partiality-algebra; open Morphism in

    ∀ {a b p q} {A : Type a} {B : Type b}
    (P : Partiality-algebra p q B) →
    (f : A → T P) →
    ∃ λ (m : Morphism (Partial⊚ A) (drop-now P)) →
      (∀ x → function m (PI.now x) ≡ now (convert P f) x)
        ×
      (∀ m′ → (∀ x → function m′ (PI.now x) ≡ now (convert P f) x) →
              m ≡ m′)
  Partial⊙′ {A = A} P f = m′ , PI.⊥-rec-now _ , lemma
    where
    P′ : Partiality-algebra _ _ A
    P′ = convert P f

    m : Morphism (partiality-algebra A) P′
    m = proj₁ (initial P′)

    m′ : Morphism (Partial⊚ A) (drop-now P)
    m′ = drop-convert m

    abstract

      lemma :
        ∀ m″ →
        (∀ x → Morphism.function m″ (PI.now x) ≡
               Partiality-algebra.now P′ x) →
        m′ ≡ m″
      lemma m″ hyp = _↔_.to equality-characterisation-Morphism (
        function m′  ≡⟨⟩
        function m   ≡⟨ cong function (proj₂ (initial P′) record
                                         { function     = function m″
                                         ; monotone     = monotone m″
                                         ; strict       = strict m″
                                         ; now-to-now   = hyp
                                         ; ω-continuous =
                                             ω-continuous m″
                                         }) ⟩∎
        function m″  ∎)
        where
        open Morphism

-- Lifts functions between types to morphisms between the
-- corresponding ω-cppos.

Partial⊙ :
  ∀ {a b} {A : Type a} {B : Type b} →
  (A → B) → Morphism (Partial⊚ A) (Partial⊚ B)
Partial⊙ f = proj₁ (Partial⊙′ (partiality-algebra _) (PI.now ∘ f))

-- Partial⊙ f is the unique morphism (of the given type) mapping
-- PI.now x to PI.now (f x) (for all x).

Partial⊙-now :
  ∀ {a b} {A : Type a} {B : Type b} {f : A → B} {x} →
  Morphism.function (Partial⊙ f) (PI.now x) ≡ PI.now (f x)
Partial⊙-now = proj₁ (proj₂ (Partial⊙′ (partiality-algebra _) _)) _

Partial⊙-unique :
  ∀ {a b} {A : Type a} {B : Type b} {f : A → B} {m} →
    (∀ x → Morphism.function m (PI.now x) ≡ PI.now (f x)) →
  Partial⊙ f ≡ m
Partial⊙-unique = proj₂ (proj₂ (Partial⊙′ (partiality-algebra _) _)) _

-- A functor that maps a set A to A ⊥.

Partial : ∀ {ℓ} → precategory-Set ℓ ext ⇨ ω-CPPO ℓ ℓ
_⇨_.functor (Partial {ℓ}) =
    Partial⊚ ∘ proj₁
  , Partial⊙
  , L.lemma₁
  , L.lemma₂
  where
  open Morphism

  module L where
   abstract

    lemma₁ : {A : Type ℓ} → Partial⊙ (id {A = A}) ≡ PA.id
    lemma₁ = Partial⊙-unique λ _ → refl

    lemma₂ : {A B C : Type ℓ} {f : A → B} {g : B → C} →
             Partial⊙ (g ∘ f) ≡ Partial⊙ g PA.∘ Partial⊙ f
    lemma₂ {f = f} {g} = Partial⊙-unique λ x →
      function (Partial⊙ g PA.∘ Partial⊙ f) (PI.now x)  ≡⟨ cong (function (Partial⊙ g)) Partial⊙-now ⟩
      function (Partial⊙ g) (PI.now (f x))              ≡⟨ Partial⊙-now ⟩∎
      PI.now (g (f x))                                  ∎

-- Partial is a left adjoint of Forget.

Partial⊣Forget : ∀ {ℓ} → Partial {ℓ = ℓ} ⊣ Forget
Partial⊣Forget {ℓ} =
    η
  , ε
  , (λ {X} →
       let P = Partial⊚ (proj₁ X) in
       _↔_.to equality-characterisation-Morphism $ ⟨ext⟩ $
       ⊥-rec-⊥ record
         { pe = fun P (function (Partial⊙ PI.now) PI.never)  ≡⟨ cong (fun P) $ strict (Partial⊙ PI.now) ⟩
                fun P PI.never                               ≡⟨ strict (m P) ⟩∎
                PI.never                                     ∎
         ; po = λ x →
                  fun P (function (Partial⊙ PI.now) (PI.now x))  ≡⟨ cong (fun P) Partial⊙-now ⟩
                  fun P (PI.now (PI.now x))                      ≡⟨ fun-now P ⟩∎
                  PI.now x                                       ∎
         ; pl = λ s hyp →
                  fun P (function (Partial⊙ PI.now) (PI.⨆ s))  ≡⟨ cong (fun P) $ ω-continuous (Partial⊙ PI.now) _ ⟩
                  fun P (PI.⨆ _)                               ≡⟨ ω-continuous (m P) _ ⟩
                  PI.⨆ _                                       ≡⟨ cong PI.⨆ $ _↔_.to PI.equality-characterisation-increasing hyp ⟩∎
                  PI.⨆ s                                       ∎
         ; pp = λ _ → PI.⊥-is-set
         })
  , (λ {X} → ⟨ext⟩ λ x →
       fun X (PI.now x)  ≡⟨ fun-now X ⟩∎
       x                 ∎)
  where
  open Morphism {q₂ = ℓ}
  open PAP
  open Partiality-algebra

  η : id⇨ ⇾ Forget ∙⇨ Partial
  _⇾_.natural-transformation η =
      PI.now
    , (λ {X Y f} → ⟨ext⟩ λ x →
         function (Partial⊙ f) (PI.now x)  ≡⟨ Partial⊙-now ⟩∎
         PI.now (f x)                      ∎)

  m : (X : ω-cppo ℓ ℓ) → Morphism (Partial⊚ (T X)) X
  m X =                                     $⟨ id ⟩
    (T X → T X)                             ↝⟨ proj₁ ∘ Partial⊙′ X ⟩
    Morphism (Partial⊚ (T X)) (drop-now X)  ↝⟨ drop-convert ⟩□
    Morphism (Partial⊚ (T X)) X             □

  fun : (X : ω-cppo ℓ ℓ) → T X ⊥ → T X
  fun X = function (m X)

  fun-now : ∀ (X : ω-cppo ℓ ℓ) {x} → fun X (PI.now x) ≡ x
  fun-now X = proj₁ (proj₂ (Partial⊙′ X _)) _

  fun-unique :
    (X : ω-cppo ℓ ℓ) (m′ : Morphism (Partial⊚ (T X)) X) →
    (∀ x → function m′ (PI.now x) ≡ x) →
    fun X ≡ function m′
  fun-unique X m′ hyp =
    cong function $ proj₂ (proj₂ (Partial⊙′ X _)) (drop-convert m′) hyp

  ε : Partial ∙⇨ Forget ⇾ id⇨
  _⇾_.natural-transformation ε =
      (λ {X} → m X)
    , (λ {X Y f} →
         let m′ = (Partial ∙⇨ Forget) ⊙ f in
         _↔_.to equality-characterisation-Morphism $ ⟨ext⟩ $
         ⊥-rec-⊥ record
           { pe = function f (fun X PI.never)   ≡⟨ cong (function f) (strict (m X)) ⟩
                  function f (never X)          ≡⟨ strict f ⟩
                  never Y                       ≡⟨ sym $ strict (m Y) ⟩
                  fun Y PI.never                ≡⟨ cong (fun Y) $ sym $ strict m′ ⟩∎
                  fun Y (function m′ PI.never)  ∎
           ; po = λ x →
                    function f (fun X (PI.now x))   ≡⟨ cong (function f) (fun-now X) ⟩
                    function f x                    ≡⟨ sym $ fun-now Y ⟩
                    fun Y (PI.now (function f x))   ≡⟨ cong (fun Y) $ sym Partial⊙-now ⟩∎
                    fun Y (function m′ (PI.now x))  ∎
           ; pl = λ s hyp →
                    function f (fun X (PI.⨆ s))   ≡⟨ cong (function f) (ω-continuous (m X) _) ⟩
                    function f (⨆ X _)            ≡⟨ ω-continuous f _ ⟩
                    ⨆ Y _                         ≡⟨ cong (⨆ Y) $ _↔_.to (equality-characterisation-increasing Y) hyp ⟩
                    ⨆ Y _                         ≡⟨ sym $ ω-continuous (m Y) _ ⟩
                    fun Y (PI.⨆ _)                ≡⟨ cong (fun Y) $ sym $ ω-continuous m′ _ ⟩∎
                    fun Y (function m′ (PI.⨆ s))  ∎
           ; pp = λ _ → T-is-set Y
           })

-- Thus we get that the partiality monad is a monad.

Partiality-monad : ∀ {ℓ} → Monad (precategory-Set ℓ ext)
Partiality-monad = adjunction→monad (Partial , Forget , Partial⊣Forget)

private

  -- The object part of the monad's functor really does correspond to
  -- the partiality monad.

  object-part-of-functor-correct :
    ∀ {a} {A : Set a} →
    proj₁ (proj₁ Partiality-monad ⊚ A) ≡ proj₁ A ⊥
  object-part-of-functor-correct = refl

  -- The definition of "map" obtained here matches the explicit
  -- definition in Partiality-monad.Inductive.Monad.

  map-correct :
    ∀ {ℓ} {A B : Set ℓ} {f : proj₁ A → proj₁ B} →
    _⊙_ (proj₁ Partiality-monad) {X = A} {Y = B} f ≡
    PO.[_⊥→_⊥].function (PM.map f)
  map-correct = refl

  -- The definition of "return" is the expected one.

  return-correct :
    ∀ {a} {A : Set a} →
    _⇾_.transformation (proj₁ (proj₂ Partiality-monad)) {X = A} ≡ PI.now
  return-correct = refl

  -- The definition of "join" obtained here matches the explicit
  -- definition in Partiality-monad.Inductive.Monad.

  join-correct :
    ∀ {a} {A : Set a} →
    _⇾_.transformation
      (proj₁ (proj₂ (proj₂ Partiality-monad))) {X = A} ≡
    PM.join
  join-correct = refl


module CTL.Proof.AG where

open import CTL.Modalities.AG
open import FStream.Core
open import FStream.FVec
open import Library

-- TODO Maybe beautify syntax here and elsewhere
data proofAG' {ℓ₁ ℓ₂} {C : Container ℓ₁} : {n : ℕ} →
     (props : FVec' C (Set ℓ₂) n) → Set (ℓ₁ ⊔ ℓ₂) where
  []AG'  : proofAG' FNil'
  _▻AG'_ : ∀ {prop : Set ℓ₂} {n} {props : ⟦ C ⟧ (FVec' C (Set ℓ₂) n)} →
           prop → A (fmap proofAG' props) → proofAG' (FCons' prop props)

_pre⟨_▻AG' : ∀ {i ℓ₁ ℓ₂} {C : Container ℓ₁} {m n}
               {props : FVec' C (Set ℓ₂) m} {props' : FVec' C (Set ℓ₂) (suc n)}
            → proofAG' props → proofAG' props'
            → AG' {i} (props pre⟨ props' ▻⋯')
nowA ([]AG' pre⟨ (proof ▻AG' _) ▻AG') = proof
laterA ([]AG' pre⟨ (proof ▻AG' proofs) ▻AG') p =
  (proofs p) pre⟨ (proof ▻AG' proofs) ▻AG'
nowA ((proof ▻AG' _) pre⟨ _ ▻AG') = proof
laterA ((_ ▻AG' proofs) pre⟨ proofs' ▻AG') p =
  (proofs p) pre⟨ proofs' ▻AG'

⟨_▻AG' : ∀ {i} {ℓ₁ ℓ₂} {C : Container ℓ₁} {n}
           {props : FVec' C (Set ℓ₂) (suc n)}
       → proofAG' props → AG' {i} (FNil' pre⟨ props ▻⋯')
⟨ proofs ▻AG' = []AG' pre⟨ proofs ▻AG'

data proofAG {ℓ₁ ℓ₂} {C : Container ℓ₁} : {n : ℕ} →
     (props : FVec C (Set ℓ₂) n) → Set (ℓ₁ ⊔ ℓ₂) where
  []AG : proofAG FNil
  ConsAG : ∀ {n} {props : ⟦ C ⟧ (Set ℓ₂ × FVec C (Set ℓ₂) n)} →
    A (fmap (λ x → (proj₁ x) × (proofAG (proj₂ x))) props) → proofAG (FCons props)
  -- TODO This constructor type signature is an abomination and should be somehow rewritten with proofAG, but how??
  -- TODO props should be of type FVec C (Set ℓ₂) (suc n) maybe? Or is it easier this way?

mapAGlemma : ∀ {i} {ℓ₁ ℓ₂ ℓ₃} {C : Container ℓ₁}
             {A : Set ℓ₂} {f : A → Set ℓ₃} {m n} →
             (v : FVec C A m) →
             (v' : FVec C A (suc n)) →
             AG {i} ((vmap f v pre⟨ vmap f v' ▻⋯)) →
             AG {i} (map f (v pre⟨ v' ▻⋯))
nowA (mapAGlemma FNil (FCons x) proofs p) = nowA (proofs p) -- TODO Why can't I join this with the next line?
nowA (mapAGlemma (FCons x) v proofs p) = nowA (proofs p)
laterA (mapAGlemma FNil (FCons (shape , vals)) proofs p) p₁ =
  mapAGlemma (proj₂ (vals p)) (FCons (shape , vals)) (laterA (proofs p)) p₁
laterA (mapAGlemma (FCons (shape , vals)) v' proofs p) p₁ =
  mapAGlemma (proj₂ (vals p)) v' (laterA (proofs p)) p₁

mapAG : ∀ {i} {ℓ₁ ℓ₂ ℓ₃} {C : Container ℓ₁}
          {A : Set ℓ₂} {f : A → Set ℓ₃} {m n} →
        {v : FVec C A m} →
        {v' : FVec C A (suc n)} →
        AG {i} ((vmap f v pre⟨ vmap f v' ▻⋯)) →
        AG {i} (map f (v pre⟨ v' ▻⋯))
mapAG {v = v} {v' = v'} = mapAGlemma v v'

_pre⟨_▻AG : ∀ {i} {ℓ₁ ℓ₂} {C : Container ℓ₁} {m n}
              {props : FVec C (Set ℓ₂) m} {props' : FVec C (Set ℓ₂) (suc n)}
          → proofAG props
          → proofAG props'
          → AG {i} (props pre⟨ props' ▻⋯)
nowA ( ([]AG pre⟨ (ConsAG proofs) ▻AG) p) = proj₁ (proofs p)
laterA ( ([]AG pre⟨ (ConsAG proofs) ▻AG) p) p₁ =
  (proj₂ (proofs p) pre⟨ ConsAG proofs ▻AG) p₁
nowA ( ((ConsAG proofs) pre⟨ _ ▻AG) p) = proj₁ (proofs p)
laterA ( ((ConsAG proofs) pre⟨ proofs' ▻AG) p) p₁ =
  (proj₂ (proofs p) pre⟨ proofs' ▻AG) p₁
-- The p are the inputs (positions) from the side effects.

mapPreCycle : ∀ {i} {ℓ₁ ℓ₂ ℓ₃} {C : Container ℓ₁} {A : Set ℓ₂}
                {f : A → Set ℓ₃} {m n}
            → (v : FVec C A m) → (v' : FVec C A (suc n))
            → proofAG (vmap f v) → proofAG (vmap f v')
            → AG {i} (map f (v pre⟨ v' ▻⋯))
nowA (mapPreCycle FNil (FCons x) []AG (ConsAG proofs) p)
      with proofs p
...   | proof , proofs' = proof
laterA (mapPreCycle FNil (FCons (shape , vals)) []AG (ConsAG proofs) p) p'
      with proofs p
...   | proof , proofs'
      with vals p
...   | a , v = mapPreCycle v (FCons (shape , vals)) proofs' (ConsAG proofs) p'
nowA (mapPreCycle (FCons x) v' (ConsAG proofs) proofs' p) = proj₁ (proofs p)
laterA (mapPreCycle (FCons (shape , vals)) (FCons x) (ConsAG proofs) proofs' p) p₁
      with proofs p
...   | proof , proofs''
      with vals p
...   | a , v = mapPreCycle v (FCons x) proofs'' proofs' p₁

infixr 5 _▻AG_
infix 6 ⟨_▻AG
infix 7 _⟩AG

⟨_▻AG : ∀ {i} {ℓ₁ ℓ₂} {C : Container ℓ₁} {n}
      → {props : FVec C (Set ℓ₂) (suc n)}
      → proofAG props
      → AG {i} (FNil pre⟨ props ▻⋯)
⟨_▻AG = []AG pre⟨_▻AG

_⟩AG : ∀ {ℓ₁ ℓ₂} {C : Container ℓ₁} {prop : ⟦ C ⟧ (Set ℓ₂)}
     → A prop
     → proofAG (FCons (fmap (_, FNil) prop))
x ⟩AG = ConsAG (λ p → x p , []AG)

_▻AG_ : ∀ {ℓ₁ ℓ₂} {C : Container ℓ₁}
          {prop : ⟦ C ⟧ (Set ℓ₂)} {n} {props : FVec C (Set ℓ₂) n}
      → A prop → proofAG props
      → proofAG (FCons (fmap (_, props) prop))
x ▻AG v = ConsAG (λ p → (x p) , v)


open import Common.Prelude
open import Common.Reflection
open import Common.Equality
open import Agda.Builtin.Sigma

magic₁ : ⊥ → Nat
magic₁ = λ ()

magic₂ : ⊥ → Nat
magic₂ = λ { () }

magic₃ : ⊥ → Nat
magic₃ ()

data Wrap (A : Set) : Set where
  wrap : A → Wrap A

magic₄ : Wrap ⊥ → Nat
magic₄ (wrap ())

data OK : Set where
  ok : OK

bad : String
bad = "not good"

macro
  checkDefinition : (Definition → Bool) → QName → Tactic
  checkDefinition isOk f hole =
    bindTC (getDefinition f) λ def →
    give (if isOk def then quoteTerm ok else quoteTerm bad) hole

pattern `Nat  = def (quote Nat) []
pattern _`→_ a b = pi (vArg a) (abs "_" b)
pattern `Wrap a = def (quote Wrap) (vArg a ∷ [])
pattern `⊥ = def (quote ⊥) []

pattern expected₄ = funDef
  (absurdClause (("()" , vArg `⊥) ∷ []) (vArg (con (quote wrap) (vArg absurd ∷ [])) ∷ [])
  ∷ [])

check₄ : OK
check₄ = checkDefinition (λ { expected₄ → true; _ → false }) magic₄

expected = extLam (absurdClause (("()" , vArg `⊥) ∷ []) (arg (argInfo visible relevant) absurd ∷ []) ∷ []) []

macro

  quoteTermNormalised : Term → Term → TC ⊤
  quoteTermNormalised t hole =
    bindTC (normalise t) λ t →
    bindTC (quoteTC t) λ t →
    unify hole t

check₁ : quoteTermNormalised magic₁ ≡ expected
check₁ = refl

check₂ : quoteTermNormalised magic₂ ≡ expected
check₂ = refl

pattern expectedDef =
  funDef (absurdClause (("()" , vArg `⊥) ∷ []) (vArg absurd ∷ []) ∷ [])

check₃ : OK
check₃ = checkDefinition (λ { expectedDef → true; _ → false }) magic₃