module Data.Tempo.Random (countToRand, countToRands) where

import Data.Bits

xorwise :: Int -> Int
xorwise :: Int -> Int
xorwise Int
x =
  let a :: Int
a = Int -> Int -> Int
forall a. Bits a => a -> a -> a
xor (Int -> Int -> Int
forall a. Bits a => a -> Int -> a
shiftL Int
x Int
13) Int
x
      b :: Int
b = Int -> Int -> Int
forall a. Bits a => a -> a -> a
xor (Int -> Int -> Int
forall a. Bits a => a -> Int -> a
shiftR Int
a Int
17) Int
a
  in Int -> Int -> Int
forall a. Bits a => a -> a -> a
xor (Int -> Int -> Int
forall a. Bits a => a -> Int -> a
shiftL Int
b Int
5) Int
b

countToIntSeed :: RealFrac a => a -> Int
countToIntSeed :: a -> Int
countToIntSeed = Int -> Int
xorwise (Int -> Int) -> (a -> Int) -> a -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Int
forall a b. (RealFrac a, Integral b) => a -> b
truncate (a -> Int) -> (a -> a) -> a -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> a -> a
forall a. Num a => a -> a -> a
* a
536870912) (a -> a) -> (a -> a) -> a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int, a) -> a
forall a b. (a, b) -> b
snd ((Int, a) -> a) -> (a -> (Int, a)) -> a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. RealFrac a => a -> (Int, a)
forall a b. (RealFrac a, Integral b) => a -> (b, a)
properFraction :: (RealFrac a => a -> (Int,a))) (a -> (Int, a)) -> (a -> a) -> a -> (Int, a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
300)

intSeedToRand :: Fractional a => Int -> a
intSeedToRand :: Int -> a
intSeedToRand = (a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
536870912) (a -> a) -> (Int -> a) -> Int -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> a
forall a b. (Real a, Fractional b) => a -> b
realToFrac (Int -> a) -> (Int -> Int) -> Int -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
536870912)

-- | countToRand converts a count (eg. measure of elapsed 'time' in a Tempo) to
-- a random value in the range [0,1) by stretching 300 counts over the range of
-- [0,2**29 == 536870912) and then apply a 'xorshift' algorithm. For the latter:
-- cf. George Marsaglia (2003). "Xorshift RNGs". Journal of Statistical Software 8:14.
-- https://www.jstatsoft.org/article/view/v008i14

countToRand :: (RealFrac a, Fractional b) => a -> b
countToRand :: a -> b
countToRand = Int -> b
forall a. Fractional a => Int -> a
intSeedToRand (Int -> b) -> (a -> Int) -> a -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Int
forall a. RealFrac a => a -> Int
countToIntSeed

-- | countToRands generates multiple pseudo-random values by converting the provided
-- count as with countToRand and then recursively using the values calculated to
-- generate additional pseudo-random values, as in the 'normal' usage of a pseudo-
-- random number geneator.

countToRands :: (RealFrac a, Fractional b) => a -> Int -> [b]
countToRands :: a -> Int -> [b]
countToRands a
t Int
n = Int -> Int -> [b]
forall a. Fractional a => Int -> Int -> [a]
countToRands' (a -> Int
forall a. RealFrac a => a -> Int
countToIntSeed a
t) Int
n

countToRands' :: Fractional a => Int -> Int -> [a]
countToRands' :: Int -> Int -> [a]
countToRands' Int
seed Int
n
  | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0 = []
  | Bool
otherwise = (Int -> a
forall a. Fractional a => Int -> a
intSeedToRand Int
seed) a -> [a] -> [a]
forall a. a -> [a] -> [a]
: (Int -> Int -> [a]
forall a. Fractional a => Int -> Int -> [a]
countToRands' (Int -> Int
xorwise Int
seed) (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1))