------------------------------------------------------------------------
-- Soundness of algorithmic equality (in the absence of equality
-- reflection)
------------------------------------------------------------------------

open import Definition.Typed.Restrictions
open import Graded.Modality

module Definition.Conversion.Soundness
  {a} {M : Set a}
  {š•„ : Modality M}
  (R : Type-restrictions š•„)
  (open Type-restrictions R)
  ⦃ no-equality-reflection : No-equality-reflection ⦄
  where

open import Definition.Untyped M
open import Definition.Untyped.Neutral M type-variant
open import Definition.Typed R
open import Definition.Typed.EqRelInstance R
open import Definition.Typed.EqualityRelation.Instance R
open import Definition.Typed.Inversion R
open import Definition.Typed.Properties R
import Definition.Typed.Reasoning.Term R as TmR
import Definition.Typed.Reasoning.Type R as TyR
open import Definition.Typed.Stability R
open import Definition.Typed.Syntactic R
open import Definition.Typed.Well-formed R
open import Definition.Conversion R
open import Definition.Conversion.Whnf R
open import Definition.Typed.Consequences.Injectivity R
open import Definition.Typed.Consequences.Reduction R
open import Definition.Typed.Consequences.NeTypeEq R

open import Tools.Function
open import Tools.Nat
open import Tools.Product
import Tools.PropositionalEquality as PE

private
  variable
    n     : Nat
    Ī“     : Con Term n
    A B   : Term _
    l₁ lā‚‚ : Universe-level

