{-  Parallelitaet in funktionalen Programmiersprachen, 
    Einfache Haskell-Beispielprogramme -}

module Main where 

import Data.Time.Clock (diffUTCTime, getCurrentTime)
import System.Environment (getArgs)
import System.Random (StdGen, getStdGen, randoms)
import Data.List (transpose)
import Control.Parallel (par,pseq)
import Strategies

usage :: String
usage = "1. Arg.: Versionen -  0 (binom), 1 (quicksort), 2 (qsort/parlist) \n" 
             ++ "2. Arg.: Eingabegroesse"


-- Hauptprogramm (Testumgebung)
main :: IO ()
main = do args <- getArgs
          if null args then  putStrLn usage 
            else
             do  
               let v = read (head args)  
               let n = read (head (tail args))
               putStrLn  $ "Input Size: " ++ show n
               input <- randomInts n `fmap` getStdGen
               start <- getCurrentTime
               let result = test v n input
               sforce result $ putStrLn  ("Length of Result List: " ++ (show (length result)))
               end <- getCurrentTime
               putStrLn (show (end `diffUTCTime` start) ++ " elapsed." )                   

{- Beispielaufrufe:

Binomialkoeffizienten: 
examplePar 0 30 +RTS -ls -N2 bzw. -N1 

Quicksort:
examplePar 1 100000 +RTS -ls -N2 bzw. -N1 


-}

test :: Int -> Int -> [Int] -> [Int] 
test v n input = case v of  
                    0 -> [binom  n (n `div` 2)]
                    1 -> qsort input  
                    2 -> qsort2 input 
                    
randomInts :: Int -> StdGen -> [Int]
randomInts  k g = let result = take k (randoms g)
                  in length result `pseq` result
                            
                            
-- Binomialkoeffizienten
binom :: Int -> Int -> Int
binom n k 
   |  k == 0 && n >= 0  = 1
   |  n < k  && n >= 0  = 0
   |  k <= n && k >= 0  
      =  
        b1 + b2  `using` strat
   | otherwise          = error "negative parameters"
   where b1 = binom (n-1) k
         b2 = binom (n-1) (k-1) 
         strat _ = b1 `par` b2 `pseq` ()


-- Quicksort
qsort :: (Ord a, NFData a) => [a] -> [a]
qsort [] = []
qsort (x:xs) 
  = -- length smalls `par` length bigs `pseq` 
    (smalls ++ x : bigs) `using` strat
  where smalls = qsort [y | y<-xs, y <=x] 
        bigs   = qsort [y | y<-xs, y > x]
        strat res =  rnf bigs  `par` rnf smalls 


qsort2 :: (Ord a, NFData a) => [a] -> [a]
qsort2 [] = []
qsort2 (x:xs) 
  = -- length smalls `par` length bigs `pseq` 
            combine ([smalls, bigs] `using` parList rnf) 
  where smalls = qsort [y | y<-xs, y <=x] 
        bigs   = qsort [y | y<-xs, y > x]
        combine (xs:ys:_) = xs ++ x:ys
