{-# OPTIONS --safe #-}

module Interface.ComputationalRelation where

open import Prelude
open import Interface.STS public

private variable
  a : Level
  C S Sig : Type
  Err Err₁ Err₂ : Type
  c : C
  s s' s'' : S
  sig : Sig

data ComputationResult {a : Level} (Err : Type) (R : Type a) : Type a where
  success : R  ComputationResult Err R
  failure : Err  ComputationResult Err R

isFailure :  {A : Type a}  ComputationResult Err A  Type a
isFailure x = ∃[ e ] x  failure e

module _ {a b} {E : Type} {A : Type a} {B : Type b} where
  caseCR_∣_∣_ : (ma : ComputationResult E A)  (∀ {a}  ma  success a  B)  (isFailure ma  B)  B
  caseCR ma  f  g with ma
  ... | success _ = f refl
  ... | failure e = g (e , refl)

  caseCR-success :  {a} {ma : ComputationResult E A} {f :  {a}  ma  success a  B} {g : isFailure ma  B}
     (eq : ma  success a)
     caseCR ma  f  g  f eq
  caseCR-success refl = refl

  caseCR-failure : {ma : ComputationResult E A} {f :  {a}  ma  success a  B} {g : isFailure ma  B}
     (eq : isFailure ma)
     caseCR ma  f  g  g eq
  caseCR-failure (_ , refl) = refl

instance
  Bifunctor-ComputationResult :  {a : Level}  Bifunctor {_} {a} ComputationResult
  Bifunctor-ComputationResult .bimap _ f (success x) = success $ f x
  Bifunctor-ComputationResult .bimap f _ (failure x) = failure $ f x

  Functor-ComputationResult :  {E : Type}  Functor (ComputationResult E)
  Functor-ComputationResult ._<$>_ f (success x) = success $ f x
  Functor-ComputationResult ._<$>_ _ (failure x) = failure x

  Applicative-ComputationResult :  {E : Type}  Applicative (ComputationResult E)
  Applicative-ComputationResult .pure = success
  Applicative-ComputationResult ._<*>_ (success f) x = f <$> x
  Applicative-ComputationResult ._<*>_ (failure e) _ = failure e

  Monad-ComputationResult :  {E : Type}  Monad (ComputationResult E)
  Monad-ComputationResult .return = success
  Monad-ComputationResult ._>>=_ (success a) m = m a
  Monad-ComputationResult ._>>=_ (failure e) _ = failure e

map-failure :  {A B C : Type} {f : A  B} {x : C} {ma}  ma  failure x  map f ma  failure x
map-failure refl = refl

success-maybe :  {R : Type}  ComputationResult Err R  Maybe R
success-maybe (success x) = just x
success-maybe (failure _) = nothing

failure-maybe :  {R : Type}  ComputationResult Err R  Maybe Err
failure-maybe (success _) = nothing
failure-maybe (failure x) = just x

_≈ᶜʳ_ :  {A}  ComputationResult Err A  ComputationResult Err A  Type
x ≈ᶜʳ y = success-maybe x  success-maybe y

module _ (STS : C  S  Sig  S  Type) where

  ExtendedRel : C  S  Sig  ComputationResult Err S  Type
  ExtendedRel c s sig (success s') = STS c s sig s'
  ExtendedRel c s sig (failure _ ) =  s'  ¬ STS c s sig s'

  record Computational Err : Type₁ where
    constructor MkComputational
    field
      computeProof : (c : C) (s : S) (sig : Sig)  ComputationResult Err (∃[ s' ] STS c s sig s')

    compute : C  S  Sig  ComputationResult Err S
    compute c s sig = map proj₁ $ computeProof c s sig

    field
      completeness : (c : C) (s : S) (sig : Sig) (s' : S)
         STS c s sig s'  compute c s sig  success s'

    open ≡-Reasoning

    computeFail : C  S  Sig  Type
    computeFail c s sig = isFailure $ compute c s sig

    ≡-success⇔STS : compute c s sig  success s'  STS c s sig s'
    ≡-success⇔STS {c} {s} {sig} {s'} with computeProof c s sig in eq
    ... | success (s'' , h) = mk⇔  where refl  h) λ h' 
      begin success s''     ≡˘⟨ completeness _ _ _ _ h 
            compute c s sig ≡⟨ completeness _ _ _ _ h' 
            success s'      
    ... | failure y = mk⇔  ()) λ h 
      begin failure _       ≡˘⟨ map-failure eq 
            compute c s sig ≡⟨ completeness _ _ _ _ h 
            success s'      

    failure⇒∀¬STS : computeFail c s sig   s'  ¬ STS c s sig s'
    failure⇒∀¬STS comp≡nothing s' h rewrite ≡-success⇔STS .Equivalence.from h =
      case comp≡nothing of λ ()

    ∀¬STS⇒failure : (∀ s'  ¬ STS c s sig s')  computeFail c s sig
    ∀¬STS⇒failure {c = c} {s} {sig} ¬sts with computeProof c s sig
    ... | success (x , y) = ⊥-elim (¬sts x y)
    ... | failure y = (y , refl)

    failure⇔∀¬STS : computeFail c s sig   s'  ¬ STS c s sig s'
    failure⇔∀¬STS = mk⇔ failure⇒∀¬STS ∀¬STS⇒failure

    recomputeProof :  {Γ s sig s'}  STS Γ s sig s'  ComputationResult Err (∃[ s'' ] STS Γ s sig s'')
    recomputeProof _ = computeProof _ _ _

module _ {STS : C  S  Sig  S  Type} (comp : Computational STS Err) where

  open Computational comp

  ExtendedRelSTS = ExtendedRel STS

  ExtendedRel-compute : ExtendedRelSTS c s sig (compute c s sig)
  ExtendedRel-compute {c} {s} {sig} with compute c s sig in eq
  ... | success s' = Equivalence.to ≡-success⇔STS eq
  ... | failure _  = λ s' h  case trans (sym $ Equivalence.from ≡-success⇔STS h) eq of λ ()

  open ≡-Reasoning

  ExtendedRel-rightUnique : ExtendedRelSTS c s sig s'  ExtendedRelSTS c s sig s''  _≈ᶜʳ_ {Err = Err} s' s''
  ExtendedRel-rightUnique {s' = success x}  {success x'} h h'
    with trans (sym $ Equivalence.from ≡-success⇔STS h) (Equivalence.from ≡-success⇔STS h')
  ... | refl = refl

  ExtendedRel-rightUnique {s' = success x} {failure _}  h h' = ⊥-elim $ h' x h
  ExtendedRel-rightUnique {s' = failure _} {success x'} h h' = ⊥-elim $ h x' h'
  ExtendedRel-rightUnique {s' = failure a} {failure b}  h h' = refl

  computational⇒rightUnique : STS c s sig s'  STS c s sig s''  s'  s''
  computational⇒rightUnique h h' with ExtendedRel-rightUnique h h'
  ... | refl = refl

  Computational⇒Dec :  _ : DecEq S   Dec (STS c s sig s')
  Computational⇒Dec {c} {s} {sig} {s'}
    with compute c s sig | ExtendedRel-compute {c} {s} {sig}
  ... | failure _ | ExSTS = no (ExSTS s')
  ... | success x  | ExSTS with x  s'
  ... | no ¬p    = no  λ h  ¬p $ sym $ computational⇒rightUnique h ExSTS
  ... | yes refl = yes ExSTS

module _ {STS : C  S  Sig  S  Type} (comp comp' : Computational STS Err) where

  open Computational comp  renaming (compute to compute₁)
  open Computational comp' renaming (compute to compute₂)

  compute-ext≡ : compute₁ c s sig ≈ᶜʳ compute₂ c s sig
  compute-ext≡ = ExtendedRel-rightUnique comp
    (ExtendedRel-compute comp) (ExtendedRel-compute comp')

open Computational ⦃...⦄

Computational⇒Dec' :
  {STS : C  S  Sig  S  Type}  comp : Computational STS Err   Dec (∃[ s' ] STS c s sig s')
Computational⇒Dec' with computeProof _ _ _ in eq
... | success x = yes x
... | failure x = no λ (_ , h)  failure⇒∀¬STS (-, cong (map proj₁) eq) _ h

record InjectError Err₁ Err₂ : Type where
  field injectError : Err₁  Err₂

open InjectError

instance
  InjectError-⊥ : InjectError  Err
  InjectError-⊥ = λ where
    .injectError ()

  InjectError-Id : InjectError Err Err
  InjectError-Id = λ where
    .injectError  id

  Computational-Id : {C S : Type}  Computational (IdSTS {C} {S}) 
  Computational-Id .computeProof _ s _ = success (s , Id-nop)
  Computational-Id .completeness _ _ _ _ Id-nop = refl

module _ {BSTS : C  S    S  Type}  _ : Computational BSTS Err₁  where
  module _ {STS : C  S  Sig  S  Type}  _ : Computational STS Err₂ 
      _ : InjectError Err₁ Err   _ : InjectError Err₂ Err  where instance
    Computational-ReflexiveTransitiveClosureᵇ : Computational (ReflexiveTransitiveClosureᵇ {_⊢_⇀⟦_⟧ᵇ_ = BSTS}{STS}) (Err)
    Computational-ReflexiveTransitiveClosureᵇ .computeProof c s [] = bimap (injectError it) (map₂′ BS-base) (computeProof c s tt)
    Computational-ReflexiveTransitiveClosureᵇ .computeProof c s (sig  sigs) with computeProof c s sig
    ... | success (s₁ , h) with computeProof c s₁ sigs
    ...   | success (s₂ , hs) = success (s₂ , BS-ind h hs)
    ...   | failure a = failure (injectError it a)
    Computational-ReflexiveTransitiveClosureᵇ .computeProof c s (sig  sigs) | failure a = failure (injectError it a)
    Computational-ReflexiveTransitiveClosureᵇ .completeness c s [] s' (BS-base p)
      with computeProof {STS = BSTS} c s tt | completeness _ _ _ _ p
    ... | success x | refl = refl
    Computational-ReflexiveTransitiveClosureᵇ .completeness c s (sig  sigs) s' (BS-ind h hs)
      with computeProof c s sig | completeness _ _ _ _ h
    ... | success (s₁ , _) | refl
      with computeProof  Computational-ReflexiveTransitiveClosureᵇ  c s₁ sigs | completeness _ _ _ _ hs
    ... | success (s₂ , _) | p = p

  module _ {STS : C ×   S  Sig  S  Type}  Computational-STS : Computational STS Err₂ 
     InjectError-Err₁ : InjectError Err₁ Err   InjectError-Err₂ : InjectError Err₂ Err 
    where instance
    Computational-ReflexiveTransitiveClosureᵢᵇ' : Computational (_⊢_⇀⟦_⟧ᵢ*'_ {_⊢_⇀⟦_⟧ᵇ_ = BSTS}{STS}) Err
    Computational-ReflexiveTransitiveClosureᵢᵇ' .computeProof c s [] =
      bimap (injectError it) (map₂′ BS-base) (computeProof (proj₁ c) s tt)
    Computational-ReflexiveTransitiveClosureᵢᵇ' .computeProof c s (sig  sigs) with computeProof c s sig
    ... | success (s₁ , h) with computeProof (proj₁ c , suc (proj₂ c)) s₁ sigs
    ... | success (s₂ , hs) = success (s₂ , BS-ind h hs)
    ... | failure a = failure a
    Computational-ReflexiveTransitiveClosureᵢᵇ' .computeProof c s (sig  sigs) | failure a = failure (injectError it a)
    Computational-ReflexiveTransitiveClosureᵢᵇ' .completeness c s [] s' (BS-base p)
      with computeProof {STS = BSTS} (proj₁ c) s tt | completeness _ _ _ _ p
    ... | success x | refl = refl
    Computational-ReflexiveTransitiveClosureᵢᵇ' .completeness c s (sig  sigs) s' (BS-ind h hs)
      with computeProof {STS = STS} c s sig | completeness _ _ _ _ h
    ... | success (s₁ , _) | refl
      with computeProof (proj₁ c , suc (proj₂ c)) s₁ sigs | completeness _ _ _ _ hs
    ...   | success (s₂ , _) | p = p

    Computational-ReflexiveTransitiveClosureᵢᵇ : Computational (ReflexiveTransitiveClosureᵢᵇ {_⊢_⇀⟦_⟧ᵇ_ = BSTS}{STS}) Err
    Computational-ReflexiveTransitiveClosureᵢᵇ .computeProof c =
      Computational-ReflexiveTransitiveClosureᵢᵇ' .computeProof (c , 0)
    Computational-ReflexiveTransitiveClosureᵢᵇ .completeness c =
      Computational-ReflexiveTransitiveClosureᵢᵇ' .completeness (c , 0)

module _ {BSTS : C  S    S  Type}  _ : Computational BSTS Err₁  where
  module _ {STS : C  S  Sig  S  Type}  _ : Computational STS Err₂ 
      _ : InjectError Err₁ Err   _ : InjectError Err₂ Err  where instance
    Computational-ReflexiveTransitiveClosureᵇ' : Computational (ReflexiveTransitiveClosureᵇ' {_⊢_⇀⟦_⟧ᵇ_ = BSTS} {STS}) (Err)
    Computational-ReflexiveTransitiveClosureᵇ' .computeProof c s sigs with (computeProof{STS = BSTS} c s tt)
    ... | failure a = failure (injectError it a)
    ... | success (s' , h) with (computeProof{STS = ReflexiveTransitiveClosureᵇ {_⊢_⇀⟦_⟧ᵇ_ = IdSTS} {STS}} c s' sigs)
    ... | failure a = failure a
    ... | success (s' , hs) = success (s' , (RTC (h , hs)))

    Computational-ReflexiveTransitiveClosureᵇ' .completeness c s sigs s'' (RTC (bsts , sts))
      with (computeProof {STS = BSTS} c s tt) | completeness _ _ _ _ bsts
    ... | success (s₁ , h) | refl
      with  computeProof {STS = ReflexiveTransitiveClosureᵇ {_⊢_⇀⟦_⟧ᵇ_ = IdSTS} {STS}}{Err} c s₁ sigs
         |  completeness {Err = Err} _ _ _ _ sts
    ... | success (s₂ , _) | p = p


Computational-ReflexiveTransitiveClosure : {STS : C  S  Sig  S  Type}   Computational STS Err 
   Computational (ReflexiveTransitiveClosure {sts = STS}) Err
Computational-ReflexiveTransitiveClosure = it

Computational-ReflexiveTransitiveClosureᵢ : {STS : C ×   S  Sig  S  Type}   Computational STS Err 
   Computational (ReflexiveTransitiveClosureᵢ {sts = STS}) Err
Computational-ReflexiveTransitiveClosureᵢ = it