```{-# OPTIONS_GHC -fvectorise #-}

module Data.Array.Parallel.Prelude.Double
( Double

-- * Ord
, (==), (/=), (<), (<=), (>), (>=), min, max
, maximumP,  minimumP
, maxIndexP, minIndexP

-- * Num
, (+), (-), (*), (/)
, negate, abs
, sumP, productP

-- * Floating
, pi
, sqrt
, exp, (**)
, log, logBase
,  sin,   tan,   cos
, asin,  atan,  acos
,  sinh,  tanh,  cosh
, asinh, atanh, acosh

-- * RealFrac
, fromInt)
where
-- Primitives needed by the vectoriser.
import Data.Array.Parallel.Prim                 ()
import Data.Array.Parallel.Prelude.Base         (Bool, Int, Double, 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  Double #-}
{-# VECTORISE SCALAR instance Ord Double #-}
{-# VECTORISE SCALAR instance Num Double #-}

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

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

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

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

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

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

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

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

-- min/max ----------------------------
min, max :: Double -> Double -> Double

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

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

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

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

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

minimumPP, maximumPP :: PArray Double :-> Double
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 Double -> Int
minIndexP !_    = 0
{-# NOINLINE  minIndexP #-}
{-# VECTORISE minIndexP = minIndexPP #-}

minIndexPP :: PArray Double :-> 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 Double -> Int
maxIndexP _     = 0
{-# NOINLINE  maxIndexP #-}
{-# VECTORISE maxIndexP = maxIndexPP #-}

maxIndexPP :: PArray Double :-> 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 ---------------------------------------------------------------------
(+), (-), (*), (/) :: Double -> Double -> Double

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

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

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

mulPP :: Double :-> Double :-> Double
mulPP   = L.closure2' (P.*) (SC.zipWith (P.*))
{-# INLINE mulPP #-}
{-# NOVECTORISE mulPP #-}

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

-- negate/abs -------------------------
negate, abs :: Double -> Double

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

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

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

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

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

sumPP, productPP :: PArray Double :-> Double
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 #-}

-- Floating -------------------------------------------------------------------
pi :: Double
pi = P.pi
{-# NOVECTORISE pi #-}

sqrt,    exp,  log,
sin,   tan,   cos,
asin,  atan,  acos,
sinh,  tanh,  cosh,
asinh, atanh, acosh  :: Double -> Double

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

(**), logBase :: Double -> Double -> Double

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

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

-- RealFrac -------------------------------------------------------------------
fromInt :: Int -> Double
fromInt         = P.fromIntegral
{-# VECTORISE SCALAR fromInt #-}
```