import System.Random (StdGen, getStdGen, randoms)
import Data.Array.Repa
import Data.Array.Repa.Operators.Reduction
import Data.Array.Repa.Eval
import Control.Monad.Identity
import Criterion.Main
import Control.DeepSeq

main = do 
    input <- randomInts count `fmap` getStdGen
    let inputArray = fromList (Z:.count) input
    print $ buySellPar inputArray
  where count = 12500

{- main = do 
    input <- randomInts count `fmap` getStdGen
    let inputArray = fromList (Z:.count) input
    print $ buySellPar inputArray
  where count = 20000 -}

buySellPar :: Array U DIM1 Int -> (Int,Int,Int)
buySellPar prices = runIdentity $ do 
  let profits = createProfitMatrix prices
  bestPerBuyDay <- foldP maxByProfit (0,0,0) profits
  result <- foldP (flip maxByProfit) (0,0,0) bestPerBuyDay
  return $ result ! Z

buySellSeq :: Array U DIM1 Int -> (Int,Int,Int)
buySellSeq prices = result ! Z
  where
    result = foldS (flip maxByProfit) (0,0,0) $ foldS maxByProfit (0,0,0) $ createProfitMatrix prices

createProfitMatrix :: Array U DIM1 Int -> Array D DIM2 (Int, Int, Int)
createProfitMatrix prices = fromFunction (Z:.n:.n) f
  where
    Z:.n = extent prices
    f (Z:.i1:.i2) | i1 >= i2  = (0,0,0)
                  | otherwise = (i1,i2, (prices ! (Z:.i2)) - (prices ! (Z:.i1)) )

maxByProfit :: (Int, Int, Int) -> (Int, Int, Int) -> (Int, Int, Int)
maxByProfit t1@(_,_,p1) t2@(_,_,p2) | p1 >= p2  = t1
                                    | otherwise = t2

randomInts :: Int -> StdGen -> [Int]
randomInts k g = let result = take k (randoms g)
                 in force result `seq` result