import Data.List
import System.Random
import Criterion.Main
import Control.Monad.Par
import Control.Exception (evaluate)
parDivConq :: NFData b
=> (a -> Bool) -- test if problem is small enough
-> (a -> [a]) -- or (a -> (a,a)), to split the problem into smaller ones
-> (a -> b) -- solver for sequential
-> ([b] -> b) -- or ((b,b) -> b), merge sub-solutions ...
-> a -- input
-> Par b
parDivConq smallEnough splitInputs seqSolver merge input =
if smallEnough input then return (seqSolver input)
else do
let ins = splitInputs input
outs <- parMapM (parDivConq smallEnough splitInputs seqSolver merge) ins
return $ merge outs
divConq :: NFData b
=> (a -> Bool) -- test if problem is small enough
-> (a -> [a]) -- or (a -> (a,a)), to split the problem into smaller ones
-> (a -> b) -- solver for sequential
-> ([b] -> b) -- or ((b,b) -> b), merge sub-solutions ...
-> a -- input
-> b -- output
divConq smallEnough splitInputs seqSolver merge input =
runPar $ parDivConq smallEnough splitInputs seqSolver merge input
mergeTwo :: Ord a => [a] -> [a] -> [a]
mergeTwo xs [] = xs
mergeTwo [] ys = ys
mergeTwo (x:xs) (y:ys) =
if y < x then y : mergeTwo (x:xs) ys
else x : mergeTwo xs (y:ys)
splitInTwo :: [a] -> ([a], [a])
splitInTwo [] = error "Cannot split two lists"
splitInTwo elements =
let
size = length elements
middle = size `div` 2
in
splitAt middle elements
seqFusionSort :: Ord a => [a] -> [a]
seqFusionSort [] = []
seqFusionSort [x] = [x]
seqFusionSort xs =
let
(as, bs) = splitInTwo xs
sortedA = seqFusionSort as
sortedB = seqFusionSort bs
in
mergeTwo sortedA sortedB
parFusionSort :: (NFData a, Ord a) => Int -> [a] -> [a]
parFusionSort sizeThreshold = divConq smallEnough splitInputs seqFusionSort (foldl mergeTwo [])
where
smallEnough :: [a] -> Bool
smallEnough elements = length elements < sizeThreshold
splitInputs :: [a] -> [[a]]
splitInputs elements =
let (xs, ys) = splitInTwo elements in
[xs, ys]
main :: IO ()
main = do
let n = 10^(7 :: Int)
let elements = (take n (randoms (mkStdGen 211570155)) :: [Int] )
putStrLn "Begin sort"
_ <- evaluate $ parFusionSort (10^(6 :: Int)) elements
putStrLn "Finished sorting."