packages feed

xorshift-plus-0.1.0.0: tests/spec/Random/XorshiftPlusSpec.hs

{-# LANGUAGE TypeApplications #-}

module Random.XorshiftPlusSpec where

import Test.Hspec
import Test.QuickCheck
import qualified Test.QuickCheck.Monadic as QC
import Random.XorshiftPlus
import Data.List
import Control.Monad
import System.CPUTime

run :: IO ()
run = do
  seed <- getCPUTime
  xorshift <- genXorshiftPlusInt (fromInteger seed)
  ws <- sequence $ replicate 10 $ getWord xorshift
  print ws
  is <- sequence $ replicate 10 $ getInt xorshift
  print is
  ds <- sequence $ replicate 10 $ getDouble xorshift
  print ds

rangeDouble :: Property
rangeDouble = property $ \seed -> QC.monadicIO $ do
  xorshift <- QC.run $ genXorshiftPlusInt seed
  ret <- QC.run $ sequence $ replicate 100 $ getDouble xorshift
  QC.assert $ all (\r -> 0.0 <= r && r <= 1.0) ret

calcChisq :: [Int] -> Double -> Double
calcChisq count expected = foldl' f 0.0 $ count
  where
    f :: Double -> Int -> Double
    f r x = r + ((((fromIntegral x) - expected)) ** 2 / expected)

testChisq :: Int -> Int -> [Int] -> Double
testChisq n k randoms = calcChisq count (fromIntegral n / fromIntegral k)
  where
    minInt = toInteger (minBound :: Int)
    maxInt = toInteger (maxBound :: Int)
    step = (maxInt - minInt) `div` (toInteger k)
    randoms' = map (\r -> fromInteger @Int ((toInteger r - minInt) `div` step)) randoms
    count = map length . group $ sort randoms'

execTestChisq :: IO Bool
execTestChisq = do
  xorshift <- genXorshiftPlusInt 1
  let (k, limit) = (10, 16.919)
  let n = k * 100
  randoms <- sequence $ replicate n $ getInt xorshift
  let ret = testChisq n k randoms
  return $ ret < limit

testCor :: Int -> Int -> [Double] -> Double
testCor n k randoms = let s = testCor' n randoms 0.0 in
                      let n' = fromIntegral n in
                      sqrt n' * (12.0 * s / n' - 3.0) / sqrt 13.0
  where
    testCor' :: Int -> [Double] -> Double -> Double
    testCor' 0 _       res = res
    testCor' n' randoms' res =
      let (buff, restRandoms) = splitAt k randoms' in
      let x =  head buff * head restRandoms in
      testCor' (n'-1) (tail randoms') (res+x)

execTestCor :: IO Bool
execTestCor = do
  xorshift <- genXorshiftPlusInt 1
  let n = 2500
  randoms <- sequence $ replicate ((n+1) * 3) $ getDouble xorshift
  cors <- forM [1, 2, 3] $ \k -> do
    let x = testCor n k randoms
    return x
  return $ all (\x -> (-1.96 < x && x < 1.96)) cors

spec :: Spec
spec = do
  describe "_run" $ do
    it "_run" $ do
      run
  describe "range" $ do
    it "getDouble" $ do
      rangeDouble
  describe "chisq" $ do
    it "chisq" $ do
      ret <- execTestChisq
      ret `shouldBe` True
  describe "cor" $ do
    it "cor" $ do
      ret <- execTestCor
      ret `shouldBe` True