{-# OPTIONS --rewriting #-}

module Examples.Decalf.ProbabilisticChoice 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.List
open import Calf.Data.Equality as Eq using (_≡_; refl; module ≡-Reasoning)
open import Calf.Data.IsBoundedG costMonoid
open import Calf.Data.IsBounded costMonoid
open import Function hiding (flip)

open import Data.Interval


postulate
  flip : (X : tp⁻)  𝕀  cmp X  cmp X  cmp X

  flip/0 : {e₀ e₁ : cmp X} 
    flip X 0𝕀 e₀ e₁  e₀
  flip/1 : {e₀ e₁ : cmp X} 
    flip X 1𝕀 e₀ e₁  e₁
  flip/same : (X : tp⁻) (e : cmp X) {p : 𝕀} 
    flip X p e e  e

  flip/sym : (X : tp⁻) (p : 𝕀) (e₀ e₁ : cmp X) 
    flip X p e₀ e₁  flip X (1- p) e₁ e₀
  flip/assocʳ : (X : tp⁻) (e₀ e₁ e₂ : cmp X) {p q r : 𝕀}  p  (p  q)  r 
    flip X p (flip X q e₀ e₁) e₂  flip X (p  q) e₀ (flip X r e₁ e₂)

flip/assocˡ : (X : tp⁻) (e₀ e₁ e₂ : cmp X) {p q r : 𝕀}  p  (p  q)  r 
  flip X p e₀ (flip X q e₁ e₂)  flip X (p  q) (flip X r e₀ e₁) e₂
flip/assocˡ X e₀ e₁ e₂ {p} {q} {r} h =
  let open ≡-Reasoning in
  begin
    flip X p e₀ (flip X q e₁ e₂)
  ≡⟨ Eq.cong  p  flip X p e₀ (flip X q e₁ e₂)) h 
    flip X (p  q  r) e₀ (flip X q e₁ e₂)
  ≡˘⟨ flip/assocʳ X e₀ e₁ e₂ (Eq.cong (_∧ q) h) 
    flip X (p  q) (flip X r e₀ e₁) e₂
  

postulate
  bind/flip : {f : val A  cmp X} {p : 𝕀} {e₀ e₁ : cmp (F A)} 
    bind {A = A} X (flip (F A) p e₀ e₁) f  flip X p (bind X e₀ f) (bind X e₁ f)
  {-# REWRITE bind/flip #-}

  flip/step : {e₀ e₁ : cmp X} {p : 𝕀} 
    step X c (flip X p e₀ e₁)  flip X p (step X c e₀) (step X c e₁)


module _ where
  bernoulli : cmp cost
  bernoulli = flip cost ½ (step⋆ 0) (step⋆ 1)

  bernoulli/upper : bernoulli ≤⁻[ cost ] step⋆ 1
  bernoulli/upper =
    let open ≤⁻-Reasoning cost in
    begin
      flip cost ½ (step⋆ 0) (step⋆ 1)
    ≲⟨ ≤⁻-mono {cost}  e  flip cost ½ e (step⋆ 1)) (≤⁺-mono step⋆ (≤⇒≤⁺ (z≤n {1}))) 
      flip cost ½ (step⋆ 1) (step⋆ 1)
    ≡⟨ flip/same cost (step⋆ 1) {½} 
      step⋆ 1
    

  binomial : cmp $ Π nat λ _  cost
  binomial zero    = ret triv
  binomial (suc n) =
    bind cost bernoulli λ _ 
    binomial n

  binomial/+ : (m n : val nat) 
    (bind cost (binomial m) λ _  binomial n)  binomial (m + n)
  binomial/+ zero    n = refl
  binomial/+ (suc m) n =
    let open ≡-Reasoning in
    begin
      ( bind cost bernoulli λ _ 
        bind cost (binomial m) λ _ 
        binomial n
      )
    ≡⟨
      ( Eq.cong (bind cost bernoulli) $ funext λ _ 
        binomial/+ m n
      )
    
      binomial (suc m + n)
    

  binomial/comm : (n : val nat) 
    (bind cost bernoulli λ _  binomial n)  (bind cost (binomial n) λ _  bernoulli)
  binomial/comm zero = refl
  binomial/comm (suc n) =
    let open ≡-Reasoning in
    begin
      ( bind cost bernoulli λ _ 
        bind cost bernoulli λ _ 
        binomial n
      )
    ≡⟨
      ( Eq.cong (bind cost bernoulli) $ funext λ _ 
        binomial/comm n
      )
    
      ( bind cost bernoulli λ _ 
        bind cost (binomial n) λ _ 
        bernoulli
      )
    

  binomial/upper : (n : val nat)  binomial n ≤⁻[ cost ] step⋆ n
  binomial/upper zero    = ≤⁻-refl
  binomial/upper (suc n) =
    let open ≤⁻-Reasoning cost in
    begin
      ( bind cost bernoulli λ _ 
        binomial n
      )
    ≲⟨ ≤⁻-mono  e  bind cost e λ _  binomial n) bernoulli/upper 
      ( bind cost (step⋆ 1) λ _ 
        binomial n
      )
    ≡⟨⟩
      step cost 1 (binomial n)
    ≲⟨ ≤⁻-mono (step cost 1) (binomial/upper n) 
      step⋆ (suc n)
    


sublist : cmp $ Π (list A) λ _  F (list A)
sublist {A} []       = ret []
sublist {A} (x  xs) =
  bind (F (list A)) (sublist {A} xs) λ xs' 
  flip (F (list A)) ½ (ret xs') (step (F (list A)) 1 (ret (x  xs')))

sublist/cost : cmp $ Π (list A) λ _  cost
sublist/cost l = binomial (length l)

sublist/is-bounded : (l : val (list A))  IsBoundedG (list A) (sublist {A} l) (sublist/cost {A} l)
sublist/is-bounded {A} []       = ≤⁻-refl
sublist/is-bounded {A} (x  xs) =
    let open ≤⁻-Reasoning cost in
    begin
      bind cost
        ( bind (F (list A)) (sublist {A} xs) λ xs' 
          flip (F (list A)) ½ (ret xs') (step (F (list A)) 1 (ret (x  xs')))
        )
         _  ret triv)
    ≡⟨⟩
      ( bind cost (sublist {A} xs) λ _ 
        flip cost ½ (ret triv) (step cost 1 (ret triv))
      )
    ≡⟨⟩
      ( bind cost (sublist {A} xs) λ _ 
        bernoulli
      )
    ≲⟨ ≤⁻-mono  e  bind cost e λ _  bernoulli) (sublist/is-bounded {A} xs) 
      ( bind cost (binomial (length xs)) λ _ 
        bernoulli
      )
    ≡˘⟨ binomial/comm (length xs) 
      binomial (length (x  xs))
    

sublist/is-bounded' : (l : val (list A))  IsBounded (list A) (sublist {A} l) (length l)
sublist/is-bounded' {A} l = ≤⁻-trans (sublist/is-bounded {A} l) (binomial/upper (length l))