module HoareLogic.Examples.Miscellaneous where

open import DynamicallyChecked.Utilities
open import DynamicallyChecked.Partiality
open import DynamicallyChecked.Universe
open import DynamicallyChecked.Rearrangement
open import DynamicallyChecked.BiGUL
open import HoareLogic.Semantics
open import HoareLogic.Triple
open import HoareLogic.Utilities

open import Function
open import Data.Empty
open import Data.Product as Product
open import Data.Sum as Sum
open import Data.Bool
open import Data.Maybe
open import Data.Nat as Nat
open import Data.Fin
open import Data.List
open import Relation.Nullary
open import Relation.Nullary.Decidable
open import Relation.Binary
open import Relation.Binary.PropositionalEquality


emb : {n : } {F : Functor n} {S V : U n} 
      ( S  (μ F)   V  (μ F))  ( S  (μ F)   V  (μ F)   S  (μ F)) 
      BiGUL F S V
emb {V = V} g p = case (((λ s v   U-dec V (g s) v ) ,
                           normal (skip g)
                                   _  true)) 
                        ((λ _ _  true) ,
                           adaptive p)  [])

emb-correctness : {n : } {F : Functor n} {S V : U n}
                  (g :  S  (μ F)   V  (μ F)) (p :  S  (μ F)   V  (μ F)   S  (μ F)) 
                  ({s :  S  (μ F)} {v :  V  (μ F)}  g (p s v)  v) 
                  Triple Π (emb {F = F} {S} {V} g p)
                          { (s' , s , v)  g s'  v × (g s  v  s'  s) × (g s  v  s'  p s v) })
emb-correctness g p PutGet =
  case
    (conseq
        { (_ , eqn , _)  trueToWitness eqn })
       skip
        { {.s , s , v} (refl , _ , eqn , _) 
              (trueToWitness eqn ,  _  refl) ,  g-s≢v  ⊥-elim (g-s≢v (trueToWitness eqn)))) ,
              (eqn , tt) , refl , tt }) ∷ᴺ
      { s v (_ , _ , feqn , _) 
            (tt , inj₁ (trueFromWitness PutGet)) ,
            λ { s' (g-s'≡v , PutGet⇒s'≡p-s-v , _) 
                  g-s'≡v ,  g-s≡v  ⊥-elim (falseToWitness feqn g-s≡v)) ,  _  PutGet⇒s'≡p-s-v PutGet) } }) ∷ᴬ [])
     _  inj₂ (inj₁ refl))

emb-correctness' : {n : } {F : Functor n} {S V : U n}
                   (g :  S  (μ F)   V  (μ F)) (p :  S  (μ F)   V  (μ F)   S  (μ F)) 
                   ({s :  S  (μ F)} {v :  V  (μ F)}  g (p s v)  v) 
                   ({s :  S  (μ F)}  p s (g s)  s) 
                   Triple Π (emb {F = F} {S} {V} g p)  { (s' , s , v)  s'  p s v })
emb-correctness' g p PutGet GetPut =
  case
    (conseq
        { (_ , eqn , _)  trueToWitness eqn })
       skip
        { {.s , s , v} (refl , _ , eqn , _) 
            trans (sym GetPut) (cong (p s) (trueToWitness eqn)) , (eqn , tt) , refl , tt }) ∷ᴺ
      { s v (_ , _ , neqn , _) 
          (tt , inj₁ (trueFromWitness PutGet)) ,
           s' eq  trans eq (trans (cong (p (p s v)) (sym PutGet)) GetPut)) }) ∷ᴬ [])
     _  inj₂ (inj₁ refl))

emb-range : {n : } {F : Functor n} {S V : U n}
            (g :  S  (μ F)   V  (μ F)) (p :  S  (μ F)   V  (μ F)   S  (μ F)) 
            TripleR Π (emb {F = F} {S} {V} g p) Π
emb-range g p =
  conseq
     _  tt)
    (case
       ((conseq
            { (eq , _)  tt , trueFromWitness eq , tt })
           skip
           id) ∷ᴺ •∷ᴬ []))
     _  inj₁ (tt , refl , tt))

eqCheck : {n : } {F : Functor n} {A : U n}  BiGUL F A (A  A)
eqCheck = rearrS var (prod var var) (refl , refl) (return refl) replace

