language-puppet-1.5.1: src/Puppet/Interpreter/RubyRandom.hs
module Puppet.Interpreter.RubyRandom
( randInit,
limitedRand,
)
where
import qualified Data.List as List
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Unboxed.Mutable as VM
import XPrelude
data RandState = RandState
{ _array :: V.Vector Int,
_left :: Int,
_initf :: Int,
_next :: Int
}
deriving (Show)
mixbits :: Int -> Int -> Int
mixbits u v = (u .&. 0x80000000) .|. (v .&. 0x7fffffff)
twist :: Int -> Int -> Int
twist u v = (mixbits u v `shiftR` 1) `xor` ma
where
ma =
if (v .&. 1) == 1
then 0x9908b0df
else 0
valN :: Int
valN = 624
valM :: Int
valM = 397
initGenrand :: Integer -> RandState
initGenrand rseed = RandState (V.fromList (scanl genfunc seed [1 .. (valN - 1)])) 1 1 0
where
seed = fromIntegral rseed .&. 0xffffffff
genfunc :: Int -> Int -> Int
genfunc curval x = (1812433253 * (curval `xor` (curval `shiftR` 30)) + x) .&. 0xffffffff
nextState :: RandState -> RandState
nextState (RandState array _ initf _) = RandState narray valN 1 0
where
rarray =
if initf == 0
then _array (initGenrand 5489)
else array
narray = V.modify (\v -> twist1 v >> twist2 v >> final v) rarray
twist1 v = mapM_ (twist' valM v) [0 .. (valN - valM - 1)]
twist2 v = mapM_ (twist' (valM - valN) v) [(valN - valM) .. (valN - 2)]
final v = do
a <- VM.read v (valN - 1)
b <- VM.read v 0
pm <- VM.read v (valM - 1)
let res = pm `xor` twist a b
VM.write v (valN - 1) res
twist' idx v n = do
a <- VM.read v n
b <- VM.read v (n + 1)
pm <- VM.read v (idx + n)
let res = pm `xor` twist a b
VM.write v n res
-- needs refactoring, too tedious for me
initGenrandBigint :: Integer -> RandState
initGenrandBigint seed =
let intarray = unfoldr reduceint seed
reduceint :: Integer -> Maybe (Integer, Integer)
reduceint 0 = Nothing
reduceint x = Just (x .&. 0xffffffff, x `shiftR` 32)
initstate = _array (initGenrand 19650218)
keylist = concat (repeat intarray)
jlist = concat (repeat [0 .. (length intarray - 1)])
kmax = max (length intarray) valN
state1 = foldl' apply1 initstate (List.zip3 keylist jlist [1 .. kmax])
apply1 :: V.Vector Int -> (Integer, Int, Int) -> V.Vector Int
apply1 ra (initKey, j, ri) =
let (a, i, sti, stim) = rollover ra ri
nsti = ((sti `xor` ((stim `xor` (stim `shiftR` 30)) * 1664525)) + fromIntegral initKey + j) .&. 0xffffffff
in a V.// [(i, nsti)]
state2 = foldl' apply2 state1 [2 .. valN]
rollover :: V.Vector Int -> Int -> (V.Vector Int, Int, Int, Int)
rollover ra ri =
let (a, i) =
if ri >= valN
then (ra V.// [(0, ra V.! (valN - 1))], 1)
else (ra, ri)
in (a, i, a V.! i, a V.! (i - 1))
apply2 :: V.Vector Int -> Int -> V.Vector Int
apply2 ra ri =
let (a, i, sti, stim) = rollover ra ri
nsti = ((sti `xor` ((stim `xor` (stim `shiftR` 30)) * 1566083941)) - i) .&. 0xffffffff
in a V.// [(i, nsti)]
in RandState (state2 V.// [(0, 0x80000000)]) 1 1 0
randInit :: Integer -> RandState
randInit x =
if x <= 0xffffffff
then initGenrand x
else initGenrandBigint x
limitedRand :: RandState -> Int -> (Int, RandState)
limitedRand s n
| n <= 0 = (0, s)
| otherwise = limitedRand' s
where
masked = foldl' (\x pow -> x .|. (x `shiftR` pow)) (n - 1) [1, 2, 4, 8, 16, 32]
limitedRand' s' =
let (rval, ns) = rbGenrandInt32 s'
val = rval .&. masked
in if n <= val
then limitedRand' ns
else (val, ns)
rbGenrandInt32 :: RandState -> (Int, RandState)
rbGenrandInt32 st =
let rst =
if _left st == 1
then nextState st
else st {_left = _left st - 1}
next = _next rst
cv = _array rst V.! next
nst = rst {_next = next + 1}
y1 = cv `xor` (cv `shiftR` 11)
y2 = y1 `xor` ((y1 `shiftL` 7) .&. 0x9d2c5680)
y3 = y2 `xor` ((y2 `shiftL` 15) .&. 0xefc60000)
y4 = y3 `xor` (y3 `shiftR` 18)
in (y4, nst)