brush-strokes-0.1.0.0: src/simd-interval/Math/Interval/Internal/SIMD/Internal.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE GHCForeignImportPrim #-}
{-# LANGUAGE UnliftedFFITypes #-}
module Math.Interval.Internal.SIMD.Internal
( π(π, MkI, PackI, inf, sup)
, scaleInterval
)
where
-- base
import Prelude hiding ( Num(..), Fractional(..), Floating(..) )
import qualified Prelude
import GHC.Exts
( Double(D#), Double#
, negateDouble#
, DoubleX2#
, packDoubleX2#, unpackDoubleX2#
, broadcastDoubleX2#
, plusDoubleX2#, timesDoubleX2#
, indexDoubleX2Array#
, readDoubleX2Array#
, writeDoubleX2Array#
, indexDoubleX2OffAddr#
, readDoubleX2OffAddr#
, writeDoubleX2OffAddr#
)
-- primitive
import Data.Primitive
-- rounded-hw
import Numeric.Rounded.Hardware
( Rounded(..) )
import Numeric.Rounded.Hardware.Internal
( intervalFromInteger, intervalFromRational )
-- ghc-prim
import GHC.Prim
( maxDouble#, shuffleDoubleX2# )
-- deepseq
import Control.DeepSeq
( NFData(..) )
--------------------------------------------------------------------------------
-- | A non-empty interval of real numbers (possibly unbounded).
data π = MkI DoubleX2#
-- Interval [lo..hi] represented as (-lo, hi)
instance NFData π where
rnf ( MkI !_ ) = ()
foreign import prim "in2_mul8"
mulI :: DoubleX2# -> DoubleX2# -> DoubleX2#
{-# COMPLETE PackI #-}
{-# INLINE PackI #-}
pattern PackI :: Double# -> Double# -> π
pattern PackI m_lo hi <- ( ( \ ( MkI x ) -> unpackDoubleX2# x ) -> (# m_lo, hi #) )
where
PackI m_lo hi = MkI ( packDoubleX2# (# m_lo, hi #) )
{-# COMPLETE π #-}
{-# INLINE π #-}
pattern π :: Double -> Double -> π
pattern π { inf, sup } <- ( ( \ ( PackI m_lo hi ) -> (# D# ( negateDouble# m_lo ), D# hi #) )
-> (# inf, sup #) )
where
π (D# lo) (D# hi) =
let m_lo = negateDouble# lo
in PackI m_lo hi
instance Prelude.Num π where
MkI x + MkI y = MkI ( x `plusDoubleX2#` y )
negate ( MkI x ) = MkI ( shuffleDoubleX2# x x (# 1#, 0# #) )
( MkI x ) * ( MkI y ) = MkI ( mulI x y )
-- This uses the C code from Lockless Inc.
-- TODO: compare with
-- "Fast and Correct SIMD Algorithms for Interval Arithmetic"
-- FrΓ©dΓ©ric Goualard, 2008
fromInteger i =
case intervalFromInteger i of
( Rounded lo, Rounded hi ) -> π lo hi
abs x@(PackI m_lo hi)
| D# m_lo <= 0
= PackI m_lo hi
| D# hi <= 0
= Prelude.negate x
| otherwise
= π 0 ( D# ( maxDouble# m_lo hi ) )
signum _ = error "No implementation of signum for intervals"
instance Prelude.Fractional π where
fromRational r =
case intervalFromRational r of
( Rounded lo, Rounded hi ) -> π lo hi
recip ( π lo hi )
#ifdef ASSERTS
| lo == 0
= π ( Prelude.recip hi ) ( 1 Prelude./ 0 )
| hi == 0
= π ( -1 Prelude./ 0 ) ( Prelude.recip lo )
| lo > 0 || hi < 0
#endif
= π ( Prelude.recip hi ) ( Prelude.recip lo )
#ifdef ASSERTS
| otherwise
= error "BAD interval recip; should use extendedRecip instead"
#endif
p / q = p Prelude.* Prelude.recip q
scaleInterval :: Double -> π -> π
scaleInterval s@(D# s#) i =
case compare s 0 of
LT -> case Prelude.negate i of { MkI x -> MkI ( broadcastDoubleX2# ( negateDouble# s# ) `timesDoubleX2#` x ) }
EQ -> π 0 0
GT -> case i of { MkI x -> MkI ( broadcastDoubleX2# s# `timesDoubleX2#` x ) }
--------------------------------------------------------------------------------
instance Prim π where
sizeOf# _ = 16#
alignment# _ = 16#
indexByteArray# arr# i# =
let v = indexDoubleX2Array# arr# i#
in MkI v
readByteArray# arr# i# s0 =
case readDoubleX2Array# arr# i# s0 of
(# s1, v #) -> (# s1, MkI v #)
writeByteArray# arr# i# (MkI v) s0 =
writeDoubleX2Array# arr# i# v s0
indexOffAddr# addr# i# =
let v = indexDoubleX2OffAddr# addr# i#
in MkI v
readOffAddr# addr# i# s0 =
case readDoubleX2OffAddr# addr# i# s0 of
(# s1, v #) -> (# s1, MkI v #)
writeOffAddr# addr# i# (MkI v) s0 =
writeDoubleX2OffAddr# addr# i# v s0