{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Crypto.Lol.GaussRandom
( realGaussian, realGaussians ) where
import Crypto.Lol.Prelude
import qualified Data.Vector.Generic as V
import Control.Applicative
import Control.Monad
import Control.Monad.Random
{-# INLINABLE realGaussian #-}
realGaussian :: forall v q m .
(ToRational v, OrdFloat q, Random q, MonadRandom m)
=> v -> m (q,q)
realGaussian svar =
let var = realToField svar / pi :: q
in do (u,v) <- iterateWhile uvGuard getUV
let t = u*u+v*v
com = sqrt (-var * log t / t)
return (u*com,v*com)
where getUV = do u <- getRandomR (-one,one)
v <- getRandomR (-one,one)
return (u,v)
uvGuard (u,v) = (u*u+v*v >= one) || (u*u+v*v == zero)
realGaussians ::
(ToRational svar, OrdFloat i, Random i, V.Vector v i, MonadRandom m)
=> svar -> Int -> m (v i)
{-# INLINABLE realGaussians #-}
realGaussians var n
| odd n = V.tail <$> realGaussians var (n+1)
| otherwise = V.fromList . uncurry (++) . unzip <$>
replicateM (n `div` 2) (realGaussian var)
iterateWhile :: (Monad m) => (a -> Bool) -> m a -> m a
{-# INLINE iterateWhile #-}
iterateWhile p x = go
where go = do
y <- x
if p y then go else return y