{-# OPTIONS --rewriting #-}

module Examples.Decalf.GlobalState where

open import Algebra.Cost

costMonoid = ℕ-CostMonoid
open CostMonoid costMonoid using ()

open import Calf costMonoid
open import Calf.Data.Nat as Nat using (nat; _*_)
open import Calf.Data.Equality as Eq using (_≡_; module ≡-Reasoning)
open import Function


S : tp⁺
S = nat

variable
  s s₁ s₂ : val S

postulate
  get : (X : tp⁻)  (val S  cmp X)  cmp X
  set : (X : tp⁻)  val S  cmp X  cmp X

  get/get : {e : val S  val S  cmp X} 
    (get X λ s₁  get X λ s₂  e s₁ s₂)  (get X λ s  e s s)
  get/set : {e : cmp X} 
    (get X λ s  set X s e)  e
  set/get : {e : val S  cmp X} 
    set X s (get X e)  set X s (e s)
  set/set : {e : cmp X} 
    set X s₁ (set X s₂ e)  set X s₂ e

  get/step : (c : ) {e : val S  cmp X} 
    step X c (get X e)  get X λ s  step X c (e s)
  set/step : (c : ) {e : cmp X} 
    step X c (set X s e)  set X s (step X c e)


module StateDependentCost where
  open import Examples.Decalf.Basic

  e : cmp (F nat)
  e =
    get (F nat) λ n 
    bind (F nat) (double n) λ n' 
    set (F nat) n' $
    ret n

  e/bound : cmp (F nat)
  e/bound =
    get (F nat) λ n 
    set (F nat) (2 * n) $
    step (F nat) n $
    ret n

  e/has-cost : e  e/bound
  e/has-cost =
    Eq.cong (get (F nat)) $ funext λ n 
    let open ≡-Reasoning in
    begin
      ( bind (F nat) (double n) λ n' 
        set (F nat) n' $
        ret n
      )
    ≡⟨ Eq.cong  e₁  bind (F nat) e₁ λ n'  set (F nat) n' (ret n)) (double/has-cost n) 
      ( bind (F nat) (step (F nat) n (ret (2 * n))) λ n' 
        set (F nat) n' $
        ret n
      )
    ≡⟨⟩
      ( step (F nat) n $
        set (F nat) (2 * n) $
        ret n
      )
    ≡⟨ set/step n 
      ( set (F nat) (2 * n) $
        step (F nat) n $
        ret n
      )