{-# OPTIONS --rewriting #-}

module Examples.Decalf.Basic where

open import Algebra.Cost

costMonoid = ℕ-CostMonoid
open CostMonoid costMonoid using ()

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


double : cmp $ Π nat λ _  F nat
double zero    = ret zero
double (suc n) =
  step (F nat) 1 $
  bind (F nat) (double n) λ n' 
  ret (suc (suc n'))

double/bound : cmp $ Π nat λ _  F nat
double/bound n = step (F nat) n (ret (2 * n))

double/has-cost : (n : val nat)  double n  double/bound n
double/has-cost zero    = refl
double/has-cost (suc n) =
  let open ≡-Reasoning in
  begin
    (step (F nat) 1 $
    bind (F nat) (double n) λ n' 
    ret (suc (suc n')))
  ≡⟨
    Eq.cong
      (step (F nat) 1)
      (begin
        (bind (F nat) (double n) λ n' 
        ret (suc (suc n')))
      ≡⟨ Eq.cong  e  bind (F nat) e λ n'  ret (suc (suc n'))) (double/has-cost n) 
        (bind (F nat) (step (F nat) n (ret (2 * n))) λ n' 
        ret (suc (suc n')))
      ≡⟨⟩
        step (F nat) n (ret (suc (suc (2 * n))))
      ≡˘⟨ Eq.cong (step (F nat) n  ret  suc) (Nat.+-suc n (n + 0)) 
        step (F nat) n (ret (2 * suc n))
      )
  
    step (F nat) 1 (step (F nat) n (ret (2 * suc n)))
  ≡⟨⟩
    step (F nat) (suc n) (ret (2 * suc n))
  

double/correct :  ((n : val nat)  double n  ret (2 * n))
double/correct u n = Eq.trans (double/has-cost n) (step/ext (F nat) (ret (2 * n)) n u)