eqCheck-correctness : {n : } {F : Functor n} {A : U n} 
                      Triple  { (_ , (v , w))  v  w })
                             (eqCheck {F = F} {A})
                              { (s' , _ , (v , w))  s'  v × s'  w })
eqCheck-correctness =
  conseq
     { {s , (v , w)} v≡w  s , refl , v≡w })
    (rearrS  { (_ , (v , w))  v  w })
             { (s' , _ , (v , w))  s'  v × s'  w })
      (conseq
          _  tt)
         replace
          { {._ , (s , t) , (v , w)} (refl , u , (u≡s , u≡t) , v≡w) 
              (v , u) , (refl , v≡w) , (refl , v≡w) , (u≡s , u≡t) })))
     { {s' , s , (v , w)} (((u , _) , (u≡v , u≡w) , s'≡u , _) , v≡w)  trans s'≡u u≡v , trans s'≡u u≡w })

eqCheck-range : {n : } {F : Functor n} {A : U n} 
                TripleR  { (_ , (v , w))  v  w }) (eqCheck {F = F} {A}) Π
eqCheck-range =
  conseq
     { {s , (v , w)} ((_ , _ , v≡w) , _)  v≡w })
    (rearrS  { (_ , (v , w))  v  w })  _  )
      (conseq
         { {(s , t) , (v , w)} (st≡vw , u , _ , u≡s , u≡t) 
             u , (u≡s , u≡t) , trans (sym (trans u≡s (cong proj₁ st≡vw))) (trans u≡t (cong proj₂ st≡vw)) })
        replace
         _  tt)))
     _  _ , tt , refl)

emptyF : Functor 0
emptyF ()

kℕ : U 0
kℕ = k  Nat._≟_

replace² : BiGUL emptyF (kℕ  kℕ) (kℕ  kℕ)
replace² = prod replace replace

replace²-correctness : Triple Π replace²  { ((s' , t') , _ , (v , w))  s'  v × t'  w })
replace²-correctness = conseq  _  tt , tt) (prod replace replace) proj₁

replace²-range : TripleR Π replace² Π
replace²-range = conseq  _  tt) (prod replace replace)  _  tt , tt)

replace²-equal-view-correctness : Triple (uncurry _≡_  proj₂) replace² (uncurry _≡_  proj₁  uncurry _≡_  Product.map id proj₂)
replace²-equal-view-correctness =
  conseq
     _  tt , tt)
    (prod replace replace)
     { {(x' , y') , (x , y) , (z , w)} ((x'≡z , y'≡w) , z≡w)  trans x'≡z (trans z≡w (sym y'≡w)) , cong₂ _,_ x'≡z y'≡w })

replace²-equal-view-range : TripleR (uncurry _≡_  proj₂) replace² (uncurry _≡_)
replace²-equal-view-range =
  conseq
     { {(s , t) , (v , w)} ((s≡v , t≡w) , s≡t)  trans (sym s≡v) (trans s≡t t≡w) })
    (prod replace replace)
     _  tt , tt)

keepHeight : BiGUL emptyF (kℕ  kℕ) kℕ
keepHeight = rearrV var (prod {H = one} var (k tt)) (refl , tt) (return refl) (prod replace (skip (const tt)))

keepHeight-correctness : Triple Π keepHeight  { ((w' , h') , (_ , h) , v)  w'  v × h'  h })
keepHeight-correctness =
  conseq
     _  _ , tt , refl)
    (rearrV Π  { ((w' , h') , (_ , h) , v)  w'  v × h'  h })
       (conseq
           _  tt , refl)
          (prod replace skip)
           { {(.v , .h) , (w , h) , (v , _)} ((refl , refl) , _)  _ , (refl , refl) , refl , refl })))
     { {(w' , .h) , (w , h) , v} ((._ , (eq , refl) , refl) , _)  eq , refl })

keepHeight-range : TripleR Π keepHeight Π
keepHeight-range =
  conseq
     _  tt)
    (rearrV Π
       (conseq
           _  _ , tt , refl , refl)
          (prod replace skip)
           { tt  tt , tt })))
    id

produceSquare : BiGUL emptyF (kℕ  kℕ) kℕ
produceSquare =
  case
    (((λ { (w , _) v   w Nat.≟ v  }) ,
      normal (skip proj₁)
             (const true)) 
     (((λ _ _  true) ,
      adaptive  { _ v  (v , v) })))  [])

produceSquare-correctness :
  Triple Π produceSquare  { ((w' , h') , (w , h) , v)  w'  v × (w  v  h'  h) × (w  v  w'  h') })
