{- |
Module      :  ELynx.Tools.Concurrent
Description :  Tools for concurrent random calculations
Copyright   :  (c) Dominik Schrempf 2019
License     :  GPL-3

Maintainer  :  dominik.schrempf@gmail.com
Stability   :  unstable
Portability :  portable

Creation date: Tue May  7 10:33:24 2019.

-}

module ELynx.Tools.Concurrent
  (
    -- * MWC
    getNGen
  , splitGen
   -- * Parallel stuff
  , getChunks
  , parMapChunk
  ) where

import           Control.Monad
import           Control.Parallel.Strategies
import qualified Data.Vector                 as V
import           Data.Word
import           System.Random.MWC

-- | Get a number of generators, possibly with a fixed seed.
getNGen :: Int -> Maybe [Word32] -> IO [GenIO]
getNGen n ms = do
  g <- maybe createSystemRandom (initialize . V.fromList) ms
  splitGen n g

-- | Split a generator.
splitGen :: Int -> GenIO -> IO [GenIO]
splitGen n gen
  | n <= 0    = return []
  | otherwise =
  fmap (gen:) . replicateM (n-1) $
  initialize =<< (uniformVector gen 256 :: IO (V.Vector Word32))

-- | For a given number of capabilities and number of calculations, get chunk
-- sizes. The chunk sizes will be as evenly distributed as possible and sum up
-- to the number of calculations.
getChunks :: Int -> Int -> [Int]
getChunks c n = ns
  where n'  = n `div` c
        r = n `mod` c
        ns = replicate r (n'+1) ++ replicate (c - r) n'

-- | Parallel map with given chunk size.
parMapChunk :: Int -> (a -> b) -> [a] -> [b]
parMapChunk n f as = map f as `using` parListChunk n rseq