packages feed

xorshift-1: src/Random/Xorshift.hs

-----------------------------------------------------------------------------
--
-- Module      :  Random.Xorshift
-- Copyright   :
-- License     :  LGPL 2.0 or higher
--
-- Maintainer  :
-- Stability   :
-- Portability :
--
-- | Xorshift random generators. The generators are implemented according to
--   the paper <http://www.jstatsoft.org/v08/i14/paper> by George Marsaglia.
--   You can use the 'RandomGen' instances to generate random numbers easily.
-----------------------------------------------------------------------------

{-# LANGUAGE GeneralizedNewtypeDeriving, CPP #-}

-- Use the C-header file to generate a platform depend Xorshift type
-- This is handy as I can use the __Xorshift macro as a data-constructor.
#include "MachDeps.h"
#if WORD_SIZE_IN_BITS == 32
#define __Xorshift Xorshift32
#else
#define __Xorshift Xorshift64
#endif

module Random.Xorshift (
    Xorshift32 (..),
    Xorshift64 (..),
    Xorshift,
    newXorshift32,
    newXorshift64,
    newXorshift,
    makeXorshift32,
    makeXorshift64,
    makeXorshift,
    getInt64,
    getInt32,
    module System.Random
  ) where

import Data.Int  (Int32,Int64)
import Data.Bits (xor,shiftL,shiftR)
import System.Random
import Data.Time

-- | Xorshift generator with 32 bits.
--   This Xorshift generator uses first a left shift, then a right shift and
--   again a left shift. It uses the parameters 13, 17 and 5. See the paper for
--   more details on how the parameters affect the generation of random numbers.
--
--   The generator has a periode of 2^32-1, please notice that the
--   generator has to be initialized with a value different from 0, elseway all
--   future values will be zero as well.
newtype Xorshift32 = Xorshift32 Int32
  deriving (Eq,Show,Enum,Bounded)

-- | Xorshift generator with 64 bits.
--   This Xorshift generator uses first a left shift, then a right shift and
--   again a left shift. It uses the parameters 13, 7 and 17. See the paper for
--   more details on how the parameters affect the generation of random numbers.
--
--   The generator has a periode of 2^64-1, please notice that the
--   generator has to be initialized with a value different from 0, elseway all
--   future values will be zero as well.
newtype Xorshift64 = Xorshift64 Int64
  deriving (Eq,Show,Enum,Bounded)

-- | Generates a new Xorshift32 from the current time.
newXorshift32 :: IO Xorshift32
newXorshift32 = getRandomValue >>= return . makeXorshift32

-- | Generates a new Xorshift32 from the current time.
newXorshift64 :: IO Xorshift64
newXorshift64 = getRandomValue >>= return . makeXorshift64

-- | Generates a new Xorshift from the current time. This is either a synonym
--   for 'newXorshift32' or 'newXorshift64'.
newXorshift :: IO Xorshift
newXorshift = getRandomValue >>= return . makeXorshift

-- | Generate a new 'Xorshift32' generator. This is essentially a wrapper around
--   the constructor.
makeXorshift32 :: Integral a => a -> Xorshift32
makeXorshift32 = Xorshift32 . fromIntegral

-- | Generate a new 'Xorshift64' generator. This is essentially a wrapper around
--   the constructor.
makeXorshift64 :: Integral a => a -> Xorshift64
makeXorshift64 = Xorshift64 . fromIntegral

-- | Generate a new 'Xorshift' generator, regardless of whatever it's bytesize
--   is on your platform.
makeXorshift :: Integral a => a -> Xorshift
makeXorshift = __Xorshift . fromIntegral

-- | Get the raw contents of the random generator. This function is preferable
--   over direct usage of the constructor itself, as the internal representation
--   of the random generator may change in future releases.
--
--   The 'getInt32' function works for 'Xorshift32' and the 'getInt64' function
--   works for 'Xorshift64'. If you're using the 'Xorshift' type, use an 'Int'
--   type in combination with the 'random' methof of "System.Random".
getInt32 :: Xorshift32 -> Int32
getInt32 (Xorshift32 a) = a

getInt64 :: Xorshift32 -> Int32
getInt64 (Xorshift32 a) = a

instance RandomGen Xorshift32 where
  next a = (fromIntegral c, b) where
    b@(Xorshift32 c) = step32 a
  split  = error "Splitting of an Xorshift random generator is not implemented."
  genRange a = (fromEnum (minBound `asTypeOf` a),
                fromEnum (maxBound `asTypeOf` a))

instance RandomGen Xorshift64 where
  next a = (fromIntegral c, b) where
    b@(Xorshift64 c) = step64 a
  split  = error "Splitting of an Xorshift random generator is not implemented."
  genRange a = (fromEnum (minBound `asTypeOf` a),
                fromEnum (maxBound `asTypeOf` a))

-- | The type Xorshift is a wrapper around either 'Xorshift32' or 'Xorshift64',
--   depending on the bit size of your system. The idea behind this is, that you
--   may want to use this package as an uncomplicated, fast random generator,
--   but repeated conversion between different word sizes are an absolute
--   performance killer.
--
--   Thus, if you don't really care about the period of your random generator or
--   how long the result is, use the 'Xorshift' synonym, whenever possible.
type Xorshift = __Xorshift

-- Iterates the random generator for 32 bits
step32 :: Xorshift32 -> Xorshift32
step32 (Xorshift32 a) = Xorshift32 d where
  b = a `xor` (shiftL a 13)
  c = b `xor` (shiftR b 17)
  d = c `xor` (shiftL c  5)

-- Iterates the random generator for 64 bits
step64 :: Xorshift64 -> Xorshift64
step64 (Xorshift64 a) = Xorshift64 d where
  b = a `xor` (shiftL a 13)
  c = b `xor` (shiftR b  7)
  d = c `xor` (shiftL c 17)

-- Generates a random value from current time. We try to archieve a good result
-- by summing all information of time.
getRandomValue :: IO Integer
getRandomValue = do UTCTime (ModifiedJulianDay day) time <- getCurrentTime
                    let picosecs = truncate $ toRational time * 10^12
                        dayInPs  = day * 86400 * 10^12
                    return $! picosecs + dayInPs