open import DynamicallyChecked.Universe
open import Data.Nat

module HoareLogic.Completeness {n : } {F : Functor n} where

open import DynamicallyChecked.Utilities
open import DynamicallyChecked.Partiality
open import DynamicallyChecked.Lens
open import DynamicallyChecked.Case as Case
open import DynamicallyChecked.Rearrangement
open import DynamicallyChecked.BiGUL
open import HoareLogic.Semantics
open import HoareLogic.Rearrangement
open import HoareLogic.Case
open import HoareLogic.Triple

open import Function
open import Data.Product as Product
open import Data.Sum
open import Data.Bool
open import Data.Maybe
open import Data.List as List
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality


skip-branch :
  {S V : U n} (bs bs' : List (CaseBranch F S V)) (cont :  S  (μ F)  Par ( S  (μ F)))
  (s' s :  S  (μ F)) (v :  V  (μ F)) 
  put-with-adaptation ( S  (μ F)) ( V  (μ F)) (interp-CaseBranch (revcat bs' bs)) [] s v cont  s' 
  OutOfDomain (List.map proj₁ bs') (s , v) 
  put-with-adaptation ( S  (μ F)) ( V  (μ F)) (interp-CaseBranch bs) (interp-CaseBranch bs') s v cont  s'
skip-branch bs [] cont s' s v seq out = seq
skip-branch bs ((p , normal b q)  bs') cont s' s v seq (p-s-v≡false , out)
    with skip-branch ((p , normal b q)  bs) bs' cont s' s v seq out
... | c rewrite p-s-v≡false = c
skip-branch bs ((p , adaptive f)  bs') cont s' s v seq (p-s-v≡false , out)
    with skip-branch ((p , adaptive f)  bs) bs' cont s' s v seq out
... | c rewrite p-s-v≡false = c

project-normal-branch :
  {S V : U n} (bs bs' : List (CaseBranch F S V))
  (p :  S  (μ F)   V  (μ F)  Bool) (b : BiGUL F S V) (q :  S  (μ F)  Bool)
  (cont :  S  (μ F)  Par ( S  (μ F))) (s' s :  S  (μ F)) (v :  V  (μ F))  p s v  true 
  put-with-adaptation ( S  (μ F)) ( V  (μ F))
    (interp-CaseBranch ((p , normal b q)  bs)) (interp-CaseBranch bs') s v cont  s' 
  Lens.put (interp b) s v  s'
project-normal-branch bs bs' p b q cont s' s v p-s-v≡true seq rewrite p-s-v≡true with seq
... | c >>= assert _ then assert _ then _ >>= return refl = c

project-adaptive-branch :
  {S V : U n} (bs bs' : List (CaseBranch F S V))
  (p :  S  (μ F)   V  (μ F)  Bool) (f :  S  (μ F)   V  (μ F)   S  (μ F))
  (cont :  S  (μ F)  Par ( S  (μ F))) (s' s :  S  (μ F)) (v :  V  (μ F))  p s v  true 
  put-with-adaptation ( S  (μ F)) ( V  (μ F))
    (interp-CaseBranch ((p , adaptive f)  bs)) (interp-CaseBranch bs') s v cont  s' 
  cont (f s v)  s'
project-adaptive-branch bs bs' p f cont s' s v p-s-v≡true c rewrite p-s-v≡true = c

mutual

  semantics-triple :
    {S V : U n} (b : BiGUL F S V) 
    Triple  { (s , v)  Σ[ s'   S  (μ F) ] runPar (Lens.put (interp b) s v)  just s' })
           b
            { (s' , s , v)  runPar (Lens.put (interp b) s v)  just s' })
  semantics-triple fail = conseq  { (_ , ()) }) fail  { (() , _) })
  semantics-triple (skip {S} {V} f) =
    conseq (aux-pre  toCompSeq  proj₂)
           skip
            { {.s , s , v} (refl , _ , eq)  fromCompSeq (aux-post (aux-pre (toCompSeq eq))) })
    where
      aux-pre : {s' s :  S  (μ F)} {v :  V  (μ F)}  Lens.put (interp (skip {S = S} {V} f)) s v  s'  f s  v
      aux-pre {s'} {s} {v} c with U-dec V (f s) v
      aux-pre (return refl) | yes f-s≡v = f-s≡v
      aux-pre ()            | no  _
      aux-post : {s :  S  (μ F)} {v :  V  (μ F)}  f s  v  Lens.put (interp (skip {S = S} {V} f)) s v  s
      aux-post {s} {v} f-s≡v with U-dec V (f s) v
      aux-post {s} {v} f-s≡v | yes _     = return refl
      aux-post {s} {v} f-s≡v | no  f-s≢v with f-s≢v f-s≡v
      aux-post {s} {v} f-s≡v | no  f-s≢v | ()
  semantics-triple replace = conseq  _  tt) replace  { (eq , _)  cong just (sym eq) })
  semantics-triple (prod {Sl} {Vl} {Sr} {Vr} l r) =
    conseq  { ((sl' , sr') , eq) 
                case toCompSeq {mx = Lens.put (interp (prod l r)) _ _} eq of λ {
                  (cl >>= cr >>= return refl)  (sl' , fromCompSeq {mx = Lens.put (interp l) _ _} cl) ,
                                                (sr' , fromCompSeq {mx = Lens.put (interp r) _ _} cr) } })
           (prod (semantics-triple l) (semantics-triple r))
            { ((eql , eqr) , _) 
                fromCompSeq {mx = Lens.put (interp (prod l r)) _ _} (toCompSeq eql >>= toCompSeq eqr >>= return refl) })
  semantics-triple (rearrS spat spat' expr c b) =
    conseq  { {s , v} (s' , eq) 
                case toCompSeq {mx = Lens.put (interp (rearrS spat spat' expr c b)) _ _} eq of λ {
                  ((deconstruct↦ >>= return refl) >>= put-b↦ >>= put-iso↦) 
                    case Iso.from-to-inverse (rearrangement-iso spat spat' expr c) put-iso↦ of λ {
                      (_ >>= return eq')  _ , toMatch spat s deconstruct↦ ,
                                           _ , trans (fromCompSeq put-b↦) (cong just (sym eq')) } } })
           (rearrS  { (res , v) 
                         λ res'  runPar (Lens.put (interp b) (construct spat' (eval spat spat' expr res)) v) 
                                     just (construct spat' (eval spat spat' expr res')) })
                    { (res' , res , v) 
                        runPar (Lens.put (interp b) (construct spat' (eval spat spat' expr res)) v) 
                          just (construct spat' (eval spat spat' expr res')) })
                   (conseq  { {t , v} (res , e , res' , eq) 
                                construct spat' (eval spat spat' expr res') ,
                                subst  x  runPar (Lens.put (interp b) x v) 
                                               just (construct spat' (eval spat spat' expr res')))
                                      (fromEval spat spat' expr t e) eq })
                           (semantics-triple b)
                            { {t' , t , v} (eq , res , e , res' , eq') 
                                let eq'' = cong-from-just
                                             (trans (sym (subst  x  runPar (Lens.put (interp b) x v) 
                                                                         just (construct spat' (eval spat spat' expr res')))
                                                                (fromEval spat spat' expr _ e) eq'))
                                                    eq)
                                in  (res' , res) , eq' , subst  x  Eval spat spat' expr (res' , x)) eq''
                                                               (toEval spat spat' expr) , e })))
            { {s' , s , v} (((res' , res) , eq , m' , m) , pre) 
                fromCompSeq {mx = Lens.put (interp (rearrS spat spat' expr c b)) _ _}
                  ((fromMatch spat s m >>= return refl) >>= toCompSeq eq >>=
                   Iso.to-from-inverse (rearrangement-iso spat spat' expr c) (fromMatch spat s' m' >>= return refl)) })
  semantics-triple (rearrV vpat vpat' expr c b) =
    conseq  { (s' , eq) 
                case toCompSeq {mx = Lens.put (interp (rearrV vpat vpat' expr c b)) _ _} eq of λ {
                  ((deconstruct↦ >>= return refl) >>= c)  _ , (_ , fromCompSeq c) , toMatch vpat _ deconstruct↦ } })
           (rearrV  { (s , res) 
                         λ s'  runPar (Lens.put (interp b) s (construct vpat' (eval vpat vpat' expr res)))  just s' })
                    { (s' , s , res) 
                        runPar (Lens.put (interp b) s (construct vpat' (eval vpat vpat' expr res)))  just s' })
                   (conseq  { {s , w} (res , ((s' , eq) , e)) 
                                s' , subst  x  runPar (Lens.put (interp b) s x)  just s')
                                           (fromEval vpat vpat' expr w e) eq })
                           (semantics-triple b)
                            { {s' , s , w} (eq , res , _ , e) 
                                res , subst  x  runPar (Lens.put (interp b) s x)  just s')
                                            (sym (fromEval vpat vpat' expr _ e)) eq , e })))
            { ((res , eq , m) , _) 
                fromCompSeq {mx = Lens.put (interp (rearrV vpat vpat' expr c b)) _ _}
                  ((fromMatch vpat _ m >>= return refl) >>= toCompSeq {mx = Lens.put (interp b) _ _} eq) })
  semantics-triple (case bs) = case (semantics-triple-case bs [])
                                     { {sv} (s' , put-s-v≡just-s') 
                                         subst  ps  CaseDomain ps sv) (sym (case-main-cond-lemma bs))
                                               (inCaseDomain (interp-CaseBranch bs) [] (toCompSeq put-s-v≡just-s')) })
  
  semantics-triple-case :
    {S V : U n} (bs bs' : List (CaseBranch F S V)) 
    CaseBranchTriple
       { (s , v)  Σ[ s'   S  (μ F) ] runPar (Lens.put (interp (case (revcat bs' bs))) s v)  just s' })
       { (s' , s , v)  runPar (Lens.put (interp (case (revcat bs' bs))) s v)  just s' })
      (NormalCaseDomain (List.map (Product.map id (elimCaseBranchType  _ _  true)  _  false))) (revcat bs' bs)))
      bs bs'
  semantics-triple-case [] bs' = []
  semantics-triple-case {S} {V} ((p , normal b q)  bs) bs' =
    conseq  { {s , v} ((s' , eq) , p-s-v≡true , OutOfDomain-bs'-s-v) 
                s' , fromCompSeq {mx = Lens.put (interp b) s v}
                       (project-normal-branch bs bs' p b q _ s' s v p-s-v≡true
                          (skip-branch ((p , normal b q)  bs) bs' _ s' s v
                             (toCompSeq {mx = Lens.put (interp (case (revcat bs' ((p , normal b q)  bs)))) s v} eq)
                             OutOfDomain-bs'-s-v)) })
           (semantics-triple b)
            { {s' , s , v} (put-b-s-v≡just-s' , (s'' , put-s-v≡just-s'') , p-s-v≡true , out-s-v) 
                  Product.map fromCompSeq id
                    (aux s' s'' s v (toCompSeq put-b-s-v≡just-s') (toCompSeq put-s-v≡just-s'') p-s-v≡true out-s-v) })
      ∷ᴺ semantics-triple-case bs ((p , normal b q)  bs')
    where
      aux :
        (s' s'' s :  S  (μ F)) (v :  V  (μ F)) 
        Lens.put (interp b) s v  s'  Lens.put (interp (case (revcat bs' ((p , normal b q)  bs)))) s v  s'' 
        p s v  true  OutOfDomain (List.map proj₁ bs') (s , v) 
        Lens.put (interp (case (revcat bs' ((p , normal b q)  bs)))) s v  s' ×
          (p s' v  true × OutOfDomain (List.map proj₁ bs') (s' , v)) × (q s'  true × OutOfRangeB bs' s')
      aux s' s'' s v put-b-s-v↦s' put-s-v↦s'' p-s-v≡true out-s-v
        with skip-branch ((p , normal b q)  bs) bs' _ s'' s v put-s-v↦s'' out-s-v
      aux s' s'' s v put-b-s-v↦s' put-s-v↦s'' p-s-v≡true out-s-v | c rewrite p-s-v≡true with c
      aux s' s'' s v put-b-s-v↦s' put-s-v↦s'' p-s-v≡true out-s-v | _
        | put-b-s-v↦s'' >>= assert p-s'-v≡true then assert q-s'≡true then check-diversion↦ >>= return refl
        with CompSeq-deterministic put-b-s-v↦s' put-b-s-v↦s''
      aux s' .s' s v put-b-s-v↦s' put-s-v↦s' p-s-v≡true out-s-v | _
        | _ >>= assert p-s'-v≡true then assert q-s'≡true then check-diversion↦ >>= return refl | refl =
        let (outd , outr) = check-diversion-inverse (interp-CaseBranch bs') s' v check-diversion↦
        in  put-s-v↦s' , (p-s'-v≡true , subst  cs  OutOfDomain cs (s' , v)) (sym (case-main-cond-lemma bs')) outd) ,
                         (q-s'≡true , case-out-of-range-inverse-lemma bs' outr)
  semantics-triple-case {S} {V} ((p , adaptive f)  bs) bs' =
     { s v ((s' , put-s-v≡just-s') , p-s-v≡true , out-s-v) 
         let snd-seq =
               fail-continuation ( S  (μ F)) ( V  (μ F)) (interp-CaseBranch (revcat bs' ((p , adaptive f)  bs))) []
                 (project-adaptive-branch bs bs' p f _ s' s v p-s-v≡true
                    (skip-branch ((p , adaptive f)  bs) bs' _ s' s v (toCompSeq put-s-v≡just-s') out-s-v))
         in  ((s' , fromCompSeq snd-seq) ,
              subst  ps  NormalCaseDomain ps (f s v , v))
                    (sym (case-branch-type-lemma (revcat bs' ((p , adaptive f)  bs))))
                    (inNormalCaseDomain (interp-CaseBranch (revcat bs' ((p , adaptive f)  bs))) [] snd-seq)) ,
              s'' put-f-s-v-v≡just-s'' 
                trans put-s-v≡just-s'
                      (cong just (CompSeq-deterministic snd-seq (toCompSeq put-f-s-v-v≡just-s'')))) })
      ∷ᴬ semantics-triple-case bs ((p , adaptive f)  bs')

completeness :
  {S V : U n} {R :  ( S  (μ F) ×  V  (μ F))} (b : BiGUL F S V) {R' :  ( S  (μ F) ×  S  (μ F) ×  V  (μ F))} 
  Sound R (Lens.put (interp b)) R'  Triple R b R'
completeness b {R'} sound =
  conseq  {sv} r  Product.map id (fromCompSeq  proj₁) (sound sv r))
         (semantics-triple b)
          { {s' , s , v} (put-s-v≡just-s' , R-s-v) 
              let (s'' , put-s-v↦s'' , R'-s''-s-v) = sound (s , v) R-s-v
              in  subst  x  R' (x , s , v))
                        (CompSeq-deterministic put-s-v↦s'' (toCompSeq put-s-v≡just-s'))
                        R'-s''-s-v })

interp-CaseBranch-revcat : {S V : U n} (bs bs' : List (CaseBranch F S V)) 
                           interp-CaseBranch (revcat bs' bs)  revcat (interp-CaseBranch bs') (interp-CaseBranch bs)
interp-CaseBranch-revcat bs []                       = refl
interp-CaseBranch-revcat bs ((p , normal b q)  bs') = interp-CaseBranch-revcat ((p , normal b q)  bs) bs'
interp-CaseBranch-revcat bs ((p , adaptive f)  bs') = interp-CaseBranch-revcat ((p , adaptive f)  bs) bs'

mutual

  semantics-tripleR :
    {S V : U n} (b : BiGUL F S V) 
    TripleR  { (s , v)  runPar (Lens.get (interp b) s)  just v })
            b
             s  Σ[ v   V  (μ F) ] runPar (Lens.get (interp b) s)  just v)
  semantics-tripleR fail = conseq  { (() , _) }) fail  { (_ , ()) })
  semantics-tripleR (skip f) = conseq (cong just  proj₁) skip  _  tt)
  semantics-tripleR replace = conseq (cong just  proj₁) replace  _  tt)
  semantics-tripleR (prod l r) =
   conseq  { ((leq , req) , _)  fromCompSeq {mx = Lens.get (interp (prod l r)) _}
                                       (toCompSeq leq >>= toCompSeq req >>= return refl) })
            (prod (semantics-tripleR l) (semantics-tripleR r))
             { ((v , w) , eq)  case toCompSeq {mx = Lens.get (interp (prod l r)) _} eq of λ {
                                    (l↦ >>= r↦ >>= return refl)  (v , fromCompSeq l↦) , (w , fromCompSeq r↦) } })
  semantics-tripleR (rearrS spat tpat expr c b) =
    conseq  { ((r , m , beq) , _)  fromCompSeq {mx = Lens.get (interp (rearrS spat tpat expr c b)) _}
                                        ((fromMatch spat _ m >>= return refl) >>= toCompSeq beq) })
           (rearrS  { (r , v)  runPar (Lens.get (interp b) (construct tpat (eval spat tpat expr r)))  just v })
                    r   λ v  runPar (Lens.get (interp b) (construct tpat (eval spat tpat expr r)))  just v)
                   (conseq  { {_ , v} (beq , r , _ , e) 
                                r , e , subst  t  runPar (Lens.get (interp b) t)  just v)
                                              (sym (fromEval spat tpat expr _ e)) beq })
                           (semantics-tripleR b)
                            { (r , (v , beq) , e) 
                                v , subst  t  runPar (Lens.get (interp b) t)  just v)
                                          (fromEval spat tpat expr _ e) beq })))
            { (v , eq) 
                case toCompSeq {mx = Lens.get (interp (rearrS spat tpat expr c b)) _} eq of λ {
                  ((deconstruct↦ >>= return refl) >>= b↦)  _ , (v , fromCompSeq b↦) , toMatch spat _ deconstruct↦ } })
  semantics-tripleR (rearrV vpat wpat expr c b) =
    conseq  { ((r , beq , m) , _) 
                fromCompSeq {mx = Lens.get (interp (rearrV vpat wpat expr c b)) _}
                  (toCompSeq beq >>=
                   Iso.to-from-inverse (rearrangement-iso vpat wpat expr c) (fromMatch vpat _ m >>= return refl)) })
           (rearrV  { (s , r)  runPar (Lens.get (interp b) s)  just (construct wpat (eval vpat wpat expr r)) })
                    s   λ r  runPar (Lens.get (interp b) s)  just (construct wpat (eval vpat wpat expr r))}
                   (conseq  { (beq , (r , beq')) 
                                r , beq' , subst  v  Eval vpat wpat expr (r , v))
                                                 (CompSeq-deterministic (toCompSeq {mx = Lens.get (interp b) _} beq')
                                                                        (toCompSeq beq))
                                                 (toEval vpat wpat expr) })
                           (semantics-tripleR b)
                            { (_ , beq)  _ , beq })))
            { {s} (v , eq) 
                case toCompSeq {mx = Lens.get (interp (rearrV vpat wpat expr c b)) _} eq of λ {
                  (b↦ >>= seq) 
                    case Iso.from-to-inverse (rearrangement-iso vpat wpat expr c) seq of λ {
                      (_ >>= return refl)  _ , fromCompSeq b↦ } } })
  semantics-tripleR (case bs) = conseq proj₁
                                       (case (semantics-tripleR-case bs []))
                                        { (_ , eq)  semantics-tripleR-CaseRange bs [] (toCompSeq eq) tt tt })

  semantics-tripleR-case :
    {S V : U n} (bs bs' : List (CaseBranch F S V)) 
    CaseBranchTripleR  { (s , v)  runPar (Lens.get (interp (case (revcat bs' bs))) s)  just v }) bs bs'
  semantics-tripleR-case [] bs' = []
  semantics-tripleR-case {S} {V} ((p , normal b q)  bs) bs' =
    _∷ᴺ_ {P = λ s   λ v  runPar (Lens.get (interp b) s)  just v ×
                            p s v  true × OutOfDomain (List.map proj₁ bs') (s , v) × q s  true × OutOfRangeB bs' s}
         (conseq  { (beq , _ , beq' , peq , outd , qeq , outr) 
                      Product.map fromCompSeq id (aux (toCompSeq beq) (toCompSeq beq') peq outd qeq outr) })
                 (semantics-tripleR b)
                 (Product.map id proj₁))
         (semantics-tripleR-case bs ((p , normal b q)  bs'))
    where
      aux : {s :  S  (μ F)} {v v' :  V  (μ F)} 
            Lens.get (interp b) s  v  Lens.get (interp b) s  v' 
            p s v'  true  OutOfDomain (List.map proj₁ bs') (s , v')  q s  true  OutOfRangeB bs' s 
            Lens.get (interp (case (revcat bs' ((p , normal b q)  bs)))) s  v ×
            p s v  true × OutOfDomain (List.map proj₁ bs') (s , v)
      aux c c' peq outd qeq outr
        with get-revcat _ _ ((p , normal (interp b) q)  interp-CaseBranch bs) (interp-CaseBranch bs')
               (check-diversion-success (interp-CaseBranch bs') _ _
                  (subst  ps  OutOfDomain ps _) (case-main-cond-lemma bs') outd) (case-out-of-range-lemma bs' outr))
      aux c c' peq outd qeq outr | cont
        rewrite CompSeq-deterministic c' c | interp-CaseBranch-revcat ((p , normal b q)  bs) bs' | qeq =
        cont (c >>= assert peq then return refl) , peq , outd
  semantics-tripleR-case ((p , adaptive f)  bs) bs' = •∷ᴬ semantics-tripleR-case bs ((p , adaptive f)  bs')
  
  semantics-tripleR-CaseRange :
    {S V : U n} (bs bs' : List (CaseBranch F S V)) 
    {s :  S  (μ F)} {v :  V  (μ F)} 
    Lens.get (interp (case bs)) s  v  OutOfDomain (List.map proj₁ bs') (s , v)  OutOfRangeB bs' s 
    CaseRange (semantics-tripleR-case bs bs') s
  semantics-tripleR-CaseRange [] bs' ()
  semantics-tripleR-CaseRange ((p , normal b q)  bs) bs' {s} c outd outr with q s | inspect q s
  semantics-tripleR-CaseRange ((p , normal b q)  bs) bs' (c >>= assert-not p-s-v≡false then return refl) outd outr
    | false | [ q-s≡false ] =
    inj₂ (semantics-tripleR-CaseRange bs ((p , normal b q)  bs') c (p-s-v≡false , outd) (q-s≡false , outr))
  semantics-tripleR-CaseRange ((p , normal b q)  bs) bs' (c >>= assert p-s-v≡true then return refl) outd outr
    | true  | [ q-s≡true  ] = inj₁ ((_ , fromCompSeq c , p-s-v≡true , outd , refl , outr) , refl , outr)
  semantics-tripleR-CaseRange ((p , adaptive f)  bs) bs' (c >>= assert-not p-s-v≡false then return refl) outd outr =
    semantics-tripleR-CaseRange bs ((p , adaptive f)  bs') c (p-s-v≡false , outd) outr

completenessR :
  {S V : U n} {P :  ( S  (μ F))} (b : BiGUL F S V) {R :  ( S  (μ F) ×  V  (μ F))} 
  SoundG P (Lens.get (interp b)) R  TripleR R b P
completenessR b {R} soundG =
  conseq  { {s , v} (get-s≡just-v , p) 
              let (v' , get-s↦v' , r) = soundG s p
              in subst  w  R (s , w)) (CompSeq-deterministic get-s↦v' (toCompSeq get-s≡just-v)) r })
         (semantics-tripleR b)
          {s} p  Product.map id (fromCompSeq  proj₁) (soundG s p))