------------------------------------------------------------------------
-- The Agda standard library
--
-- *Pseudo-random* number generation
------------------------------------------------------------------------

{-# OPTIONS --cubical-compatible --guardedness #-}

module System.Random where

import System.Random.Primitive as Prim

open import Data.Bool.Base using (T)
open import Data.Nat.Base using (; z≤n) hiding (module )
open import Foreign.Haskell.Pair using (_,_)
open import Function.Base using (_$_; _∘_)
open import IO.Base using (IO; lift; lift!; _<$>_; _>>=_; pure)
import IO.Effectful as IO
open import Level using (0ℓ; suc; _⊔_; lift)
open import Relation.Binary.Core using (Rel)

------------------------------------------------------------------------
-- Ranged generation shall return proofs

record InBounds {a r} {A : Set a} (_≤_ : Rel A r) (lo hi : A) : Set (a  r) where
  constructor _∈[_,_]
  field
    value : A
    .isLowerBound : lo  value
    .isUpperBound : value  hi

RandomRIO :  {a r} {A : Set a} (_≤_ : Rel A r)  Set (suc (a  r))
RandomRIO {A = A} _≤_ = (lo hi : A)  .(lo  hi)  IO (InBounds _≤_ lo hi)

------------------------------------------------------------------------
-- Instances

module Char where

  open import Data.Char.Base using (Char; _≤_)

  randomIO : IO Char
  randomIO = lift Prim.randomIO-Char

  randomRIO : RandomRIO _≤_
  randomRIO lo hi _ = do
    value  lift (Prim.randomRIO-Char (lo , hi))
    pure (value ∈[ trustMe , trustMe ])
    where postulate trustMe :  {A}  A

module Float where

  open import Data.Float.Base using (Float; _≤_)

  randomIO : IO Float
  randomIO = lift Prim.randomIO-Float

  randomRIO : RandomRIO _≤_
  randomRIO lo hi _ = do
    value  lift (Prim.randomRIO-Float (lo , hi))
    pure (value ∈[ trustMe , trustMe ])
    where postulate trustMe :  {A}  A

module  where

  open import Data.Integer.Base using (; _≤_)

  randomIO : IO 
  randomIO = lift Prim.randomIO-Int

  randomRIO : RandomRIO _≤_
  randomRIO lo hi _ = do
    value  lift (Prim.randomRIO-Int (lo , hi))
    pure (value ∈[ trustMe , trustMe ])
    where postulate trustMe :  {A}  A

module  where

  open import Data.Nat.Base using (; _≤_)

  randomIO : IO 
  randomIO = lift Prim.randomIO-Nat

  randomRIO : RandomRIO _≤_
  randomRIO lo hi _ = do
    value  lift (Prim.randomRIO-Nat (lo , hi))
    pure (value ∈[ trustMe , trustMe ])
    where postulate trustMe :  {A}  A

module Word64 where

  open import Data.Word64.Base using (Word64; _≤_)

  randomIO : IO Word64
  randomIO = lift Prim.randomIO-Word64

  randomRIO : RandomRIO _≤_
  randomRIO lo hi _ = do
    value  lift (Prim.randomRIO-Word64 (lo , hi))
    pure (value ∈[ trustMe , trustMe ])
    where postulate trustMe :  {A}  A

module Fin where

  open import Data.Nat.Base as  using (suc; NonZero; z≤n; s≤s)
  import Data.Nat.Properties as 
  open import Data.Fin.Base using (Fin; _≤_; fromℕ<; toℕ)
  import Data.Fin.Properties as Fin

  randomIO :  {n}  .{{NonZero n}}  IO (Fin n)
  randomIO {n = n@(suc _)} = do
    suc k ∈[ lo≤k , k≤hi ]  ℕ.randomRIO 1 n (s≤s z≤n)
    pure (fromℕ< k≤hi)

  toℕ-cancel-InBounds :  {n} {lo hi : Fin n} 
                        InBounds ℕ._≤_ (toℕ lo) (toℕ hi) 
                        InBounds _≤_ lo hi
  toℕ-cancel-InBounds {n} {lo} {hi} (k ∈[ toℕlo≤k , k≤toℕhi ]) =
    let
      .k<n  : k ℕ.< n
      k<n = ℕ.≤-<-trans k≤toℕhi (Fin.toℕ<n hi)

      .lo≤k : lo  fromℕ< k<n
      lo≤k = Fin.toℕ-cancel-≤ $ let open ℕ.≤-Reasoning in begin
        toℕ lo           ≤⟨ toℕlo≤k 
        k                ≡⟨ Fin.toℕ-fromℕ< k<n 
        toℕ (fromℕ< k<n) 

      .k≤hi : fromℕ< k<n  hi
      k≤hi = Fin.toℕ-cancel-≤ $ let open ℕ.≤-Reasoning in begin
        toℕ (fromℕ< k<n) ≡⟨ Fin.toℕ-fromℕ< k<n 
        k                ≤⟨ k≤toℕhi 
        toℕ hi           

    in fromℕ< k<n ∈[ lo≤k , k≤hi ]

  randomRIO :  {n}  RandomRIO {A = Fin n} _≤_
  randomRIO {n} lo hi p = do
    k  ℕ.randomRIO (toℕ lo) (toℕ hi) (Fin.toℕ-mono-≤ p)
    pure (toℕ-cancel-InBounds k)

module List {a} {A : Set a} (rIO : IO A)  where

  open import Data.List.Base using (List; replicate)
  open import Data.List.Effectful using (module TraversableA)

  -- Careful: this can generate very long lists!
  -- You may want to use Vec≤ instead.
  randomIO : IO (List A)
  randomIO = do
    lift n  lift! ℕ.randomIO
    TraversableA.sequenceA IO.applicative $ replicate n rIO

module Vec {a} {A : Set a} (rIO : IO A) (n : ) where

  open import Data.Vec.Base using (Vec; replicate)
  open import Data.Vec.Effectful using (module TraversableA)

  randomIO : IO (Vec A n)
  randomIO = TraversableA.sequenceA IO.applicative $ replicate n rIO

module Vec≤ {a} {A : Set a} (rIO : IO A) (n : ) where

  open import Data.Vec.Bounded.Base using (Vec≤; _,_)

  randomIO : IO (Vec≤ A n)
  randomIO = do
    lift (len ∈[ _ , len≤n ])  lift! (ℕ.randomRIO 0 n z≤n)
    vec  Vec.randomIO rIO len
    pure (vec , len≤n)

module String where

  open import Data.String.Base using (String; fromList)

  -- Careful: this can generate very long lists!
  -- You may want to use String≤ instead.
  randomIO : IO String
  randomIO = fromList <$> List.randomIO Char.randomIO

module String≤ (n : ) where

  import Data.Vec.Bounded.Base as Vec≤
  open import Data.String.Base using (String; fromList)

  randomIO : IO String
  randomIO = fromList  Vec≤.toList <$> Vec≤.randomIO Char.randomIO n

open import Data.Char.Base using (Char; _≤_)

module RangedString≤ (a b : Char)  .(a≤b : a  b) (n : ) where

  import Data.Vec.Bounded.Base as Vec≤
  open import Data.String.Base using (String; fromList)

  randomIO : IO String
  randomIO =
    fromList  Vec≤.toList  Vec≤.map InBounds.value
    <$> Vec≤.randomIO (Char.randomRIO a b a≤b) n