{- |
  This module contains the raw random number generator algorithm.
  Usually you would import "Random.MWC.Pure" for a more convinient
  API.
-}

module Random.MWC.Primitive
    (
      -- * Random seed
      Seed (), seed,

      -- * Random number generation
      next_word,
    )
  where

import Data.Bits
import Data.Word

-- | An immutable random seed value for the PRNG.
data Seed =
    Seed
    {
      word1, word2, word3, word4, carry :: {-# UNPACK #-} !Word32
    }
  deriving (Eq, Ord)

magic = 0xFFFFFF4E :: Word64

seed0 =
  Seed
    0x8DC106A9
    0x42FE9BA1
    0x0284BC8A
    0xABA48CE2
    0x5935B28D

{- |
  Create a new random seed value from the supplied list of 'Word32'
  values. If the list is empty, return a default, hard-coded value.
  Otherwise, every element of the list affects the result. The list
  /must/ be finite; the function will loop forever othewise.
-}
seed :: [Word32] -> Seed
seed = foldr f seed0
  where
    f i (Seed w1 w2 w3 w4 c) = Seed w2 w3 w4 (w1 `xor` i) c

{- |
  Given an initial 'Seed' value, return a random 'Word32' and a new
  'Seed' value.

  The 'Word32' value is chosen psuedo-randomly (i.e., the same 'Seed'
  is guaranteed to always yield the same choice) with uniform
  distribution (i.e., all possibilities equally likely) over the
  complete range from 0x00000000 to 0xFFFFFFFF inclusive.
-}
next_word :: Seed -> (Word32, Seed)
next_word (Seed w1 w2 w3 w4 c) =
  let
    new = magic * (fromIntegral w4) + (fromIntegral c)
    lo  = fromIntegral $ new
    hi  = fromIntegral $ new `shift` (-32)
  in (lo, Seed lo w1 w2 w3 hi)