-- | This module provides functions to generate random numbers using balanced
--   integer tables randomized by the RealDice data or custom integer tables
module RealDice.RNG
  ( RDGen,
    randomIntR,
    randomFloat,
    randomDouble,
    mkRDGen,
    mkRDGenCustom,
  )
where

import RealDice.Generate.BalancedTables (rdIntsPrime)
import RealDice.Manipulate.GetValueFromRNGTable (getIntByIndex)
import RealDice.Manipulate.RandomizeList (randomizeList)

-- | Stores a balanced table of random integers and an index pointing to the
--   next value to return
data RDGen where
  RDGen :: {RDGen -> Int
index :: Int, RDGen -> [Int]
rngTable :: [Int]} -> RDGen

-- | Creates a new RDGen with the given index and the default Int table

-- | ==== __Examples__
--   >>> mkRDGen 143
--   {143, rdIntsPrime}
mkRDGen :: Int -> RDGen
mkRDGen :: Int -> RDGen
mkRDGen Int
i = Int -> [Int] -> RDGen
mkRDGenCustom Int
i [Int]
rdIntsPrime

-- | Creates a new RDGen with the given index and Int table

-- | Defaults to the RealDice balanced table of random integers if an empty
--   list is given

-- | ==== __Examples__
--   >>> mkRDGenCustom 143 [1, 0, 4, 3, 2]
--   {143, [1, 0, 4, 3, 2]}
--   >>> mkRDGenCustom 143 []
--   {143, rdIntsPrime}
mkRDGenCustom :: Int -> [Int] -> RDGen
mkRDGenCustom :: Int -> [Int] -> RDGen
mkRDGenCustom Int
i [] = RDGen {index :: Int
index = Int
i, rngTable :: [Int]
rngTable = [Int]
rdIntsPrime}
mkRDGenCustom Int
i [Int]
table = RDGen {index :: Int
index = Int
i, rngTable :: [Int]
rngTable = [Int]
table}

-- | Generates a random integer value between minResult and maxResult via a
--   simple table lookup

-- | ==== __Examples__
--   >>> randomIntR (1, 20) (mkRDGen 143)
--   (12, {144, rdIntsPrime})
--   >>> randomIntR (-1000000, 1000000) (mkRDGen 42)
--   (76465, {43, rdIntsPrime})
--   >>> randomIntR (10, 1) (mkRDGen 42)
--   (0, {43, rdIntsPrime})
randomIntR :: (Int, Int) -> RDGen -> (Int, RDGen)
randomIntR :: (Int, Int) -> RDGen -> (Int, RDGen)
randomIntR (Int
minResult, Int
maxResult) RDGen
rng
  | Int
minResult Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
maxResult = (Int
0, RDGen
rng)
  | Bool
otherwise =
      ( Int -> [Int] -> Int
getIntByIndex
          (Int -> [Int] -> Int
getIntByIndex (RDGen -> Int
index RDGen
rng) (RDGen -> [Int]
rngTable RDGen
rng))
          ([Int] -> [Int]
randomizeList [Int
minResult .. Int
maxResult]),
        RDGen {index :: Int
index = RDGen -> Int
index RDGen
rng Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1, rngTable :: [Int]
rngTable = RDGen -> [Int]
rngTable RDGen
rng}
      )

-- Generate a random float value between 0 and 1

-- | ==== __Examples__
--   >>> randomFloat 3 (mkRDGen 143)
--   (0.503, {144, rdIntsPrime})
--   >>> randomFloat 0 (mkRDGen 143)
--   (0, {143, rdIntsPrime})
--   >>> randomFloat (-1) (mkRDGen 143)
--   (0, {143, rdIntsPrime})
randomFloat :: Int -> RDGen -> (Float, RDGen)
randomFloat :: Int -> RDGen -> (Float, RDGen)
randomFloat Int
decimalPrecision RDGen
rng
  | Int
decimalPrecision Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = (Float
0, RDGen
rng)
  | Bool
otherwise = (Int, Float) -> RDGen -> (Float, RDGen)
randomFloatSinglePass (Int
decimalPrecision, Float
0) RDGen
rng

-- Generates a single digit to be used to compose a random float

