{-# LANGUAGE CPP                   #-}
{-# LANGUAGE MagicHash             #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE UnboxedTuples         #-}
module Numeric.DataFrame.Internal.Array.Family.DoubleX4 (DoubleX4 (..)) where


import           GHC.Base
import           Numeric.DataFrame.Internal.Array.Class
import           Numeric.DataFrame.Internal.Array.PrimOps
import           Numeric.PrimBytes


data DoubleX4 = DoubleX4# Double# Double# Double# Double#


instance Bounded DoubleX4 where
    maxBound = case inftyD of D# x -> DoubleX4# x x x x
    minBound = case negate inftyD of D# x -> DoubleX4# x x x x


instance Show DoubleX4 where
    show (DoubleX4# a1 a2 a3 a4)
      =  "{ " ++ show (D# a1)
      ++ ", " ++ show (D# a2)
      ++ ", " ++ show (D# a3)
      ++ ", " ++ show (D# a4)
      ++ " }"



instance Eq DoubleX4 where

    DoubleX4# a1 a2 a3 a4 == DoubleX4# b1 b2 b3 b4 =
      isTrue#
      (       (a1 ==## b1)
      `andI#` (a2 ==## b2)
      `andI#` (a3 ==## b3)
      `andI#` (a4 ==## b4)
      )
    {-# INLINE (==) #-}

    DoubleX4# a1 a2 a3 a4 /= DoubleX4# b1 b2 b3 b4 =
      isTrue#
      (      (a1 /=## b1)
      `orI#` (a2 /=## b2)
      `orI#` (a3 /=## b3)
      `orI#` (a4 /=## b4)
      )
    {-# INLINE (/=) #-}



-- | Implement partial ordering for `>`, `<`, `>=`, `<=`
--           and lexicographical ordering for `compare`
instance Ord DoubleX4 where
    DoubleX4# a1 a2 a3 a4 > DoubleX4# b1 b2 b3 b4 =
      isTrue#
      (       (a1 >## b1)
      `andI#` (a2 >## b2)
      `andI#` (a3 >## b3)
      `andI#` (a4 >## b4)
      )
    {-# INLINE (>) #-}

    DoubleX4# a1 a2 a3 a4 < DoubleX4# b1 b2 b3 b4 =
      isTrue#
      (       (a1 <## b1)
      `andI#` (a2 <## b2)
      `andI#` (a3 <## b3)
      `andI#` (a4 <## b4)
      )
    {-# INLINE (<) #-}

    DoubleX4# a1 a2 a3 a4 >= DoubleX4# b1 b2 b3 b4 =
      isTrue#
      (       (a1 >=## b1)
      `andI#` (a2 >=## b2)
      `andI#` (a3 >=## b3)
      `andI#` (a4 >=## b4)
      )
    {-# INLINE (>=) #-}

    DoubleX4# a1 a2 a3 a4 <= DoubleX4# b1 b2 b3 b4 =
      isTrue#
      (       (a1 <=## b1)
      `andI#` (a2 <=## b2)
      `andI#` (a3 <=## b3)
      `andI#` (a4 <=## b4)
      )
    {-# INLINE (<=) #-}

    -- | Compare lexicographically
    compare (DoubleX4# a1 a2 a3 a4) (DoubleX4# b1 b2 b3 b4)
      | isTrue# (a1 >## b1) = GT
      | isTrue# (a1 <## b1) = LT
      | isTrue# (a2 >## b2) = GT
      | isTrue# (a2 <## b2) = LT
      | isTrue# (a3 >## b3) = GT
      | isTrue# (a3 <## b3) = LT
      | isTrue# (a4 >## b4) = GT
      | isTrue# (a4 <## b4) = LT
      | otherwise = EQ
    {-# INLINE compare #-}

    -- | Element-wise minimum
    min (DoubleX4# a1 a2 a3 a4) (DoubleX4# b1 b2 b3 b4) = DoubleX4#
      (if isTrue# (a1 >## b1) then b1 else a1)
      (if isTrue# (a2 >## b2) then b2 else a2)
      (if isTrue# (a3 >## b3) then b3 else a3)
      (if isTrue# (a4 >## b4) then b4 else a4)
    {-# INLINE min #-}

    -- | Element-wise maximum
    max (DoubleX4# a1 a2 a3 a4) (DoubleX4# b1 b2 b3 b4) = DoubleX4#
      (if isTrue# (a1 >## b1) then a1 else b1)
      (if isTrue# (a2 >## b2) then a2 else b2)
      (if isTrue# (a3 >## b3) then a3 else b3)
      (if isTrue# (a4 >## b4) then a4 else b4)
    {-# INLINE max #-}



-- | element-wise operations for vectors
instance Num DoubleX4 where

    DoubleX4# a1 a2 a3 a4 + DoubleX4# b1 b2 b3 b4
      = DoubleX4# ((+##) a1 b1) ((+##) a2 b2) ((+##) a3 b3) ((+##) a4 b4)
    {-# INLINE (+) #-}

    DoubleX4# a1 a2 a3 a4 - DoubleX4# b1 b2 b3 b4
      = DoubleX4# ((-##) a1 b1) ((-##) a2 b2) ((-##) a3 b3) ((-##) a4 b4)
    {-# INLINE (-) #-}

    DoubleX4# a1 a2 a3 a4 * DoubleX4# b1 b2 b3 b4
      = DoubleX4# ((*##) a1 b1) ((*##) a2 b2) ((*##) a3 b3) ((*##) a4 b4)
    {-# INLINE (*) #-}

    negate (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (negateDouble# a1) (negateDouble# a2) (negateDouble# a3) (negateDouble# a4)
    {-# INLINE negate #-}

    abs (DoubleX4# a1 a2 a3 a4)
      = DoubleX4#
      (if isTrue# (a1 >=## 0.0##) then a1 else negateDouble# a1)
      (if isTrue# (a2 >=## 0.0##) then a2 else negateDouble# a2)
      (if isTrue# (a3 >=## 0.0##) then a3 else negateDouble# a3)
      (if isTrue# (a4 >=## 0.0##) then a4 else negateDouble# a4)
    {-# INLINE abs #-}

    signum (DoubleX4# a1 a2 a3 a4)
      = DoubleX4# (if isTrue# (a1 >## 0.0##)
                  then 1.0##
                  else if isTrue# (a1 <## 0.0##) then -1.0## else 0.0## )
                 (if isTrue# (a2 >## 0.0##)
                  then 1.0##
                  else if isTrue# (a2 <## 0.0##) then -1.0## else 0.0## )
                 (if isTrue# (a3 >## 0.0##)
                  then 1.0##
                  else if isTrue# (a3 <## 0.0##) then -1.0## else 0.0## )
                 (if isTrue# (a4 >## 0.0##)
                  then 1.0##
                  else if isTrue# (a4 <## 0.0##) then -1.0## else 0.0## )
    {-# INLINE signum #-}

    fromInteger n = case fromInteger n of D# x -> DoubleX4# x x x x
    {-# INLINE fromInteger #-}



instance Fractional DoubleX4 where

    DoubleX4# a1 a2 a3 a4 / DoubleX4# b1 b2 b3 b4 = DoubleX4#
      ((/##) a1 b1) ((/##) a2 b2) ((/##) a3 b3) ((/##) a4 b4)
    {-# INLINE (/) #-}

    recip (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      ((/##) 1.0## a1) ((/##) 1.0## a2) ((/##) 1.0## a3) ((/##) 1.0## a4)
    {-# INLINE recip #-}

    fromRational r = case fromRational r of D# x -> DoubleX4# x x x x
    {-# INLINE fromRational #-}



instance Floating DoubleX4 where

    pi = DoubleX4#
      3.141592653589793238##
      3.141592653589793238##
      3.141592653589793238##
      3.141592653589793238##
    {-# INLINE pi #-}

    exp (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (expDouble# a1) (expDouble# a2) (expDouble# a3) (expDouble# a4)
    {-# INLINE exp #-}

    log (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (logDouble# a1) (logDouble# a2) (logDouble# a3) (logDouble# a4)
    {-# INLINE log #-}

    sqrt (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (sqrtDouble# a1) (sqrtDouble# a2) (sqrtDouble# a3) (sqrtDouble# a4)
    {-# INLINE sqrt #-}

    sin (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (sinDouble# a1) (sinDouble# a2) (sinDouble# a3) (sinDouble# a4)
    {-# INLINE sin #-}

    cos (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (cosDouble# a1) (cosDouble# a2) (cosDouble# a3) (cosDouble# a4)
    {-# INLINE cos #-}

    tan (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (tanDouble# a1) (tanDouble# a2) (tanDouble# a3) (tanDouble# a4)
    {-# INLINE tan #-}

    asin (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (asinDouble# a1) (asinDouble# a2) (asinDouble# a3) (asinDouble# a4)
    {-# INLINE asin #-}

    acos (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (acosDouble# a1) (acosDouble# a2) (acosDouble# a3) (acosDouble# a4)
    {-# INLINE acos #-}

    atan (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (atanDouble# a1) (atanDouble# a2) (atanDouble# a3) (atanDouble# a4)
    {-# INLINE atan #-}

    sinh (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (sinhDouble# a1) (sinhDouble# a2) (sinhDouble# a3) (sinhDouble# a4)
    {-# INLINE sinh #-}

    cosh (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (coshDouble# a1) (coshDouble# a2) (coshDouble# a3) (coshDouble# a4)
    {-# INLINE cosh #-}

    tanh (DoubleX4# a1 a2 a3 a4) = DoubleX4#
      (tanhDouble# a1) (tanhDouble# a2) (tanhDouble# a3) (tanhDouble# a4)
    {-# INLINE tanh #-}

    DoubleX4# a1 a2 a3 a4 ** DoubleX4# b1 b2 b3 b4 = DoubleX4#
      ((**##) a1 b1) ((**##) a2 b2) ((**##) a3 b3) ((**##) a4 b4)
    {-# INLINE (**) #-}

    logBase x y         =  log y / log x
    {-# INLINE logBase #-}

    asinh x = log (x + sqrt (1.0+x*x))
    {-# INLINE asinh #-}

    acosh x = log (x + (x+1.0) * sqrt ((x-1.0)/(x+1.0)))
    {-# INLINE acosh #-}

    atanh x = 0.5 * log ((1.0+x) / (1.0-x))
    {-# INLINE atanh #-}

-- offset in bytes is S times bigger than offset in prim elements,
-- when S is power of two, this is equal to shift
#define BOFF_TO_PRIMOFF(off) uncheckedIShiftRL# off 3#
#define ELEM_N 4

instance PrimBytes DoubleX4 where

    getBytes (DoubleX4# a1 a2 a3 a4) = case runRW#
       ( \s0 -> case newByteArray# (byteSize @DoubleX4 undefined) s0 of
           (# s1, marr #) -> case writeDoubleArray# marr 0# a1 s1 of
             s2 -> case writeDoubleArray# marr 1# a2 s2 of
               s3 -> case writeDoubleArray# marr 2# a3 s3 of
                 s4 -> case writeDoubleArray# marr 3# a4 s4 of
                   s5 -> unsafeFreezeByteArray# marr s5
       ) of (# _, a #) -> a
    {-# INLINE getBytes #-}

    fromBytes off arr
      | i <- BOFF_TO_PRIMOFF(off)
      = DoubleX4#
      (indexDoubleArray# arr i)
      (indexDoubleArray# arr (i +# 1#))
      (indexDoubleArray# arr (i +# 2#))
      (indexDoubleArray# arr (i +# 3#))
    {-# INLINE fromBytes #-}

    readBytes mba off s0
      | i <- BOFF_TO_PRIMOFF(off)
      = case readDoubleArray# mba i s0 of
      (# s1, a1 #) -> case readDoubleArray# mba (i +# 1#) s1 of
        (# s2, a2 #) -> case readDoubleArray# mba (i +# 2#) s2 of
          (# s3, a3 #) -> case readDoubleArray# mba (i +# 3#) s3 of
            (# s4, a4 #) -> (# s4, DoubleX4# a1 a2 a3 a4 #)
    {-# INLINE readBytes #-}

    writeBytes mba off (DoubleX4# a1 a2 a3 a4) s
      | i <- BOFF_TO_PRIMOFF(off)
      = writeDoubleArray# mba (i +# 3#) a4
      ( writeDoubleArray# mba (i +# 2#) a3
      ( writeDoubleArray# mba (i +# 1#) a2
      ( writeDoubleArray# mba  i        a1 s )))
    {-# INLINE writeBytes #-}

    readAddr addr s0
      = case readDoubleOffAddr# addr 0# s0 of
      (# s1, a1 #) -> case readDoubleOffAddr# addr 1# s1 of
        (# s2, a2 #) -> case readDoubleOffAddr# addr 2# s2 of
          (# s3, a3 #) -> case readDoubleOffAddr# addr 3# s3 of
            (# s4, a4 #) -> (# s4, DoubleX4# a1 a2 a3 a4 #)
    {-# INLINE readAddr #-}

    writeAddr (DoubleX4# a1 a2 a3 a4) addr s
      = writeDoubleOffAddr# addr 3# a4
      ( writeDoubleOffAddr# addr 2# a3
      ( writeDoubleOffAddr# addr 1# a2
      ( writeDoubleOffAddr# addr 0# a1 s )))
    {-# INLINE writeAddr #-}

    byteSize _ = byteSize @Double undefined *# ELEM_N#
    {-# INLINE byteSize #-}

    byteAlign _ = byteAlign @Double undefined
    {-# INLINE byteAlign #-}

    byteOffset _ = 0#
    {-# INLINE byteOffset #-}

    indexArray ba off
      | i <- off *# ELEM_N#
      = DoubleX4#
      (indexDoubleArray# ba i)
      (indexDoubleArray# ba (i +# 1#))
      (indexDoubleArray# ba (i +# 2#))
      (indexDoubleArray# ba (i +# 3#))
    {-# INLINE indexArray #-}

    readArray mba off s0
      | i <- off *# ELEM_N#
      = case readDoubleArray# mba i s0 of
      (# s1, a1 #) -> case readDoubleArray# mba (i +# 1#) s1 of
        (# s2, a2 #) -> case readDoubleArray# mba (i +# 2#) s2 of
          (# s3, a3 #) -> case readDoubleArray# mba (i +# 3#) s3 of
            (# s4, a4 #) -> (# s4, DoubleX4# a1 a2 a3 a4 #)
    {-# INLINE readArray #-}

    writeArray mba off (DoubleX4# a1 a2 a3 a4) s
      | i <- off *# ELEM_N#
      = writeDoubleArray# mba (i +# 3#) a4
      ( writeDoubleArray# mba (i +# 2#) a3
      ( writeDoubleArray# mba (i +# 1#) a2
      ( writeDoubleArray# mba  i        a1 s )))
    {-# INLINE writeArray #-}


instance PrimArray Double DoubleX4 where

    broadcast (D# x) = DoubleX4# x x x x
    {-# INLINE broadcast #-}

    ix# 0# (DoubleX4# a1 _ _ _) = D# a1
    ix# 1# (DoubleX4# _ a2 _ _) = D# a2
    ix# 2# (DoubleX4# _ _ a3 _) = D# a3
    ix# 3# (DoubleX4# _ _ _ a4) = D# a4
    ix# _   _                   = undefined
    {-# INLINE ix# #-}

    gen# _ f s0 = case f s0 of
      (# s1, D# a1 #) -> case f s1 of
        (# s2, D# a2 #) -> case f s2 of
          (# s3, D# a3 #) -> case f s3 of
            (# s4, D# a4 #) -> (# s4, DoubleX4# a1 a2 a3 a4 #)


    upd# _ 0# (D# q) (DoubleX4# _ y z w) = DoubleX4# q y z w
    upd# _ 1# (D# q) (DoubleX4# x _ z w) = DoubleX4# x q z w
    upd# _ 2# (D# q) (DoubleX4# x y _ w) = DoubleX4# x y q w
    upd# _ 3# (D# q) (DoubleX4# x y z _) = DoubleX4# x y z q
    upd# _ _ _ x                         = x
    {-# INLINE upd# #-}

    elemOffset _ = 0#
    {-# INLINE elemOffset #-}

    elemSize0 _  = ELEM_N#
    {-# INLINE elemSize0 #-}

    fromElems off _ ba = DoubleX4#
      (indexDoubleArray# ba off)
      (indexDoubleArray# ba (off +# 1#))
      (indexDoubleArray# ba (off +# 2#))
      (indexDoubleArray# ba (off +# 3#))
    {-# INLINE fromElems #-}