produceSquare-correctness =
  case
    ((conseq
         { {(w , h) , v} (_ , e , _)  trueToWitness e })
        skip
         { {.(w , h) , (w , h) , v} (refl , _ , e , _) 
             (trueToWitness e ,  _  refl) ,  w≢v  ⊥-elim (w≢v (trueToWitness e)))) , (e , tt) , refl , tt })) ∷ᴺ
      { (w , h) v (tt , _ , ne , _) 
          (tt , inj₁ (trueFromWitness refl)) ,
          λ { (w' , h') (w'≡v , v≡v→h'≡v , _) 
              w'≡v ,  w≡v  ⊥-elim (falseToWitness ne w≡v)) ,  _  trans w'≡v (sym (v≡v→h'≡v refl))) } }) ∷ᴬ [])
     _  inj₂ (inj₁ refl))

produceSquare-range : TripleR Π produceSquare Π
produceSquare-range =
  conseq
    proj₁
    (case
       ((conseq
            { {(w , h) , v} (w≡v , _)  tt , trueFromWitness w≡v , tt })
           skip
            _  tt)) ∷ᴺ •∷ᴬ []))
     _  inj₁ (tt , refl , tt))

resetHeight : BiGUL emptyF (kℕ  kℕ) kℕ
resetHeight =
  case
    (((λ { (w , _) v   w Nat.≟ v  }) ,
      normal (skip proj₁)
             (const true)) 
     (((λ _ _  true) ,
      adaptive  { _ v  (v , zero) })))  [])

resetHeight-correctness :
  Triple Π resetHeight  { ((w' , h') , (w , h) , v)  w'  v × (w  v  h'  h) × (w  v  h'  zero) })
resetHeight-correctness =
  case
    ((conseq
         { {(w , h) , v} (_ , e , _)  trueToWitness e })
        skip
         { {.(w , h) , (w , h) , v} (refl , _ , e , _) 
             (trueToWitness e ,  _  refl) ,  w≢v  ⊥-elim (w≢v (trueToWitness e)))) , (e , tt) , refl , tt })) ∷ᴺ
      { (w , h) v (tt , _ , ne , _) 
          (tt , inj₁ (trueFromWitness refl)) ,
          λ { (w' , h') (w'≡v , v≡v→h'≡0 , _) 
              w'≡v ,  w≡v  ⊥-elim (falseToWitness ne w≡v)) ,  _  v≡v→h'≡0 refl) } }) ∷ᴬ [])
     _  inj₂ (inj₁ refl))

resetHeight-range : TripleR Π resetHeight Π
resetHeight-range =
  conseq
    proj₁
    (case
       ((conseq
            { {(w , h) , v} (w≡v , _)  tt , trueFromWitness w≡v , tt })
           skip
            _  tt)) ∷ᴺ •∷ᴬ []))
     _  inj₁ (tt , refl , tt))

alwaysResetHeight : BiGUL emptyF (kℕ  kℕ) kℕ
alwaysResetHeight = rearrV var (prod var (k {G = kℕ} zero)) (refl , tt) (return refl) replace²

alwaysResetHeight-correctness : Triple Π alwaysResetHeight  { ((w' , h') , _ , v)  w'  v × h'  zero })
alwaysResetHeight-correctness =
  conseq
     _  _ , tt , refl)
    (rearrV Π  { ((w' , h') , _ , v)  w'  v × h'  zero })
       (conseq
           _  tt)
          replace²-correctness
           { {(.u , .v) , (w , h) , (u , v)} ((refl , refl) , _ , _ , _ , 0≡v) 
                 _ , (refl , sym 0≡v) , (refl , 0≡v) })))
     { ((_ , p , refl) , _)  p })

alwaysResetHeight-range : TripleR Π alwaysResetHeight ((_≡ zero)  proj₂)
alwaysResetHeight-range =
  conseq
     _  tt)
    (rearrV Π
       (conseq {Q = (_≡ zero)  proj₂}
           { ((_ , v≡0) , _)  _ , tt , refl , sym v≡0 })
          (prod
             replace
             (conseq
                 { (s≡v , s≡0)  trans (sym s≡v) s≡0 })
                replace
                 _  tt)))
          (_,_ tt)))
    id

just-lemma : {A : Set} {x y : A}  (Maybe A  just x)  just y  x  y
just-lemma refl = refl