-- | ==== __Examples__
--  >>> randomFloatSinglePass (1, 0.003) (mkRDGen 145)
--  (0.503, {146, rdIntsPrime})
--  >>> randomFloatSinglePass (0, 0) (mkRDGen 143)
--  (0, {143, rdIntsPrime})
--  >>> randomFloatSinglePass (-1, 0) (mkRDGen 143)
--  (0, {143, rdIntsPrime})
randomFloatSinglePass :: (Int, Float) -> RDGen -> (Float, RDGen)
randomFloatSinglePass :: (Int, Float) -> RDGen -> (Float, RDGen)
randomFloatSinglePass (Int
0, Float
currentFloat) RDGen
rng = (Float
currentFloat, RDGen
rng)
randomFloatSinglePass (Int
decimalPlace, Float
currentFloat) RDGen
rng = do
  let randomDigit :: Int
randomDigit = Int -> [Int] -> Int
getIntByIndex (Int -> [Int] -> Int
getIntByIndex (RDGen -> Int
index RDGen
rng) (RDGen -> [Int]
rngTable RDGen
rng)) ([Int] -> [Int]
randomizeList [Int
0 .. Int
9])
  (Int, Float) -> RDGen -> (Float, RDGen)
randomFloatSinglePass
    ( Int
decimalPlace Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1,
      Float
currentFloat Float -> Float -> Float
forall a. Num a => a -> a -> a
+ (Int -> Float
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
randomDigit Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ (Float
10 Float -> Int -> Float
forall a b. (Num a, Integral b) => a -> b -> a
^ Int
decimalPlace))
    )
    (RDGen {index :: Int
index = RDGen -> Int
index RDGen
rng Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1, rngTable :: [Int]
rngTable = RDGen -> [Int]
rngTable RDGen
rng})

-- Generates a random float value between 0 and 1

-- | ==== __Examples__
--   >>> randomDouble 3 (mkRDGen 143)
--   (0.503, {144, rdIntsPrime})
--   >>> randomDouble 0 (mkRDGen 143)
--   (0, {143, rdIntsPrime})
--   >>> randomDouble (-1) (mkRDGen 143)
--   (0, {143, rdIntsPrime})
randomDouble :: Int -> RDGen -> (Double, RDGen)
randomDouble :: Int -> RDGen -> (Double, RDGen)
randomDouble Int
decimalPrecision RDGen
rng
  | Int
decimalPrecision Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = (Double
0, RDGen
rng)
  | Bool
otherwise = (Int, Double) -> RDGen -> (Double, RDGen)
randomDoubleSinglePass (Int
decimalPrecision, Double
0) RDGen
rng

-- Generates a single digit to be used to compose a random float

-- | ==== __Examples__
--   >>> randomDoubleSinglePass (1, 0.003) (mkRDGen 145)
--   (0.503, {146, rdIntsPrime})
--   >>> randomDoubleSinglePass (0, 0) (mkRDGen 143)
--   (0, {143, rdIntsPrime})
--   >>> randomDoubleSinglePass (-1, 0) (mkRDGen 143)
--   (0, {143, rdIntsPrime})
randomDoubleSinglePass :: (Int, Double) -> RDGen -> (Double, RDGen)
randomDoubleSinglePass :: (Int, Double) -> RDGen -> (Double, RDGen)
randomDoubleSinglePass (Int
0, Double
currentDouble) RDGen
rng = (Double
currentDouble, RDGen
rng)
randomDoubleSinglePass (Int
decimalPlace, Double
currentDouble) RDGen
rng = do
  let randomDigit :: Int
randomDigit = Int -> [Int] -> Int
getIntByIndex (Int -> [Int] -> Int
getIntByIndex (RDGen -> Int
index RDGen
rng) (RDGen -> [Int]
rngTable RDGen
rng)) ([Int] -> [Int]
randomizeList [Int
0 .. Int
9])
  (Int, Double) -> RDGen -> (Double, RDGen)
randomDoubleSinglePass
    ( Int
decimalPlace Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1,
      Double
currentDouble Double -> Double -> Double
forall a. Num a => a -> a -> a
+ (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
randomDigit Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Double
10 Double -> Int -> Double
forall a b. (Num a, Integral b) => a -> b -> a
^ Int
decimalPlace))
    )
    (RDGen {index :: Int
index = RDGen -> Int
index RDGen
rng Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1, rngTable :: [Int]
rngTable = RDGen -> [Int]
rngTable RDGen
rng})