```-- |
-- Module      : Foundation.Random.XorShift
--
-- XorShift variant: Xoroshiro128+
-- <https://en.wikipedia.org/wiki/Xoroshiro128%2B>
--
-- Xoroshiro128+ is a PRNG that uses a shift/rotate-based linear transformation.
-- This is lar
--
-- C implementation at:
-- <http://xoroshiro.di.unimi.it/xoroshiro128plus.c>
--
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.Subtractive

-- | State of Xoroshiro128 plus
data State = State {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64

-- | Given a state, call the function 'f' with the generated Word64 and the next State
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)

-- | Same as 'next' but give a random value of type Double in the range of [0.0 .. 1.0]
nextDouble :: State -> (Double -> State -> a) -> a
nextDouble st f = next st \$ \w -> f (toDouble w)
where
-- generate a number in the interval [1..2[ by bit manipulation.
-- this generate double with a ~2^52
where

-- | Jump the state by 2^64 calls of next
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
```