{-# OPTIONS_GHC -fvectorise #-}

module Data.Array.Parallel.Prelude.Int 
        ( Int
          
          -- * Ord
        , (==), (/=), (<), (<=), (>), (>=), min, max
        , maximumP,  minimumP
        , maxIndexP, minIndexP
     
          -- * Num
        , (+), (-), (*)
        , negate, abs
        , sumP, productP
        
          -- * Integral
        , div, mod, sqrt
        
          -- * Enum
        , enumFromToP)
where
-- Primitives needed by the vectoriser.
import Data.Array.Parallel.Prim                         ()      
import Data.Array.Parallel.Prelude.Base                 (Bool, Int, Eq, Ord, Num)
import Data.Array.Parallel.PArr
import Data.Array.Parallel.PArray
import Data.Array.Parallel.Lifted                       ((:->)(..))
import qualified Data.Array.Parallel.Lifted             as L
import qualified Data.Array.Parallel.PArray.Scalar      as SC
import qualified Prelude as P
        

{-# VECTORISE SCALAR instance Eq  Int #-}
{-# VECTORISE SCALAR instance Ord Int #-}
{-# VECTORISE SCALAR instance Num Int #-}


infixl 7 *
infixl 6 +, -
infix  4 ==, /=, <, <=, >, >=
infixl 7 `div`, `mod`

-- Ord ------------------------------------------------------------------------
(==), (/=), (<), (<=), (>), (>=) :: Int -> Int -> Bool

(==) = (P.==)
{-# VECTORISE SCALAR (==) #-}

(/=) = (P./=)
{-# VECTORISE SCALAR (/=) #-}

(<=) = (P.<=)
{-# VECTORISE SCALAR (<=) #-}

(<)  = (P.<)
{-# VECTORISE SCALAR (<) #-}

(>=) = (P.>=)
{-# VECTORISE SCALAR (>=) #-}

(>)  = (P.>)
{-# VECTORISE SCALAR (>) #-}


-- min/max ----------------------------
min, max :: Int -> Int -> Int

min = P.min
{-# VECTORISE SCALAR min #-}

max = P.max
{-# VECTORISE SCALAR max #-}


-- minimum/maximum --------------------
minimumP, maximumP :: PArr Int -> Int

minimumP arr    = headPArr arr
{-# NOINLINE  minimumP #-}
{-# VECTORISE minimumP = minimumPP #-}

maximumP arr    = headPArr arr
{-# NOINLINE  maximumP #-}
{-# VECTORISE maximumP = maximumPP #-}

minimumPP, maximumPP :: PArray Int :-> Int
minimumPP      = L.closure1' (SC.fold1 P.min) (SC.fold1s P.min)
{-# INLINE      minimumPP #-}
{-# NOVECTORISE minimumPP #-}

maximumPP      = L.closure1' (SC.fold1 P.max) (SC.fold1s P.max)
{-# INLINE      maximumPP #-}
{-# NOVECTORISE maximumPP #-}


-- minIndex/maxIndex ------------------
minIndexP :: PArr Int -> Int
minIndexP !_    = 0 
{-# NOINLINE  minIndexP #-}
{-# VECTORISE minIndexP = minIndexPP #-}

minIndexPP :: PArray Int :-> Int
minIndexPP      = L.closure1' (SC.fold1Index min') (SC.fold1sIndex min')
{-# INLINE      minIndexPP #-}
{-# NOVECTORISE minIndexPP #-}

min' :: P.Ord b => (a, b) -> (a, b) -> (a, b)
min' (i,x) (j,y) | x P.<= y    = (i,x)
                 | P.otherwise = (j,y)
{-# NOVECTORISE min' #-}


maxIndexP :: PArr Int -> Int
maxIndexP _     = 0
{-# NOINLINE  maxIndexP #-}
{-# VECTORISE maxIndexP = maxIndexPP #-}

maxIndexPP :: PArray Int :-> Int
maxIndexPP      = L.closure1' (SC.fold1Index max') (SC.fold1sIndex max')
{-# INLINE      maxIndexPP #-}
{-# NOVECTORISE maxIndexPP #-}

max' :: P.Ord b => (a, b) -> (a, b) -> (a, b)
max' (i,x) (j,y) | x P.>= y    = (i,x)
                 | P.otherwise = (j,y)
{-# NOVECTORISE max' #-}


-- Num ------------------------------------------------------------------------
(+), (-), (*) :: Int -> Int -> Int

(+) = (P.+)
{-# VECTORISE SCALAR (+) #-}

(-) = (P.-)
{-# VECTORISE SCALAR (-) #-}

(*) = (P.*)
{-# VECTORISE SCALAR (*) #-}


-- negate/abs -------------------------
negate, abs :: Int -> Int

negate  = P.negate
{-# VECTORISE SCALAR negate #-}

abs     = P.abs
{-# VECTORISE SCALAR abs #-}


-- sum/product ------------------------
sumP, productP :: PArr Int -> Int

sumP arr        = headPArr arr
{-# NOINLINE  sumP #-}
{-# VECTORISE sumP      = sumPP #-}

productP arr    = headPArr arr
{-# NOINLINE  productP #-}
{-# VECTORISE productP  = productPP #-}

sumPP, productPP :: PArray Int :-> Int
sumPP          = L.closure1' (SC.fold (+) 0) (SC.folds (+) 0)
{-# INLINE      sumPP #-}
{-# NOVECTORISE sumPP #-}

productPP      = L.closure1' (SC.fold (*) 1) (SC.folds (*) 1)
{-# INLINE      productPP #-}
{-# NOVECTORISE productPP #-}


-- Integral -------------------------------------------------------------------
div, mod :: Int -> Int -> Int

div = P.div
{-# VECTORISE SCALAR div #-}

mod = P.mod
{-# VECTORISE SCALAR mod #-}


sqrt :: Int -> Int 
sqrt n = P.floor (P.sqrt (P.fromIntegral n) :: P.Double)
{-# VECTORISE SCALAR sqrt #-}


-- Enum -----------------------------------------------------------------------
enumFromToP :: Int -> Int -> PArr Int
enumFromToP !_ !_       = emptyPArr
{-# NOINLINE  enumFromToP #-}
{-# VECTORISE enumFromToP = enumFromToPP #-}

enumFromToPP :: Int :-> Int :-> PArray Int
enumFromToPP    = L.closure2' SC.enumFromTo SC.enumFromTol
{-# INLINE      enumFromToPP #-}
{-# NOVECTORISE enumFromToPP #-}