{-# Language CPP, BangPatterns, ScopedTypeVariables #-}
module System.Random.TF.Instances
(Random (..), randomEnum) where
import Data.Bits
import Data.Int
import Data.Word
import System.Random.TF.Gen
#if !MIN_VERSION_base(4,5,0)
unsafeShiftR :: Bits a => a -> Int -> a
unsafeShiftR = shiftR
unsafeShiftL :: Bits a => a -> Int -> a
unsafeShiftL = shiftL
#endif
myUnfoldr :: (t -> (a, t)) -> t -> [a]
myUnfoldr f g = x' : myUnfoldr f g'
where
(x', g') = f g
class Random a where
randomR :: RandomGen g => (a,a) -> g -> (a,g)
random :: RandomGen g => g -> (a, g)
randomRs :: RandomGen g => (a,a) -> g -> [a]
randomRs ival g = myUnfoldr (randomR ival) g
randoms :: RandomGen g => g -> [a]
randoms g = myUnfoldr random g
boundsWrap :: Integral a => (a -> g -> (a, g)) -> (a, a) -> g -> (a, g)
boundsWrap f (l, h) rng
| l == h = (l, rng)
| l > h = mapFst (h+) $ f (l - h) rng
| otherwise = mapFst (l+) $ f (h - l) rng
where mapFst g (x, y) = (g x, y)
randomWord32 :: RandomGen g => (Word32, Word32) -> g -> (Word32, g)
randomWord32 (l, h) rng = boundsWrap randomWord32' (l, h) rng
randomInt32 :: RandomGen g => (Int32, Int32) -> g -> (Int32, g)
randomInt32 (l, h) rng = boundsWrap randomInt32' (l, h) rng
where
randomInt32' m r = case randomWord32' (fromIntegral m) r of
(x, r') -> (fromIntegral x, r')
word32Mask :: Word32 -> Word32
word32Mask w =
(((((w .>. 1) .>. 2) .>. 4) .>. 8) .>. 16)
where
w .>. n = w .|. (w `unsafeShiftR` n)
{-# INLINE randomWord32' #-}
randomWord32' :: RandomGen g => Word32 -> g -> (Word32, g)
randomWord32' k
| k' == 0 = next
| k' .&. k == 0 = \rng ->
case next rng of
(x, rng') -> (x .&. k, rng')
| otherwise = loop
where
k' = k + 1
mask = word32Mask k
loop rng
| x' <= k = (x', rng')
| otherwise = loop rng'
where
(x, rng') = next rng
x' = x .&. mask
makeWord64 :: Word32 -> Word32 -> Word64
makeWord64 w1 w2 = w1' `unsafeShiftL` 32 .|. w2'
where
w1', w2' :: Word64
w1' = fromIntegral w1
w2' = fromIntegral w2
randomWord64 :: RandomGen g => (Word64, Word64) -> g -> (Word64, g)
randomWord64 (l, h) rng = boundsWrap randomWord64' (l, h) rng
randomInt64 :: RandomGen g => (Int64, Int64) -> g -> (Int64, g)
randomInt64 (l, h) rng = boundsWrap randomInt64' (l, h) rng
where
randomInt64' m r = case randomWord64' (fromIntegral m) r of
(x, r') -> (fromIntegral x, r')
randomWord64' :: RandomGen g => Word64 -> g -> (Word64, g)
randomWord64' k
| k <= m32 = \rng ->
case randomWord32' (fromIntegral k) rng of
(x, rng') -> (fromIntegral x, rng')
| k' == 0 = \rng ->
let !(x1, rng') = next rng
!(x2, rng'') = next rng'
in (makeWord64 x1 x2, rng'')
| k' .&. k == 0 = \rng ->
let !(x1, rng') = next rng
!(x2, rng'') = next rng'
in (makeWord64 x1 x2 .&. k, rng'')
| otherwise = loop
where
m32 :: Word64
m32 = fromIntegral (maxBound :: Word32)
k' = k + 1
mask = word32Mask (fromIntegral $ k `unsafeShiftR` 32)
loop rng
| x <= k = (x, rng'')
| otherwise = loop rng''
where
(x1, rng') = next rng
(x2, rng'') = next rng'
x = makeWord64 (x1 .&. mask) x2
getShiftAndLead :: (Integral a, Bits a) => a -> (Int, Word32)
getShiftAndLead !x = cWords x 0
where
cWords !x !c
| x' == 0 = (c, fromIntegral x)
| otherwise = cWords x' (c+1)
where
x' = x `unsafeShiftR` 32
randomInteger :: RandomGen g => (Integer, Integer) -> g -> (Integer, g)
randomInteger (l, h) rng = boundsWrap randomInteger' (l, h) rng
{-# INLINE randomInteger' #-}
randomInteger' :: forall g. RandomGen g => Integer -> g -> (Integer, g)
randomInteger' k rng
| k < 2^64 = case randomWord64' (fromIntegral k) rng of
(x, rng') -> (fromIntegral x, rng')
| otherwise = loop rng
where
(w, l) = getShiftAndLead k
construct rng
| even w = construct' (fromIntegral lx) w rng'
| otherwise = construct' (fromIntegral x) (w-1) rng''
where
(lx, rng') = randomWord32' l rng
(x2, rng'') = next rng'
x = makeWord64 lx x2
construct' :: Integer -> Int -> g -> (Integer, g)
construct' !a 0 rng = (a, rng)
construct' !a n rng =
construct' (a `shiftL` 64 .|. fromIntegral x) (n-2) rng''
where
(x1, rng') = next rng
(x2, rng'') = next rng'
x = makeWord64 x1 x2
loop rng
| x <= k = (x, rng')
| otherwise = loop rng'
where
(x, rng') = construct rng
randomBounded :: (RandomGen g, Random a, Bounded a) => g -> (a, g)
randomBounded = randomR (minBound, maxBound)
instance Random Int where
randomR (a, b) rng = (fromIntegral x, rng')
where !(x, rng') = randomR (fromIntegral a :: Int64, fromIntegral b) rng
random = randomBounded
randomEnum :: (Enum a, RandomGen g) => (a, a) -> g -> (a, g)
randomEnum (a,b) g =
case randomR (fromEnum a, fromEnum b) g of
(x, g') -> (toEnum x, g')
instance Random Char where
randomR = randomEnum
random = randomBounded
instance Random Bool where
randomR = randomEnum
random = randomBounded
instance Random Integer where
randomR = randomInteger
random = randomR (toInteger (minBound::Int), toInteger (maxBound::Int))
instance Random Word32 where
randomR = randomWord32
random = next
instance Random Word64 where
randomR = randomWord64
random = randomBounded
instance Random Int32 where
randomR = randomInt32
random g = let (x, g') = next g in (fromIntegral x, g')
instance Random Int64 where
randomR = randomInt64
random = randomBounded
instance Random Word8 where
randomR (l, h) g =
let (x, g') = randomWord32 (fromIntegral l, fromIntegral h) g
in (fromIntegral x, g')
random g = let (x, g') = next g in (fromIntegral x, g')
instance Random Int8 where
randomR (l, h) g =
let (x, g') = randomInt32 (fromIntegral l, fromIntegral h) g
in (fromIntegral x, g')
random g = let (x, g') = next g in (fromIntegral x, g')
instance Random Word16 where
randomR (l, h) g =
let (x, g') = randomWord32 (fromIntegral l, fromIntegral h) g
in (fromIntegral x, g')
random g = let (x, g') = next g in (fromIntegral x, g')
instance Random Int16 where
randomR (l, h) g =
let (x, g') = randomInt32 (fromIntegral l, fromIntegral h) g
in (fromIntegral x, g')
random g = let (x, g') = next g in (fromIntegral x, g')