module Basement.Alg.XorShift
    ( State(..)
    , next
    , nextDouble
    , jump
    ) where
import           Data.Word
import           Data.Bits
import           Basement.Compat.Base
import           Basement.Floating (wordToDouble)
import           Basement.Numerical.Additive
import           Basement.Numerical.Subtractive
data State = State {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64
next :: State -> (Word64 -> State -> a) -> a
next (State s0 s1prev) f = f ran stNext
  where
    !stNext = State s0' s1'
    !ran    = s0 + s1prev
    !s1     = s0 `xor` s1prev
    s0'     = (s0 `rotateL` 55) `xor` s1 `xor` (s1 `unsafeShiftL` 14)
    s1'     = (s1 `rotateL` 36)
nextDouble :: State -> (Double -> State -> a) -> a
nextDouble st f = next st $ \w -> f (toDouble w)
  where
    
    
    toDouble w = wordToDouble (upperMask .|. (w .&. lowerMask)) - 1.0
      where
        upperMask = 0x3FF0000000000000
        lowerMask = 0x000FFFFFFFFFFFFF
jump :: State -> State
jump (State s0 s1) = withK 0xd86b048b86aa9922
                   $ withK 0xbeac0467eba5facb
                   $ (State 0 0)
  where
    withK :: Word64 -> State -> State
    withK !k = loop 0
      where
        loop !i st@(State c0 c1)
            | i == 64     = st
            | testBit k i = loop (i+1) (State (c0 `xor` s0) (c1 `xor` s1))
            | otherwise   = st