packages feed

mini-1.7.0.0: src/Mini/Random/Class.hs

-- | A pure interface for pseudorandom value generation
module Mini.Random.Class (
  -- * Classes
  Generator (
    nextWord32,
    nextWord64
  ),
  Splittable (
    split
  ),
  Random (
    random
  ),

  -- * Operations
  randoms,
) where

import Data.Bifunctor (
  first,
 )
import Data.Bits (
  shiftL,
  shiftR,
  (.&.),
 )
import Data.Int (
  Int16,
  Int32,
  Int64,
  Int8,
 )
import Data.List (
  unfoldr,
 )
import Data.Word (
  Word,
  Word16,
  Word32,
  Word64,
  Word8,
 )
import Mini.Random.SplitMix (
  SplitMix,
 )
import qualified Mini.Random.SplitMix as SplitMix (
  nextWord32,
  nextWord64,
  split,
 )
import Prelude (
  Bool,
  Char,
  Double,
  Float,
  Int,
  Integer,
  Maybe (
    Just
  ),
  fromIntegral,
  odd,
  toEnum,
  (.),
  (/),
 )

-- Classes

-- | The class of pseudorandom number generators
class Generator g where
  nextWord32 :: g -> (Word32, g)
  nextWord32 = first fromIntegral . nextWord64
  nextWord64 :: g -> (Word64, g)

instance Generator SplitMix where
  nextWord32 = SplitMix.nextWord32
  nextWord64 = SplitMix.nextWord64

-- | The class of splittable pseudorandom number generators
class (Generator g) => Splittable g where
  split :: g -> (g, g)

instance Splittable SplitMix where
  split = SplitMix.split

-- | The class of types for which pseudorandom values can be generated
class Random a where
  random :: (Generator g) => g -> (a, g)

instance Random Bool where
  random = first odd . nextWord32

-- | Limited to 7-bit values (ASCII)
instance Random Char where
  random = first (toEnum . fromIntegral . (.&. 0x7F)) . nextWord32

instance Random Int8 where
  random = first fromIntegral . nextWord32

instance Random Int16 where
  random = first fromIntegral . nextWord32

instance Random Int32 where
  random = first fromIntegral . nextWord32

instance Random Int64 where
  random = first fromIntegral . nextWord64

-- | Limited to 64-bit values
instance Random Int where
  random = first (fromIntegral . (fromIntegral :: Word64 -> Int64)) . nextWord64

-- | Limited to 64-bit values
instance Random Integer where
  random = first (fromIntegral . (fromIntegral :: Word64 -> Int64)) . nextWord64

instance Random Word8 where
  random = first fromIntegral . nextWord32

instance Random Word16 where
  random = first fromIntegral . nextWord32

instance Random Word32 where
  random = nextWord32

instance Random Word64 where
  random = nextWord64

-- | Limited to 64-bit values
instance Random Word where
  random = first fromIntegral . nextWord64

-- | Limited to values in the semi-open interval [0, 1)
instance Random Float where
  random = first go . nextWord32
   where
    go w =
      let w24 = w `shiftR` 8
          b24 = 1 `shiftL` 24 :: Word32
       in fromIntegral w24 / fromIntegral b24

-- | Limited to values in the semi-open interval [0, 1)
instance Random Double where
  random = first go . nextWord64
   where
    go w =
      let w53 = w `shiftR` 11
          b53 = 1 `shiftL` 53 :: Word64
       in fromIntegral w53 / fromIntegral b53

-- Operations

-- | Generate an infinite list of pseudorandom values
randoms :: (Random a, Generator g) => g -> [a]
randoms = unfoldr (Just . random)