{-# LANGUAGE TypeOperators #-}
module Points2D.Generate 
	( genPointsUniform
	, genPointsUniformWithSeed
	, genPointsDisc
	, genPointsCombo
	, pointsPArrayOfUArray )
where
import Points2D.Types
import Randomish
import qualified Data.Array.Parallel.Unlifted 	    as U
import qualified Data.Array.Parallel.Prelude 	    as P
import qualified Data.Array.Parallel.PArray         as P
import Data.Array.Parallel.PArray		    (PArray)
import Control.Exception

-- 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.
seed :: Int
seed 	= 42742

-- | Some uniformly distributed points
genPointsUniform 
	:: Int			-- ^ number of points
	-> Double		-- ^ minimum coordinate
	-> Double		-- ^ maximum coordinate
        -> U.Array (Double, Double)

genPointsUniform n pointMin pointMax
 = let  pts             = randomishDoubles (n*2) pointMin pointMax seed
        xs              = U.extract pts 0 n
        ys              = U.extract pts n n
   in   U.zip xs ys


-- | Some uniformly distributed points
genPointsUniformWithSeed
	:: Int			-- ^ seed
	-> Int			-- ^ number of points
	-> Double		-- ^ minimum coordinate
	-> Double		-- ^ maximum coordinate
        -> U.Array (Double, Double)

genPointsUniformWithSeed seed' n pointMin pointMax
 = let  pts             = randomishDoubles (n*2) pointMin pointMax seed'
        xs              = U.extract pts 0 n
        ys              = U.extract pts n n
   in   U.zip xs ys


-- | Some points distributed as a disc
genPointsDisc
	:: Int			-- ^ number of points
	-> (Double, Double) 	-- ^ center of disc
	-> Double 		-- ^ radius of disc
        -> U.Array (Double, Double)

genPointsDisc n (originX, originY) radiusMax
 = let	radius = randomishDoubles n 0     radiusMax seed
        angle  = randomishDoubles n (-pi) pi        (seed + 1234)

	makeXY r a	
	 	= ( originX + r * cos a
		  , originY + r * sin a)	

    in	originX `seq` originY `seq` U.zipWith makeXY radius angle


-- | A point cloud with areas of high an low density
genPointsCombo 
	:: Int 			-- ^ number of points
        -> U.Array (Double, Double)

genPointsCombo n
 	=  genPointsDisc    (n `div` 5) (250, 250) 200
	U.+:+ genPointsDisc (n `div` 5) (100, 100) 80 
	U.+:+ genPointsDisc (n `div` 5) (150, 300) 30 
	U.+:+ genPointsDisc (n `div` 5) (500, 120) 30 
	U.+:+ genPointsDisc (n `div` 5) (300, 200) 150


-- | Convert a list of points to a PArray
pointsPArrayOfUArray 
	:: U.Array (Double, Double)
	-> IO (PArray Point)

pointsPArrayOfUArray ps
  = do
      let pts = makePointsPA 
			(P.fromUArrPA' (U.fsts ps))
 			(P.fromUArrPA' (U.snds ps))
      evaluate $ P.nf pts
      return pts