mutual
  -- Algorithmic equality of neutrals is well-formed.
  soundness~↑ : āˆ€ {k l A} → Ī“ ⊢ k ~ l ↑ A → Ī“ ⊢ k ≔ l ∷ A
  soundness~↑ (var-refl x x≔y) = PE.subst (Ī» y → _ ⊢ _ ≔ var y ∷ _) x≔y (refl x)
  soundness~↑ (app-cong k~l x₁) =
    app-cong (soundness~↓ k~l) (soundnessConv↑Term x₁)
  soundness~↑ (fst-cong x) =
    let p≔ = soundness~↓ x
        ⊢ΣFG = proj₁ (syntacticEqTerm p≔)
        _ , ⊢G , _ = inversion-ΠΣ ⊢ΣFG
    in  fst-cong ⊢G p≔
  soundness~↑ (snd-cong x) =
    let p≔ = soundness~↓ x
        ⊢ΣFG = proj₁ (syntacticEqTerm p≔)
        _ , ⊢G , _ = inversion-ΠΣ ⊢ΣFG
    in  snd-cong ⊢G p≔
  soundness~↑ (natrec-cong x₁ xā‚‚ xā‚ƒ k~l) =
    let F≔G = soundnessConv↑ x₁ in
    natrec-cong F≔G (soundnessConv↑Term xā‚‚) (soundnessConv↑Term xā‚ƒ)
      (soundness~↓ k~l)
  soundness~↑ (prodrec-cong x x₁ xā‚‚) =
    let C≔E = soundnessConv↑ x
        g≔h = soundness~↓ x₁
        u≔v = soundnessConv↑Term xā‚‚
        _ , _ , ok = inversion-ΠΣ (proj₁ (syntacticEqTerm g≔h))
    in  prodrec-cong C≔E g≔h u≔v ok
  soundness~↑ (emptyrec-cong x₁ k~l) =
    emptyrec-cong (soundnessConv↑ x₁) (soundness~↓ k~l)
  soundness~↑ (unitrec-cong x x₁ xā‚‚ no-Ī·) =
    let F≔H = soundnessConv↑ x
        k≔l = soundness~↓ x₁
        u≔v = soundnessConv↑Term xā‚‚
        ok = inversion-Unit (proj₁ (syntacticEqTerm k≔l))
    in  unitrec-cong F≔H k≔l u≔v ok no-Ī·
  soundness~↑ (J-cong A₁≔Aā‚‚ t₁≔tā‚‚ B₁≔Bā‚‚ u₁≔uā‚‚ v₁≔vā‚‚ w₁~wā‚‚ ≔Id) =
    case soundnessConv↑ A₁≔Aā‚‚ of Ī» {
      A₁≔Aā‚‚ →
    case soundnessConv↑Term t₁≔tā‚‚ of Ī» {
      t₁≔tā‚‚ →
    J-cong A₁≔Aā‚‚
      (syntacticEqTerm t₁≔tā‚‚ .projā‚‚ .proj₁) t₁≔tā‚‚ (soundnessConv↑ B₁≔Bā‚‚)
      (soundnessConv↑Term u₁≔uā‚‚) (soundnessConv↑Term v₁≔vā‚‚)
      (conv (soundness~↓ w₁~wā‚‚) ≔Id) }}
  soundness~↑ (K-cong A₁≔Aā‚‚ t₁≔tā‚‚ B₁≔Bā‚‚ u₁≔uā‚‚ v₁~vā‚‚ ≔Id ok) =
    K-cong (soundnessConv↑ A₁≔Aā‚‚) (soundnessConv↑Term t₁≔tā‚‚)
      (soundnessConv↑ B₁≔Bā‚‚) (soundnessConv↑Term u₁≔uā‚‚)
      (conv (soundness~↓ v₁~vā‚‚) ≔Id) ok
  soundness~↑ ([]-cong-cong A₁≔Aā‚‚ t₁≔tā‚‚ u₁≔uā‚‚ v₁~vā‚‚ ≔Id ok) =
    []-cong-cong (soundnessConv↑ A₁≔Aā‚‚) (soundnessConv↑Term t₁≔tā‚‚)
      (soundnessConv↑Term u₁≔uā‚‚) (conv (soundness~↓ v₁~vā‚‚) ≔Id) ok

  -- Algorithmic equality of neutrals in WHNF is well-formed.
  soundness~↓ : āˆ€ {k l A} → Ī“ ⊢ k ~ l ↓ A → Ī“ ⊢ k ≔ l ∷ A
  soundness~↓ ([~] A₁ (D , _) k~l) = conv (soundness~↑ k~l) (subset* D)

  -- Algorithmic equality of types is well-formed.
  soundnessConv↑ : āˆ€ {A B} → Ī“ ⊢ A [conv↑] B → Ī“ ⊢ A ≔ B
  soundnessConv↑ ([↑] _ _ (D , _) (D′ , _) A′<>B′) =
    trans (subset* D) (trans (soundnessConv↓ A′<>B′) (sym (subset* D′)))

  -- Algorithmic equality of types in WHNF is well-formed.
  soundnessConv↓ : āˆ€ {A B} → Ī“ ⊢ A [conv↓] B → Ī“ ⊢ A ≔ B
  soundnessConv↓ (U-refl āŠ¢Ī“) = refl (Uā±¼ āŠ¢Ī“)
  soundnessConv↓ (ā„•-refl āŠ¢Ī“) = refl (ā„•ā±¼ āŠ¢Ī“)
  soundnessConv↓ (Empty-refl āŠ¢Ī“) = refl (Emptyā±¼ āŠ¢Ī“)
  soundnessConv↓ (Unit-refl āŠ¢Ī“ ok) = refl (Unitā±¼ āŠ¢Ī“ ok)
  soundnessConv↓ (ne x) = univ (soundness~↓ x)
  soundnessConv↓ (ΠΣ-cong A₁≔Aā‚‚ B₁≔Bā‚‚ ok) =
    ΠΣ-cong (soundnessConv↑ A₁≔Aā‚‚) (soundnessConv↑ B₁≔Bā‚‚) ok
  soundnessConv↓ (Id-cong A₁≔Aā‚‚ t₁≔tā‚‚ u₁≔uā‚‚) =
    Id-cong (soundnessConv↑ A₁≔Aā‚‚) (soundnessConv↑Term t₁≔tā‚‚)
      (soundnessConv↑Term u₁≔uā‚‚)

  -- Algorithmic equality of terms is well-formed.
  soundnessConv↑Term : āˆ€ {a b A} → Ī“ ⊢ a [conv↑] b ∷ A → Ī“ ⊢ a ≔ b ∷ A
  soundnessConv↑Term ([↑]ā‚œ B t′ u′ (D , _) (d , _) (d′ , _) t<>u) =
    conv (trans (subset*Term d)
                (trans (soundnessConv↓Term t<>u)
                       (sym′ (subset*Term d′))))
         (sym (subset* D))

  -- Algorithmic equality of terms in WHNF is well-formed.
  soundnessConv↓Term : āˆ€ {a b A} → Ī“ ⊢ a [conv↓] b ∷ A → Ī“ ⊢ a ≔ b ∷ A
  soundnessConv↓Term (ā„•-ins x) = soundness~↓ x
  soundnessConv↓Term (Empty-ins x) = soundness~↓ x
  soundnessConv↓Term (UnitŹ·-ins _ t~u) = soundness~↓ t~u
  soundnessConv↓Term (Σʷ-ins x x₁ xā‚‚) =
    let a≔b = soundness~↓ xā‚‚
        _ , neA , _ = ne~↓ xā‚‚
        _ , ⊢a , _ = syntacticEqTerm a≔b
        Σ≔Σ′ = neTypeEq neA x ⊢a
    in  conv a≔b (sym Σ≔Σ′)
  soundnessConv↓Term (ne-ins t u x x₁) =
    let _ , neA , _ = ne~↓ x₁
        _ , t∷M , _ = syntacticEqTerm (soundness~↓ x₁)
        M≔A = neTypeEq neA t∷M t
    in  conv (soundness~↓ x₁) M≔A
  soundnessConv↓Term (univ ⊢A ⊢B A≔B) =
    soundnessConv↓-U ⊢A ⊢B A≔B .proj₁
  soundnessConv↓Term (zero-refl āŠ¢Ī“) = refl (zeroā±¼ āŠ¢Ī“)
  soundnessConv↓Term (starŹ·-refl āŠ¢Ī“ ok _) = refl (starā±¼ āŠ¢Ī“ ok)
  soundnessConv↓Term (suc-cong c) = suc-cong (soundnessConv↑Term c)
  soundnessConv↓Term (prod-cong x₁ xā‚‚ xā‚ƒ ok) =
    prod-cong x₁ (soundnessConv↑Term xā‚‚)
      (soundnessConv↑Term xā‚ƒ) ok
  soundnessConv↓Term (Ī·-eq x x₁ y y₁ c) =
    Ī·-eq′ x x₁ (soundnessConv↑Term c)
  soundnessConv↓Term (Ī£-Ī· ⊢p ⊢r pProd rProd fstConv sndConv) =
    let fst≔ = soundnessConv↑Term fstConv
        snd≔ = soundnessConv↑Term sndConv
    in  Ī£-η′ ⊢p ⊢r fst≔ snd≔
  soundnessConv↓Term (Ī·-unit [a] [b] aUnit bUnit ok) =
    Ī·-unit [a] [b] ok
  soundnessConv↓Term
    {Ī“} (Id-ins {v₁} {t} {u} {A} {A′} {t′} {u′} ⊢v₁ v₁~vā‚‚) =
    case soundness~↓ v₁~vā‚‚ of Ī» {
      v₁≔vā‚‚ →
    conv v₁≔vā‚‚
      (                                          $⟨ syntacticEqTerm v₁≔vā‚‚ .projā‚‚ .proj₁ , ⊢v₁ ⟩
       Ī“ ⊢ v₁ ∷ Id A′ t′ u′ Ɨ Ī“ ⊢ v₁ ∷ Id A t u  ā†’āŸØ uncurry (neTypeEq (ne~↓ v₁~vā‚‚ .projā‚‚ .proj₁)) ⟩
       Ī“ ⊢ Id A′ t′ u′ ≔ Id A t u                ā–”) }
  soundnessConv↓Term (rfl-refl t≔u) =
    refl (rflⱼ′ t≔u)

  -- A variant of soundnessConv↓.

  soundnessConv↓-U :
    Ī“ ⊢ A ∷ U l₁ →
    Ī“ ⊢ B ∷ U lā‚‚ →
    Ī“ ⊢ A [conv↓] B →
    Ī“ ⊢ A ≔ B ∷ U l₁ Ɨ l₁ PE.≔ lā‚‚
  soundnessConv↓-U {l₁} {lā‚‚} ⊢A ⊢B (ne {l} A~B) =
    let A≔B             = soundness~↓ A~B
        _ , A-ne , B-ne = ne~↓ A~B
        _ , ⊢A′ , ⊢B′   = syntacticEqTerm A≔B
        U≔U₁            = neTypeEq A-ne ⊢A′ ⊢A
        U≔Uā‚‚            = neTypeEq B-ne ⊢B′ ⊢B
    in
      conv A≔B U≔U₁
    , U-injectivity
        (U l₁  ā‰”Ė˜āŸØ U≔U₁ ⟩⊢
         U l   ā‰”āŸØ U≔Uā‚‚ āŸ©āŠ¢āˆŽ
         U lā‚‚  āˆŽ)
    where
    open TyR
  soundnessConv↓-U {l₁} {lā‚‚} ⊢U₁ ⊢Uā‚‚ (U-refl {l} _) =
      refl ⊢U₁
    , U-injectivity
        (U l₁      ā‰”āŸØ inversion-U ⊢U₁ ⟩⊢
         U (1+ l)  ā‰”Ė˜āŸØ inversion-U ⊢Uā‚‚ āŸ©āŠ¢āˆŽ
         U lā‚‚      āˆŽ)
    where
    open TyR
  soundnessConv↓-U {l₁} {lā‚‚} ⊢ΠΣA₁Aā‚‚ ⊢ΠΣB₁Bā‚‚ (ΠΣ-cong A₁≔B₁ A₂≔Bā‚‚ ok) =
    let lā‚ƒ , lā‚„ , ⊢A₁ , ⊢Aā‚‚ , U≔U₁ , _ = inversion-ΠΣ-U ⊢ΠΣA₁Aā‚‚
        lā‚… , l₆ , ⊢B₁ , ⊢Bā‚‚ , U≔Uā‚‚ , _ = inversion-ΠΣ-U ⊢ΠΣB₁Bā‚‚
        A₁≔B₁ , lā‚ƒā‰”lā‚…                  = soundnessConv↑-U ⊢A₁ ⊢B₁ A₁≔B₁
        A₂≔Bā‚‚ , l₄≔l₆                  =
          soundnessConv↑-U ⊢Aā‚‚
            (stabilityTerm (refl-āˆ™ (sym (univ A₁≔B₁))) ⊢Bā‚‚) A₂≔Bā‚‚
    in
      conv (ΠΣ-cong A₁≔B₁ A₂≔Bā‚‚ ok) (sym U≔U₁)
    , U-injectivity
        (U l₁          ā‰”āŸØ U≔U₁ ⟩⊢
         U (lā‚ƒ āŠ”įµ˜ lā‚„)  ā‰”āŸØ PE.cong U $ PE.congā‚‚ _āŠ”įµ˜_ lā‚ƒā‰”lā‚… l₄≔l₆ āŸ©āŠ¢ā‰”
         U (lā‚… āŠ”įµ˜ l₆)  ā‰”Ė˜āŸØ U≔Uā‚‚ āŸ©āŠ¢āˆŽ
         U lā‚‚          āˆŽ)
    where
    open TyR
  soundnessConv↓-U {l₁} {lā‚‚} ⊢Empty₁ ⊢Emptyā‚‚ (Empty-refl _) =
      refl ⊢Empty₁
    , U-injectivity
        (U l₁  ā‰”āŸØ inversion-Empty ⊢Empty₁ ⟩⊢
         U 0   ā‰”Ė˜āŸØ inversion-Empty ⊢Emptyā‚‚ āŸ©āŠ¢āˆŽ
         U lā‚‚  āˆŽ)
    where
    open TyR
  soundnessConv↓-U {l₁} {lā‚‚} ⊢Unit₁ ⊢Unitā‚‚ (Unit-refl {l} _ _) =
      refl ⊢Unit₁
    , U-injectivity
        (U l₁  ā‰”āŸØ inversion-Unit-U ⊢Unit₁ .proj₁ ⟩⊢
         U l   ā‰”Ė˜āŸØ inversion-Unit-U ⊢Unitā‚‚ .proj₁ āŸ©āŠ¢āˆŽ
         U lā‚‚  āˆŽ)
    where
    open TyR
  soundnessConv↓-U {l₁} {lā‚‚} āŠ¢ā„•ā‚ āŠ¢ā„•ā‚‚ (ā„•-refl _) =
      refl āŠ¢ā„•ā‚
    , U-injectivity
        (U l₁  ā‰”āŸØ inversion-ā„• āŠ¢ā„•ā‚ ⟩⊢
         U 0   ā‰”Ė˜āŸØ inversion-ā„• āŠ¢ā„•ā‚‚ āŸ©āŠ¢āˆŽ
         U lā‚‚  āˆŽ)
    where
    open TyR
  soundnessConv↓-U
    {l₁} {lā‚‚} ⊢IdAt₁tā‚‚ ⊢IdBu₁uā‚‚ (Id-cong A≔B t₁≔u₁ t₂≔uā‚‚) =
    let lā‚ƒ , ⊢A , ⊢t₁ , ⊢tā‚‚ , U≔U₁ = inversion-Id-U ⊢IdAt₁tā‚‚
        lā‚„ , ⊢B , ⊢u₁ , ⊢uā‚‚ , U≔Uā‚‚ = inversion-Id-U ⊢IdBu₁uā‚‚
        A≔B , lā‚ƒā‰”lā‚„                = soundnessConv↑-U ⊢A ⊢B A≔B
    in
      conv
        (Id-cong A≔B (soundnessConv↑Term t₁≔u₁)
           (soundnessConv↑Term t₂≔uā‚‚))
        (sym U≔U₁)
    , U-injectivity
        (U l₁  ā‰”āŸØ U≔U₁ ⟩⊢
         U lā‚ƒ  ā‰”āŸØ PE.cong U lā‚ƒā‰”lā‚„ āŸ©āŠ¢ā‰”
         U lā‚„  ā‰”Ė˜āŸØ U≔Uā‚‚ āŸ©āŠ¢āˆŽ
         U lā‚‚  āˆŽ)
    where
    open TyR

  -- A variant of soundnessConv↑.

  soundnessConv↑-U :
    Ī“ ⊢ A ∷ U l₁ → Ī“ ⊢ B ∷ U lā‚‚ → Ī“ ⊢ A [conv↑] B →
    Ī“ ⊢ A ≔ B ∷ U l₁ Ɨ l₁ PE.≔ lā‚‚
  soundnessConv↑-U {A} {l₁} {B} {lā‚‚} ⊢A ⊢B ([↑] A′ B′ Aā†˜A′ Bā†˜B′ A′≔B′) =
    let A″ , A″-type , A⇒*A″ = red-U ⊢A
        B″ , B″-type , B⇒*B″ = red-U ⊢B
        _ , _ , ⊢A″ = wf-āŠ¢ā‰”āˆ· (subset*Term A⇒*A″)
        _ , _ , ⊢B″ = wf-āŠ¢ā‰”āˆ· (subset*Term B⇒*B″)
        A′≔A″ = whrDet* Aā†˜A′ (univ* A⇒*A″ , typeWhnf A″-type)
        B′≔B″ = whrDet* Bā†˜B′ (univ* B⇒*B″ , typeWhnf B″-type)
        A′≔B′ , l₁≔lā‚‚ =
          soundnessConv↓-U (PE.subst (_ ⊢_∷ _) (PE.sym A′≔A″) ⊢A″)
            (PE.subst (_ ⊢_∷ _) (PE.sym B′≔B″) ⊢B″) A′≔B′
    in
      (A          ⇒*⟨ A⇒*A″ ⟩⊢
       A″         ā‰”Ė˜āŸØ A′≔A″ āŸ©āŠ¢ā‰”
       A′ ∷ U l₁  ā‰”āŸØ A′≔B′ ⟩⊢∷
                   ⟨ PE.cong U l₁≔lā‚‚ āŸ©ā‰”ā‰”
       B′ ∷ U lā‚‚  ā‰”āŸØ B′≔B″ āŸ©āŠ¢āˆ·ā‰”
       B″         ⇐*⟨ B⇒*B″ āŸ©āŠ¢āˆŽ
       B          āˆŽ)
    , l₁≔lā‚‚
    where
    open TmR