{-# OPTIONS --erased-cubical #-}
module Comparision.Hardy where

open import Cubical.Foundations.Prelude

open import Agda.Builtin.Nat renaming (Nat to ) hiding (_+_; _<_)
open import Agda.Builtin.List using (List; []; _∷_)
open import Agda.Builtin.IO
open import Agda.Builtin.String
open import Agda.Builtin.Unit

open import PropTrunc

open import BrouwerTree.Core
open import BrouwerTree.Properties.Core
open import BrouwerTree.Arithmetic.Addition

-- Cubical Agda gets stuck transporting in inductive families, so we
-- reimplement the parts of CNF that we need with pure propositional
-- equality, without cubical features
import CantorNormalForm.WithPropositionalEquality.Hardy as CNF
import CantorNormalForm.WithPropositionalEquality.Base as CNF
import CantorNormalForm.WithPropositionalEquality.Addition as CNF


hardy : Brw      
hardy zero n =  n 
hardy (Brw.succ a) n = hardy a (suc n)
hardy (limit f) n = hardy (f n) n
hardy (bisim f {f↑} g {g↑} f∼g i) = λ n  squash  (hardy (f n) n) (hardy (g n) n) i
hardy (trunc x y p q i j) n =
  hcomp  { k (i = i0)  squash (hardy x n) (hardy (p j) n) k
           ; k (i = i1)  squash (hardy x n) (hardy (q j) n) k
           ; k (j = i0)  squash (hardy x n) (hardy x n) k
           ; k (j = i1)  squash (hardy x n) (hardy y n) k
           }) (hardy x n)

-- To be able to use --erased-cubical, we reimplement some of the
-- features we need of Brouwer trees that rely on files from the cubical
-- library not declared to work in --erased-cubical
mutual
  ω·_ : Brw  Brw
  ω· zero = zero
  ω· (succ y) = (ω· y) + ω
  ω· (limit f {f↑}) = limit  n  ω· f n) {ω·-increasing f↑}
  ω· bisim f {f↑} g {g↑} (f≲g , g≲f) i =
    bisim  n  ω· f n) {ω·-increasing f↑}
           n  ω· g n) {ω·-increasing g↑}
          (ω·-preserves-≲ f≲g , ω·-preserves-≲ g≲f) i
  (ω· trunc x y p q i j) =
    trunc (ω· x) (ω· y)  j  ω· (p j))  j  ω· (q j)) i j

  ω·-mono :  {y z : Brw}  y  z  (ω· y)  (ω· z)
  ω·-mono ≤-zero = ≤-zero
  ω·-mono (≤-trans p q) = ≤-trans (ω·-mono p) (ω·-mono q)
  ω·-mono (≤-succ-mono {x} {y} p) =
    ≤-limiting _  k  ≤-cocone _ {k = k} (+x-mono (ι k) (ω·-mono p)))
  ω·-mono (≤-cocone f p) = ≤-cocone  n  ω· f n) (ω·-mono p)
  ω·-mono (≤-limiting f p) = ≤-limiting  n  ω· f n)  k  ω·-mono (p k))
  ω·-mono (≤-trunc p q i) = ≤-trunc (ω·-mono p) (ω·-mono q) i

  ω·-mono-< : {y z : Brw}  y < z  (ω· y) < (ω· z)
  ω·-mono-< p = ≤-trans (≤-cocone _ {k = 1} ≤-refl) (ω·-mono p)

  ω·-increasing :  {f}  increasing f  increasing  n  ω· f n)
  ω·-increasing {f} f↑ k = ω·-mono-< (f↑ k)

  {-# TERMINATING #-}
  ω·-preserves-≲ :  {f g}  f  g   n  ω· f n)   n  ω· g n)
  ω·-preserves-≲ f≲g k = ∥-∥rec isPropPropTrunc  { (l , fk≤gl)   l , ω·-mono fk≤gl  }) (f≲g k)

ω^⟨_⟩ :   Brw
ω^⟨ zero  = one
ω^⟨ suc n  = ω· ω^⟨ n 

postulate
  return :  {a} {A : Type a}  A  IO A
  _>>=_  :  {a b} {A : Type a} {B : Type b}  IO A  (A  IO B)  IO B
  putStr      : String  IO 
  putStrLn    : String  IO 

{-# FOREIGN GHC import qualified Data.Text    as T   #-}
{-# FOREIGN GHC import qualified Data.Text.IO as TIO #-}
{-# COMPILE GHC putStr     = TIO.putStr                #-}
{-# COMPILE GHC putStrLn   = TIO.putStrLn              #-}

_>>_  :  {a b} {A : Type a} {B : Type b}  IO A  IO B  IO B
ma >> mb = ma >>=  _  mb)

{-# COMPILE GHC return = \_ _ -> return    #-}
{-# COMPILE GHC _>>=_  = \_ _ _ _ -> (>>=) #-}


printTrunc :    -> IO {Agda.Primitive.lzero} 
printTrunc  x  = putStrLn (primShowNat x)
printTrunc (squash x y i) = admitIO⊤≡ (printTrunc x) (printTrunc y) i where
  -- For printing purposes, we can happily postulate that all IO actions
  -- are equal
  postulate
    admitIO⊤≡ : (p q : IO )  p  q


{-# FOREIGN GHC import Criterion.Main #-}

postulate
  Benchmarkable : Set
  Benchmark     : Set
  whnf          :  {a b} {A : Set a} {B : Set b}  (A  B)  A  Benchmarkable
  whnfIO        :  {a} {A : Set a}  IO A  Benchmarkable
  bench         : String  Benchmarkable  Benchmark
  bgroup        : String  List Benchmark  Benchmark
  defaultMain   : List Benchmark  IO 

{-# COMPILE GHC Benchmarkable = type Criterion.Main.Benchmarkable #-}
{-# COMPILE GHC Benchmark     = type Criterion.Main.Benchmark     #-}

{-# COMPILE GHC whnf   = \ _ _ _ _ -> Criterion.Main.whnf   #-}
{-# COMPILE GHC whnfIO = \ _ _     -> Criterion.Main.whnfIO #-}

{-# COMPILE GHC bench  = Criterion.Main.bench  . Data.Text.unpack #-}
{-# COMPILE GHC bgroup = Criterion.Main.bgroup . Data.Text.unpack #-}

{-# COMPILE GHC defaultMain = Criterion.Main.defaultMain #-}

sizes = 500  1000  1500  2000  2500  3000  10000  []
sizesCNF = 500  1000  1500  2000  2500  3000  10000  20000  30000  40000  []

main : IO {Agda.Primitive.lzero} 
main = defaultMain
  ((bgroup "Brw" (map  n  bench (show n) (whnf (hardy ω^⟨ n ) 1)) sizes)) 
  (bgroup "Cnf" (map  n  bench (show n) (whnf (CNF.hardy CNF.ω^⟨ CNF.NtoC n ) 1)) sizesCNF)) 
  [])
  where
    show = primShowNat
    map : {A B : Set}  (A  B)  List A  List B
    map f [] = []
    map f (a  as) = f a  map f as

-- This relies on the Criterion Haskell library for benchmarking. To install it, run
--
--   cabal install --lib criterion
--
-- in a terminal.