module HoareLogic.Triple where

open import DynamicallyChecked.Utilities
open import DynamicallyChecked.Lens
open import DynamicallyChecked.Universe
open import DynamicallyChecked.Case
open import DynamicallyChecked.BiGUL
open import DynamicallyChecked.Rearrangement
open import HoareLogic.Semantics
open import HoareLogic.Rearrangement
open import HoareLogic.Case

open import Level using (lift)
open import Function
open import Data.Empty
open import Data.Product as Product
open import Data.Sum
open import Data.Bool
open import Data.Nat as Nat
open import Data.Nat.Properties
open import Data.List as List
open import Relation.Nullary
open import Relation.Binary
open import Relation.Binary.PropositionalEquality


OutOfRangeB : {n : } {F : Functor n} {S V : U n}  List (CaseBranch F S V)   ( S  (μ F))
OutOfRangeB []                      = Π
OutOfRangeB ((_ , normal _ q)  bs) = (False  q)  OutOfRangeB bs
OutOfRangeB ((_ , adaptive _)  bs) = OutOfRangeB bs

mutual

  data Triple {n : } {F : Functor n} : {S V : U n} 
               ( S  (μ F) ×  V  (μ F))  BiGUL F S V   ( S  (μ F) ×  S  (μ F) ×  V  (μ F))  Set₁ where
    fail    : {S V : U n}  Triple  (fail {S = S} {V}) 
    skip    : {S V : U n} {f :  S  (μ F)   V  (μ F)} 
              Triple (uncurry _≡_  Product.map f id) (skip {S = S} {V} f) (uncurry _≡_  Product.map id proj₁)
    replace : {S : U n}  Triple Π (replace {S = S}) (uncurry _≡_  Product.map id proj₂)
    prod    : {Sl Vl Sr Vr : U n}
              {Rl :  ( Sl  (μ F) ×  Vl  (μ F))} {Rl' :  ( Sl  (μ F) ×  Sl  (μ F) ×  Vl  (μ F))}
              {l : BiGUL F Sl Vl}  Triple Rl l Rl' 
              {Rr :  ( Sr  (μ F) ×  Vr  (μ F))} {Rr' :  ( Sr  (μ F) ×  Sr  (μ F) ×  Vr  (μ F))}
              {r : BiGUL F Sr Vr}  Triple Rr r Rr' 
              Triple  { ((sl , sr) , (vl , vr))  Rl (sl , vl) × Rr (sr , vr) }) (prod l r)
                      { ((sl' , sr') , (sl , sr) , (vl , vr))  Rl' (sl' , sl , vl) × Rr' (sr' , sr , vr) })
    rearrS  : {S T V : U n}
              {spat : Pattern F S} {tpat : Pattern F T} {expr : Expr spat tpat} {c : CompleteExpr spat tpat expr}
              {b : BiGUL F T V} (R :  (PatResult spat ×  V  (μ F)))
                                (R' :  (PatResult spat × PatResult spat ×  V  (μ F))) 
              Triple ((Eval spat tpat expr  swap)  R) b
                      { (t' , t , v)   λ { (r' , r)  R' (r' , r , v) × Eval spat tpat expr (r' , t') ×
                                                                            Eval spat tpat expr (r , t) } }) 
              Triple (Match spat  R) (rearrS spat tpat expr c b)
                      { (s' , s , v)   λ { (r' , r)  R' (r' , r , v) × Match spat (s' , r') × Match spat (s , r) } })
    rearrV  : {S V W : U n}
              {vpat : Pattern F V} {wpat : Pattern F W} {expr : Expr vpat wpat} {c : CompleteExpr vpat wpat expr}
              {b : BiGUL F S W} (R :  ( S  (μ F) × PatResult vpat)) (R' :  ( S  (μ F) ×  S  (μ F) × PatResult vpat)) 
              Triple (R  Eval vpat wpat expr) b
                      { (s' , s , v)   λ r  R' (s' , s , r) × Eval vpat wpat expr (r , v) }) 
              Triple (R  (Match vpat  swap)) (rearrV vpat wpat expr c b)
                      { (s' , s , v)   λ r  R' (s' , s , r) × Match vpat (v , r) })
    case    : {S V : U n} {bs : List (CaseBranch F S V)}
              {R :  ( S  (μ F) ×  V  (μ F))} {R' :  ( S  (μ F) ×  S  (μ F) ×  V  (μ F))} 
              CaseBranchTriple R R'
                (NormalCaseDomain (List.map (Product.map id (elimCaseBranchType  _ _  true)  _  false))) bs)) bs [] 
              R  CaseDomain (List.map proj₁ bs) 
              Triple R (case bs) R'
    conseq  : {S V : U n} {b : BiGUL F S V}
              {R Q :  ( S  (μ F) ×  V  (μ F))} {R' Q' :  ( S  (μ F) ×  S  (μ F) ×  V  (μ F))} 
              Q  R  Triple R b R'  R'  (Q  proj₂)  Q'  Triple Q b Q'

  data CaseBranchTriple {n : } {F : Functor n} {S V : U n}
                        (R :  ( S  (μ F) ×  V  (μ F))) (R' :  ( S  (μ F) ×  S  (μ F) ×  V  (μ F)))
                        (ReentryCond :  ( S  (μ F) ×  V  (μ F))) :
                        List (CaseBranch F S V)  List (CaseBranch F S V)  Set₁ where
    []    : {bs' : List (CaseBranch F S V)}  CaseBranchTriple R R' ReentryCond [] bs'
    _∷ᴺ_  : {p :  S  (μ F)   V  (μ F)  Bool} {b : BiGUL F S V} {q :  S  (μ F)  Bool}
            {bs bs' : List (CaseBranch F S V)} 
            Triple (R  (True  uncurry p)  OutOfDomain (List.map proj₁ bs')) b
                   (R'  (((True  uncurry p)  OutOfDomain (List.map proj₁ bs'))  Product.map id proj₂) 
                         (((True  q)  OutOfRangeB bs')  proj₁)) 
            CaseBranchTriple R R' ReentryCond bs ((p , normal b q)  bs') 
            CaseBranchTriple R R' ReentryCond ((p , normal b q)  bs) bs'
    _∷ᴬ_ : {p :  S  (μ F)   V  (μ F)  Bool} {f :  S  (μ F)   V  (μ F)   S  (μ F)}
           {bs bs' : List (CaseBranch F S V)} 
           ((s :  S  (μ F)) (v :  V  (μ F))  (R  (True  uncurry p)  OutOfDomain (List.map proj₁ bs')) (s , v) 
              (R  ReentryCond) (f s v , v) × ((s' :  S  (μ F))  R' (s' , f s v , v)  R' (s' , s , v))) 
           CaseBranchTriple R R' ReentryCond bs ((p , adaptive f)  bs') 
           CaseBranchTriple R R' ReentryCond ((p , adaptive f)  bs) bs'

infixr 5 _∷ᴺ_ _∷ᴬ_

case-main-cond-lemma : {n : } {F : Functor n} {S V : U n}
                       (bs : List (CaseBranch F S V))  List.map proj₁ bs  List.map proj₁ (interp-CaseBranch bs)
case-main-cond-lemma [] = refl
case-main-cond-lemma ((p , normal _ _)  bs) = cong (p ∷_) (case-main-cond-lemma bs)
case-main-cond-lemma ((p , adaptive _)  bs) = cong (p ∷_) (case-main-cond-lemma bs)

case-branch-type-lemma : {n : } {F : Functor n} {S V : U n} (bs : List (CaseBranch F S V)) 
                         List.map (Product.map id (elimCaseBranchType  _ _  true)  _  false))) bs 
                         List.map (Product.map id (isNormal _ _)) (interp-CaseBranch bs)
case-branch-type-lemma []                      = refl
case-branch-type-lemma ((p , normal _ _)  bs) = cong ((p , true ) ∷_) (case-branch-type-lemma bs)
case-branch-type-lemma ((p , adaptive _)  bs) = cong ((p , false) ∷_) (case-branch-type-lemma bs)

case-out-of-range-lemma : {n : } {F : Functor n} {S V : U n} (bs : List (CaseBranch F S V)) 
                          OutOfRangeB bs  OutOfRange (interp-CaseBranch bs)
case-out-of-range-lemma []                      = id
case-out-of-range-lemma ((p , normal _ _)  bs) = Product.map id (case-out-of-range-lemma bs)
case-out-of-range-lemma ((p , adaptive _)  bs) = case-out-of-range-lemma bs

case-out-of-range-inverse-lemma :
  {n : } {F : Functor n} {S V : U n} (bs : List (CaseBranch F S V)) 
  OutOfRange (interp-CaseBranch bs)  OutOfRangeB bs
case-out-of-range-inverse-lemma []                      = id
case-out-of-range-inverse-lemma ((p , normal _ _)  bs) = Product.map id (case-out-of-range-inverse-lemma bs)
case-out-of-range-inverse-lemma ((p , adaptive _)  bs) = case-out-of-range-inverse-lemma bs

soundness : {n : } {F : Functor n} {S V : U n}
            {R :  ( S  (μ F) ×  V  (μ F))} {b : BiGUL F S V} {R' :  ( S  (μ F) ×  S  (μ F) ×  V  (μ F))} 
            Triple R b R'  Sound R (Lens.put (interp b)) R'
soundness fail = fail-soundness
soundness (skip {V = V} {f}) = skip-soundness (U-dec V) f
soundness replace = replace-soundness
soundness (prod {l = l} tl {r = r} tr) = prod-soundness _ _ (interp l) (soundness tl) _ _ (interp r) (soundness tr)
soundness (rearrS {tpat = tpat} {c = c} {b} R R' t) = rearrS-soundness _ tpat _ c (interp b) R R' (soundness t)
soundness (rearrV {wpat = wpat} {c = c} {b} R R' t) = rearrV-soundness _ wpat _ c (interp b) R R' (soundness t)
soundness {n} {F} {S} {V} (case {bs = bs} {R} {R'} ts dom) =
  case-soundness (interp-CaseBranch bs) R R'
    (subst (BranchSound R R' (interp-CaseBranch bs) []  NormalCaseDomain)
           (case-branch-type-lemma bs) (case-soundness-lemma ts))
    (subst  ps  R  CaseDomain ps) (case-main-cond-lemma bs) dom)
  where
    case-soundness-lemma : {bs bs' : List (CaseBranch F S V)} {ReentryCond :  ( S  (μ F) ×  V  (μ F))} 
                           CaseBranchTriple R R' ReentryCond bs bs' 
                           BranchSound R R' (interp-CaseBranch bs) (interp-CaseBranch bs') ReentryCond
    case-soundness-lemma [] = tt
    case-soundness-lemma (t ∷ᴺ ts) with soundness t
    case-soundness-lemma {bs' = bs'} (t ∷ᴺ ts) | sound rewrite case-main-cond-lemma bs' =
       sv pre  let (s' , put↦ , r' , (peq , outd) , qeq , outr) = sound sv pre
                  in   s' , put↦ , r' , (peq , outd) , qeq , case-out-of-range-lemma bs' outr) , case-soundness-lemma ts
    case-soundness-lemma {bs' = bs'} (p ∷ᴬ ts) rewrite case-main-cond-lemma bs' = lift p , case-soundness-lemma ts
soundness (conseq q t q') = consequence _ _ _ (soundness t) _ q _ q'

expand :   {n : } {F : Functor n} {S V : U n}  (BiGUL F S V  BiGUL F S V)  BiGUL F S V
expand zero    f = fail
expand (suc n) f = f (expand n f)

expandTriple :
  {n : } {F : Functor n} {S V : U n} (f : BiGUL F S V  BiGUL F S V)
  (measure :  S  (μ F) ×  V  (μ F)  ) 
  (R :  ( S  (μ F) ×  V  (μ F))) (R' :  ( S  (μ F) ×  S  (μ F) ×  V  (μ F))) 
  ((n : ) (rec : BiGUL F S V)  ({m : }  Triple (R  ((_≡ m)  measure)   _  m < n)) rec R') 
                                 Triple (R  ((_≡ n)  measure)) (f rec) R') 
  (l n : )  n  l  Triple (R  ((_≡ n)  measure)) (expand (suc l) f) R'
expandTriple f measure R R' g zero   .zero z≤n     = g zero fail (conseq  { (_ , _ , ()) }) fail  { (() , _) }))
expandTriple f measure R R' g (suc l) n    n≤suc-l = g n (expand (suc l) f) aux
  where
    aux : {m : }  Triple (R  ((_≡ m)  measure)   _  m < n)) (expand (suc l) f) R'
    aux {m} with suc m ≤? n
    aux {m} | yes m<n = conseq (Product.map id proj₁)
                               (expandTriple f measure R R' g l m
                                  (≤-pred (DecTotalOrder.trans Nat.decTotalOrder m<n n≤suc-l)))
                               proj₁
    aux {m} | no ¬m<n = conseq  { (_ , _ , m<n)  ⊥-elim (¬m<n m<n) })
                               (expandTriple f measure R R' g l l (DecTotalOrder.refl Nat.decTotalOrder))
                               proj₁

expand-sound :
  {n : } {F : Functor n} {S V : U n} (f : BiGUL F S V  BiGUL F S V)
  (measure :  S  (μ F) ×  V  (μ F)  ) 
  (R :  ( S  (μ F) ×  V  (μ F))) (R' :  ( S  (μ F) ×  S  (μ F) ×  V  (μ F))) 
  (l : ) →((n : )  n  l  Triple (R  ((_≡ n)  measure)) (expand (suc l) f) R') 
  Sound (R  ((_≤ l)  measure)) (Lens.put (interp (expand (suc l) f))) R'
expand-sound f measure R R' l ts (s , v) (Rsv , m≤l) = soundness (ts (measure (s , v)) m≤l) (s , v) (Rsv , refl)

mutual

  data TripleR {n : } {F : Functor n} : {S V : U n} 
                ( S  (μ F) ×  V  (μ F))  BiGUL F S V   ( S  (μ F))  Set₁ where
    fail    : {S V : U n}  TripleR  (fail {S = S} {V}) 
    skip    : {S V : U n} {f :  S  (μ F)   V  (μ F)} 
              TripleR (uncurry _≡_  Product.map f id) (skip {S = S} {V} f) Π
    replace : {S : U n}  TripleR (uncurry _≡_) (replace {S = S}) Π
    prod    : {Sl Vl Sr Vr : U n}
              {Rl :  ( Sl  (μ F) ×  Vl  (μ F))} {Pl :  ( Sl  (μ F))}
              {l : BiGUL F Sl Vl}  TripleR Rl l Pl 
              {Rr :  ( Sr  (μ F) ×  Vr  (μ F))} {Pr :  ( Sr  (μ F))}
              {r : BiGUL F Sr Vr}  TripleR Rr r Pr 
              TripleR  { ((sl , sr) , (vl , vr))  Rl (sl , vl) × Rr (sr , vr) })
                      (prod l r)
                       { (sl , sr)  Pl sl × Pr sr })
    rearrS  : {S T V : U n}
              {spat : Pattern F S} {tpat : Pattern F T} {expr : Expr spat tpat} {c : CompleteExpr spat tpat expr}
              {b : BiGUL F T V} (R :  (PatResult spat ×  V  (μ F))) (P :  (PatResult spat)) 
              TripleR ((Eval spat tpat expr  swap)  R) b  t   λ r  P r × Eval spat tpat expr (r , t)) 
              TripleR (Match spat  R) (rearrS spat tpat expr c b)  s   λ r  P r × Match spat (s , r))
    rearrV  : {S V W : U n}
              {vpat : Pattern F V} {wpat : Pattern F W} {expr : Expr vpat wpat} {c : CompleteExpr vpat wpat expr}
              {b : BiGUL F S W} (R :  ( S  (μ F) × PatResult vpat)) {P :  ( S  (μ F))} 
              TripleR (R  Eval vpat wpat expr) b P 
              TripleR (R  (Match vpat  swap)) (rearrV vpat wpat expr c b) P
    case    : {S V : U n} {bs : List (CaseBranch F S V)} {R :  ( S  (μ F) ×  V  (μ F))} 
              (ts : CaseBranchTripleR R bs [])  TripleR R (case bs) (CaseRange ts)
    conseq  : {S V : U n} {b : BiGUL F S V} {R T :  ( S  (μ F) ×  V  (μ F))} {P Q :  ( S  (μ F))} 
              R  (Q  proj₁)  T  TripleR R b P  Q  P  TripleR T b Q

  data CaseBranchTripleR {n : } {F : Functor n} {S V : U n} (R :  ( S  (μ F) ×  V  (μ F))) :
                         List (CaseBranch F S V)  List (CaseBranch F S V)  Set₁ where
    []   : {bs' : List (CaseBranch F S V)}  CaseBranchTripleR R [] bs'
    _∷ᴺ_ : {p :  S  (μ F)   V  (μ F)  Bool} {b : BiGUL F S V} {q :  S  (μ F)  Bool}
           {bs bs' : List (CaseBranch F S V)} {P :  ( S  (μ F))} 
           TripleR (R  (True  uncurry p)  OutOfDomain (List.map proj₁ bs')) b P 
           CaseBranchTripleR R bs ((p , normal b q)  bs')  CaseBranchTripleR R ((p , normal b q)  bs) bs'
    •∷ᴬ_ : {p :  S  (μ F)   V  (μ F)  Bool} {f :  S  (μ F)   V  (μ F)   S  (μ F)}
           {bs bs' : List (CaseBranch F S V)} 
           CaseBranchTripleR R bs ((p , adaptive f)  bs')  CaseBranchTripleR R ((p , adaptive f)  bs) bs'

  CaseRange : {n : } {F : Functor n} {S V : U n} {R :  ( S  (μ F) ×  V  (μ F))}
              {bs bs' : List (CaseBranch F S V)}  CaseBranchTripleR R bs bs'   ( S  (μ F))
  CaseRange             []                          = 
  CaseRange {bs' = bs'} (_∷ᴺ_ {q = q} {P = P} _ ts) = (P  (True  q)  OutOfRangeB bs')  CaseRange ts
  CaseRange             (•∷ᴬ                    ts) = CaseRange ts

infixr 5 •∷ᴬ_

soundnessR : {n : } {F : Functor n} {S V : U n}
             {R :  ( S  (μ F) ×  V  (μ F))} {b : BiGUL F S V} {P :  ( S  (μ F))} 
             TripleR R b P  SoundG P (Lens.get (interp b)) R
soundnessR fail = fail-soundnessG
soundnessR (skip {V = V} {f}) = skip-soundnessG (U-dec V) f
soundnessR replace = replace-soundnessG
soundnessR (prod {l = l} tl {r = r} tr) = prod-soundnessG _ _ (interp l) (soundnessR tl) _ _ (interp r) (soundnessR tr)
soundnessR (rearrS {tpat = tpat} {c = c} {b} R P t) = rearrS-soundnessG _ tpat _ c (interp b) P R (soundnessR t) 
soundnessR (rearrV {wpat = wpat} {c = c} {b} R   t) = rearrV-soundnessG _ wpat _ c (interp b) _ R (soundnessR t)
soundnessR {n} {F} {S} {V} {R} (case {bs = bs} ts) =
  case-soundnessG (interp-CaseBranch bs) R (case-soundnessR-lemma ts) (CaseRange ts) (case-range-lemma ts)
  where
    case-soundnessR-lemma :
      {bs bs' : List (CaseBranch F S V)}  CaseBranchTripleR R bs bs' 
      BranchSoundG R (interp-CaseBranch bs) (interp-CaseBranch bs')
    case-soundnessR-lemma [] = tt
    case-soundnessR-lemma {bs' = bs'} (_∷ᴺ_ {P = P} t ts)
      rewrite case-main-cond-lemma bs' = (P , soundnessR t) , case-soundnessR-lemma ts
    case-soundnessR-lemma (•∷ᴬ ts) = case-soundnessR-lemma ts

    case-range-lemma :
      {bs bs' : List (CaseBranch F S V)} (ts : CaseBranchTripleR R bs bs') 
      CaseRange ts  Range R (interp-CaseBranch bs) (interp-CaseBranch bs') (case-soundnessR-lemma ts)
    case-range-lemma [] ()
    case-range-lemma {bs' = bs'} (_ ∷ᴺ ts) (inj₁ (p , q , out))
      rewrite case-main-cond-lemma bs' = inj₁ (p , q , case-out-of-range-lemma bs' out)
    case-range-lemma {bs' = bs'} (_ ∷ᴺ ts) (inj₂ r) rewrite case-main-cond-lemma bs' = inj₂ (case-range-lemma ts r)
    case-range-lemma (•∷ᴬ ts) r = case-range-lemma ts r
soundnessR (conseq R∩Q⊆T t Q⊆P) = consequenceG _ _ _ (soundnessR t) _ Q⊆P _ R∩Q⊆T

expandTripleR :
  {n : } {F : Functor n} {S V : U n} (f : BiGUL F S V  BiGUL F S V)
  (measure :  S  (μ F) ×  V  (μ F)  ) 
  (R :  ( S  (μ F) ×  V  (μ F))) (P :    ( S  (μ F))) 
  ((n : ) (rec : BiGUL F S V)  ({m : }  TripleR (R  ((_≡ m)  measure)) rec (P m   _  m < n))) 
                                 TripleR (R  ((_≡ n)  measure)) (f rec) (P n)) 
  (l n : )  n  l  TripleR (R  ((_≡ n)  measure)) (expand (suc l) f) (P n) 
expandTripleR f measure R P g zero   .zero z≤n     = g zero fail (conseq  { (() , _) }) fail  { (_ , ()) }))
expandTripleR f measure R P g (suc l) n    n≤suc-l = g n (expand (suc l) f) aux
  where
    aux : {m : }  TripleR (R  ((_≡ m)  measure)) (expand (suc l) f) (P m   _  m < n))
    aux {m} with suc m ≤? n
    aux {m} | yes m<n = conseq proj₁
                               (expandTripleR f measure R P g l m
                                  (≤-pred (DecTotalOrder.trans Nat.decTotalOrder m<n n≤suc-l)))
                               proj₁
    aux {m} | no ¬m<n = conseq  { (_ , _ , m<n)  ⊥-elim (¬m<n m<n) })
                               (expandTripleR f measure R P g l l (DecTotalOrder.refl Nat.decTotalOrder))
                                { (_ , m<n)  ⊥-elim (¬m<n m<n) })

expand-soundG :
  {n : } {F : Functor n} {S V : U n} (f : BiGUL F S V  BiGUL F S V)
  (measure :  S  (μ F) ×  V  (μ F)  )
  (R :  ( S  (μ F) ×  V  (μ F))) (P' :    ( S  (μ F)))
  (l : )  ((n : )  n  l  TripleR (R  ((_≡ n)  measure)) (expand (suc l) f) (P' n)) 
  SoundG  s  Σ[ n   ] n  l × P' n s) (Lens.get (interp (expand (suc l) f))) (R  ((_≤ l)  measure))
expand-soundG f measure R P' l ts s (n , n≤l , P'-n-s) =
  let (v , get↦ , Rsv , meq) = soundnessR (ts n n≤l) s P'-n-s
  in  v , get↦ , Rsv , subst (_≤ l) (sym meq) n≤l