packages feed

stc-lang-1.0.0: src/Control/Monad/SD/Combinator.hs

module Control.Monad.SD.Combinator where

import Control.Monad
import Control.Monad.Generator
import Control.Monad.Par.Class as PC
import Control.Monad.Par.Combinator (InclusiveRange, InclusiveRange(..))
import Control.Monad.SD.Ohua
import Control.Monad.SD.STCLang
import Control.Monad.SD.Smap
import Data.Dynamic2
import Data.StateElement

import Control.Monad.State as S
import Data.List as List
  ----
  -- The below comes originally from: https://hackage.haskell.org/package/monad-par-extras-0.3.3/docs/src/Control-Monad-Par-Combinator.html#parMapReduceRangeThresh
  ----
  -- | Computes a binary map\/reduce over a finite range.  The range is
  -- recursively split into two, the result for each half is computed in
  -- parallel, and then the two results are combined.  When the range
  -- reaches the threshold size, the remaining elements of the range are
  -- computed sequentially.
  --
  -- For example, the following is a parallel implementation of
  --
  -- >  foldl (+) 0 (map (^2) [1..10^6])
  --
  -- > parMapReduceRangeThresh 100 (InclusiveRange 1 (10^6))
  -- >        (\x -> return (x^2))
  -- >        (\x y -> return (x+y))
  -- >        0
  --
  -- parMapReduceRangeThresh ::
  --      (NFData a, ParFuture iv p)
  --   => Int -- ^ threshold
  --   -> InclusiveRange -- ^ range over which to calculate
  --   -> (Int -> p a) -- ^ compute one result
  --   -> (a -> a -> p a) -- ^ combine two results (associative)
  --   -> a -- ^ initial result
  --   -> p a
  -- parMapReduceRangeThresh threshold range fn binop init =
  -- loop min max
  -- where
  --   loop min max
  --     | max - min <= threshold =
  --       let mapred a b = do
  --             x <- fn b
  --             result <- a `binop` x
  --             return result
  --        in foldM mapred init [min .. max]
  --     | otherwise = do
  --       let mid = min + ((max - min) `quot` 2)
  --       rght <- spawn $ loop (mid + 1) max
  --       l <- loop min mid
  --       r <- get rght
  --       l `binop` r

instance Show InclusiveRange

mapReduceRangeThresh ::
       (NFData a, Typeable a, Show a)
    => Int -- ^ threshold
    -> InclusiveRange -- ^ range over which to calculate
    -> (Int -> a) -- ^ compute one result
    -> (a -> a -> a) -- ^ combine two results (associative)
    -> a -- ^ initial result
    -> IO a
mapReduceRangeThresh threshold range fn binop init
    -- sadly I could not use STCLang to build this :(
    -- reason: it must be STCLang a b to implement liftSignal instead of just
    -- STCLang b just like OhuaM b
    -- (_, [reduceState]) <- runOhuaM mapReduce [toS init]
    -- return $ fromS reduceState
 = do
    (_, [reduceState]) <- runSTCLang mapReduce chunkGenerator
    return $ fromS reduceState
  where
    mapReduce = do
        reduceST <- liftWithState (return init) reduce
        -- return $\x -> smapGen ((pure . mapAndCombine) >=> reduceST) x
        return $ smapGen ((pure . mapAndCombine) >=> reduceST)
      -- mapReduce = do
      --   smapGen
      --     ((pure . mapAndCombine) >=> liftWithIndex 0 reduce)
      --     chunkGenerator
    chunkGenerator :: Generator IO InclusiveRange
    chunkGenerator =
        flip stateToGenerator range $ do
            (InclusiveRange mi ma) <- S.get
            if mi >= ma
                then return Nothing
                else let mi' = min (mi + threshold) ma
                      in do S.put $ InclusiveRange (mi' + 1) ma
                            return $ Just $ InclusiveRange mi mi'
    list (InclusiveRange mi ma)
        | mi >= ma = []
        | otherwise = InclusiveRange mi mi' : list (InclusiveRange (mi' + 1) ma)
      where
        mi' = min (mi + threshold) ma
    mapAndCombine (InclusiveRange mi ma) =
        let mapred a b =
                let x = fn b
                    result = a `binop` x
                 in result
         in List.foldl mapred init [mi .. ma]
    reduce v = S.get >>= (S.put . (`binop` v))
  --{-# INLINE parMapReduceRangeThresh #-}
  -- streams output from the map phase to the reduce phase

mapReduce ::
       (NFData a, NFData b, Typeable b, Show a, Show b)
    => (a -> b)
    -> (b -> b -> b)
    -> b
    -> [a]
    -> IO b
mapReduce mapper reducer init xs = do
    (_, [reduceState]) <- runOhuaM algo [toS init]
    return $ fromS reduceState
  where
    algo = smapGen (pure . mapper >=> liftWithIndex 0 reduce) $ listGenerator xs
    reduce v = S.get >>= (S.put . (`reducer` v))
  --{-# INLINE mapReduce #-}