import qualified Types as QH
import QuickHullVect (quickhull)

import Data.Array.Parallel.Unlifted as U
import Data.Array.Parallel.Prelude
import qualified Data.Array.Parallel.Prelude.Double as D
import Data.Array.Parallel.PArray as P
import Data.Array.Parallel
import Data.Array.Parallel.Prelude ( fromUArrPA_2' )

import Prelude as Prel
import qualified System.Random as R
import System.IO
import Control.Exception (evaluate)

import Bench.Benchmark
import Bench.Options
import TestData


-- Random points generation
--

-- IMPORTANT: We use the same seed with the same random generator in all
--            quickhull codes.  The asymptotic work complexity of quickhull
--            is between O (N) and O (N^2) depending on the input.
--            To compare benchmark results, they always need to use the same
--            input.

generatePoints :: Int -> Maybe String -> IO (Point (PArray QH.Point))
generatePoints n s
  = do
      let rg = R.mkStdGen 42742     -- always use the same seed
          ds = U.randomRs (n*2) (-100, 100) rg
          xs = U.extract ds 0 n
          ys = U.extract ds n n
      save s xs ys
      convert xs ys
  where
    save Nothing  _ _ = return ()
    save (Just s) xs ys = do
                            h <- openBinaryFile s WriteMode
                            U.hPut h (U.zip xs ys)
                            hClose h

{-
          ps  = toPairs (take (2*n) (R.randomRs (-100, 100) rg))
          pts = QH.points (P.fromList (Prel.map fst ps))
                          (P.fromList (Prel.map snd ps))
      evaluate $ nf pts -- force pts
      return $ ("N = " ++ show n) `mkPoint` pts
  where
    toPairs []        = []
    toPairs (x:y:pts) = (x, y) : toPairs pts

    force pts = toUArrPA (QH.xsOf pts) U.!: 0 D.+ 
                toUArrPA (QH.ysOf pts) U.!: 0
-}

loadPoints :: String -> IO (Point (PArray QH.Point))
loadPoints file
  = do
      h <- openBinaryFile file ReadMode
      upts <- U.hGet h
      hClose h
      convert (U.fsts upts) (U.snds upts)
{-
      let pts = QH.points (fromUArrPA' (U.fsts upts)) (fromUArrPA' (U.snds upts))
      evaluate $ nf pts
      return $ ("N = " ++ show (U.length upts)) `mkPoint` pts
-}

convert :: U.Array Double -> U.Array Double -> IO (Point (PArray QH.Point))
convert xs ys
  = do
      let pts = QH.points (fromUArrPA' xs) (fromUArrPA' ys)
      evaluate $ nf pts
      return $ ("N = " ++ show (U.length xs)) `mkPoint` pts



-- Main
-- ----

{- Simple test
pts = points (P.fromList (Prel.map fst coords))
             (P.fromList (Prel.map snd coords))
  where
    coords = [(3,3),(2,7),(0,0),(8,5), (4,6),(5,3),(9,6),(10,0)]

result = Prel.zip (U.toList (toUArrPA (xsOf ps)))
                  (U.toList (toUArrPA (ysOf ps)))
  where
    ps = quickhull pts

main = print result
 -}

main = ndpMain "Quick hull"
               "[OPTION] ... SIZES ..."
               run [] ()

run opts () [] = failWith ["No sizes or input files specified"]
run opts () args =
  do
    benchmark opts quickhull
        (gen args)
        nf
        (\ps -> "Result length = " ++ show (P.length ps))
    return ()
  where
    gen [] = []
    gen (arg:args)
      = case reads arg of
          [(n,"")] -> case args of
                        ("w" : s : args') -> generatePoints n (Just s) : gen args'
                        _                  -> generatePoints n Nothing  : gen args
          _        -> loadPoints arg : gen args

{-
  case Prel.map read sizes of
    []  -> failWith ["No sizes or input files specified"]
    szs -> do
             benchmark opts runQuickhull
                (Prel.map generatePoints szs)
                (`seq` ()) (("Result length = " ++) . show)
             return ()
  where
    runQuickhull :: PArray QH.Point -> Int
    runQuickhull pts = let result = quickhull pts
                           resxs  = toUArrPA (QH.xsOf result)
                       in
                       resxs U.!: 0 `seq` U.length resxs
-}
        

