{-# OPTIONS --rewriting #-}

module Examples.Amortized.Simple where

open import Algebra.Cost

costMonoid = ℕ-CostMonoid
open CostMonoid costMonoid using ()

open import Level using (0ℓ)

open import Calf costMonoid
open import Calf.Data.Product
open import Calf.Data.Bool
open import Calf.Data.Nat as Nat using (; zero; suc; nat; _+_; _∸_; pred; _*_; _^_; _>_)
open import Calf.Data.Equality as Eq using (_≡_; refl; _≡⁺_; ≡⁺-syntax; _≡⁻_; ≡⁻-syntax; module ≡-Reasoning)

open import Examples.Amortized.Core


postulate
  simple : tp⁻
record Simple : Set where
  coinductive
  field
    quit : cmp (F unit)
    next : cmp simple
postulate
  simple/decode : val (U simple)  Simple
  {-# REWRITE simple/decode #-}

  quit/step :  {c e}  Simple.quit (step simple c e)  step (F unit) c (Simple.quit e)
  next/step :  {c e}  Simple.next (step simple c e)  step simple   c (Simple.next e)
  {-# REWRITE quit/step next/step #-}

{-# TERMINATING #-}
every : cmp simple
Simple.quit every = ret triv
Simple.next every = step simple 1 every

Φ : val bool  
Φ false = 1
Φ true  = 0

{-# TERMINATING #-}
alternating : cmp (Π bool λ _  simple)
Simple.quit (alternating b) = step (F unit) (Φ b) (ret triv)
Simple.next (alternating false) = step simple 2 (alternating true)
Simple.next (alternating true ) = alternating false

record _≈_ (s₁ s₂ : cmp simple) : Set where
  coinductive
  field
    quit : Simple.quit s₁  Simple.quit s₂
    next : Simple.next s₁  Simple.next s₂
postulate
  _≈⁻_ : (s₁ s₂ : cmp simple)  tp⁻
  ≈⁻/decode : {s₁ s₂ : cmp simple}  val (U (s₁ ≈⁻ s₂))  s₁  s₂
  {-# REWRITE ≈⁻/decode #-}

≈-cong : (c : ) {x y : Simple}  x  y  step simple c x  step simple c y
_≈_.quit (≈-cong c h) = Eq.cong (step (F unit) c) (_≈_.quit h)
_≈_.next (≈-cong c h) = ≈-cong c (_≈_.next h)

{-# TERMINATING #-}
every≈alternating :  b  alternating b  step simple (Φ b) every
_≈_.quit (every≈alternating _) = refl
_≈_.next (every≈alternating false) = ≈-cong 2 (every≈alternating true)
_≈_.next (every≈alternating true ) = every≈alternating false

simple-program : tp⁺
simple-program = nat

{-# TERMINATING #-}
ψ : cmp (Π simple-program λ _  Π (U simple) λ _  F unit)
ψ zero    s = Simple.quit s
ψ (suc n) s = ψ n (Simple.next s)

_≈'_ : (q₁ q₂ : cmp simple)  tp⁻
s₁ ≈' s₂ = Π simple-program λ p  ψ p s₁ ≡⁻[ F unit ] ψ p s₂

{-# TERMINATING #-}
classic-amortization : {s₁ s₂ : cmp simple}  val (U (s₁ ≈⁻ s₂)  U (s₁ ≈' s₂))
classic-amortization = forward , backward
  where
    forward : {s₁ s₂ : cmp simple}  s₁  s₂  cmp (s₁ ≈' s₂)
    forward h zero    = _≈_.quit h
    forward h (suc n) = forward (_≈_.next h) n

    backward : {s₁ s₂ : cmp simple}  cmp (s₁ ≈' s₂)  s₁  s₂
    _≈_.quit (backward classic) = classic zero
    _≈_.next (backward classic) = backward  n  classic (suc n))