module _ (Rational : Set) (_≟_ : Decidable (_≡_ {A = Rational}))
         (zero : Rational)
         (mul : Rational  Rational  Rational)
         (div : Rational  (d : Rational)  Maybe Rational)
         (DivNonZero : (x y : Rational)  y  zero  Σ[ z  Rational ] div x y  just z)
         (DivMul : (x y z : Rational)  div x y  just z  mul y z  x)
         (CrossEq : (a b c d : Rational)  mul d a  mul c b  b  zero  d  zero  div a b  div c d) where

  kQ : U 0
  kQ = k Rational _≟_
  
  fromJustOrZero : Maybe Rational  Rational
  fromJustOrZero (just x) = x
  fromJustOrZero nothing  = zero

  keepRatio : BiGUL emptyF (kQ  kQ) kQ
  keepRatio =
    case
      (((λ { (w , _) _   w  zero  }) ,
        adaptive λ { (_ , h) v  (v , h) }) 
       ((λ { (w , _) v   w  v  }) ,
        normal (skip proj₁)  _  true)) 
       ((λ _ _  true) ,
        adaptive  { (w , h) v  (v , fromJustOrZero (div (mul h v) w)) }))  [])

  keepRatio-correctness : Triple  { (_ , v)  v  zero }) keepRatio
                                  { ((w' , h') , (w , h) , v) 
                                        w'  v × (w  zero  h'  h) × (w  zero  div h' w'  div h w) })
  keepRatio-correctness =
    case
      ((λ { (w , h) v (v≢zero , w==zero , _) 
            (v≢zero , falseFromWitness v≢zero , inj₁ (trueFromWitness refl)) ,
             { (w' , h') (w'≡v , v≡zero→h'≡h , v≢zero→h'/w'≡h/v) 
                 w'≡v ,
                  w≡zero 
                    let (x , h'/w'≡just-x) = DivNonZero h' w' (subst (_≢ zero) (sym w'≡v) v≢zero)
                        (y , h/v≡just-y)   = DivNonZero h v v≢zero
                    in  trans (sym (DivMul h' w' x h'/w'≡just-x))
                              (trans (trans (subst  a  mul w' x  mul a x) w'≡v refl)
                                            (cong (mul v) (just-lemma (trans (sym h'/w'≡just-x)
                                                                             (trans (v≢zero→h'/w'≡h/v v≢zero) h/v≡just-y)))))
                                     (DivMul h v y h/v≡just-y))) ,
                  w≢zero  ⊥-elim (w≢zero (trueToWitness w==zero))) }) }) ∷ᴬ
       conseq
          { {(w , h) , v} (v≢zero , w==v , w/=zero , _)  trueToWitness w==v })
         skip
          { {.(w , h) , (w , h) , v} (refl , v≢zero , w==v , w/=zero , _) 
              (trueToWitness w==v ,  _  refl) ,  _  refl)) , (w==v , w/=zero , tt) , refl , tt }) ∷ᴺ
        { (w , h) v (v≢zero , _ , w/=v , w/=zero , _) 
            (v≢zero , falseFromWitness v≢zero , inj₁ (trueFromWitness refl)) ,
             { (w' , h')  aux w' h' w h v (falseToWitness w/=zero) v≢zero }) }) ∷ᴬ [])
       _  inj₂ (inj₂ (inj₁ refl)))
    where
      aux : (w' h' w h v : Rational)  w  zero  v  zero 
            w'  v × (v  zero  h'  fromJustOrZero (div (mul h v) w)) ×
                     (v  zero  div h' w'  div (fromJustOrZero (div (mul h v) w)) v) 
            w'  v × (w  zero  h'  h) × (w  zero  div h' w'  div h w)
      aux w' h' w h v w≢zero v≢zero (w'≡v , v≡zero→ , v≢zero→) with DivNonZero (mul h v) w w≢zero
      aux w' h' w h v w≢zero v≢zero (w'≡v , v≡zero→ , v≢zero→) | x , eq rewrite eq =
        w'≡v ,  w≡zero  ⊥-elim (w≢zero w≡zero)) ,
         _  trans (v≢zero→ v≢zero) (CrossEq x v h w (DivMul (mul h v) w x eq) v≢zero w≢zero ))

  keepRatio-range : TripleR  { (_ , v)  v  zero }) keepRatio  { (w' , _)  w'  zero })
  keepRatio-range =
    conseq
      proj₁
      (case (•∷ᴬ (conseq
                     { {(w , h) , v} (w≡v , w≢zero) 
                         subst (_≢ zero) w≡v w≢zero , trueFromWitness w≡v , falseFromWitness w≢zero , tt })
                    skip
                     _  tt)) ∷ᴺ •∷ᴬ []))
      (inj₁  (_, (refl , tt)))