module TimeSeries.PRG64 where
import Data.Bits
import Data.List (intersperse, unfoldr)
import Data.Word
rnd ::
Word64
-> Word64
-> Integer
-> ([Double], [Double])
rnd bw nb seed = (r,b) where
r = take (fromIntegral bw) $ unfoldr f (prg64Init seed1)
b = take (fromIntegral nb) $ unfoldr f (prg64Init seed2)
f g = let (g',w64) = prg64Next g in Just (fromIntegral w64 / max64, g')
seed1 = seed
seed2 = seed + 0x9387429
max64 = fromIntegral (maxBound :: Word64)
data PRG64 = PRG64 {
prg64A, prg64B :: !Word64
}
deriving Show
prg64Keys :: [(Word64, Word64)]
prg64Keys =
[ (0xb7e151628aed2a6b, 0x9e3779b97f4a7c15)
, (0x1234567890abcdef, 0xfedcba9876543210)
, (0x0000000000000001, 0x0000000000000002)
, (0xC045000000000000, 0x34f5b9d947cc1a58)
, (0x1bbf46f5c26e05c7, 0xd44cae3c25bd8080)
, (0xe593d49150265452, 0xdb1953f8241b620e)
, (0x22bf42c19c5591b8, 0xb1a3180c5032e4c2)
]
prg64Next :: PRG64 -> (PRG64, Word64)
prg64Next (PRG64 a0 b0) = (PRG64 ae be, ae `xor` b0)
where
(ae,be) = foldr rounding (a0,b0) prg64Keys
rounding (a,b) (s1,s2) = (a', b')
where
a' = rotateL (xor a b) (fromIntegral b) + s1
b' = rotateL (xor a' b) (fromIntegral a') + s2
prg64Init :: Integer -> PRG64
prg64Init seed = result
where
(initValue, nRounds') = seed `divMod` 11
nRounds = fromIntegral (nRounds' + 3)
a = fromIntegral $ shiftR initValue 64
b = fromIntegral $ initValue
result = fst $ head $ drop nRounds $ iterate (prg64Next . fst) (PRG64 a b, 0)
prg64Bits :: Num a => PRG64 -> [[a]]
prg64Bits prg = map randomBits randoms
where
randoms = map snd $ tail $ iterate (prg64Next . fst) (prg,0)
bitsMasks = map (shiftL 1) [0..63]
randomBit r mask = if (r .&. mask) == 0 then 1 else 1
randomBits r = map (randomBit r) bitsMasks
generateRandomVectorsCSV :: Integer -> Int -> Int -> String
generateRandomVectorsCSV seed nbits nvalues
| nbits > 64 = error "Too many bits."
| otherwise = text
where
bits :: [[Int]]
bits = prg64Bits (prg64Init seed)
text = unlines $
map (concat . intersperse "\t" . map show . take nbits) $ take nvalues bits