{-# LANGUAGE MagicHash, UnboxedTuples #-} -- | -- Copyright : Anders Claesson 2013 -- Maintainer : Anders Claesson -- -- Convenience functions for dealing with arrays of 'CLong's. module Sym.Internal.CLongArray ( -- * Data type CLongArray -- * Conversions , fromList , toList , slice , unsafeSlice -- * Accessors , size , at , unsafeAt , elemIndices -- * Map , imap , izipWith -- * Low level functions , unsafeNew , unsafeWith ) where import Data.Ord import Sym.Internal.Size import Foreign import Foreign.C.Types import GHC.Base infixl 9 `at` infixl 9 `unsafeAt` inlinePerformIO :: IO a -> a inlinePerformIO (IO m) = case m realWorld# of (# _, r #) -> r {-# INLINE inlinePerformIO #-} -- Data type -- --------- -- | An array of 'CLong's data CLongArray = CArr {-# UNPACK #-} !(ForeignPtr CLong) -- elements {-# UNPACK #-} !Int -- size instance Show CLongArray where show w = "fromList " ++ show (toList w) instance Eq CLongArray where u == v = toList u == toList v instance Ord CLongArray where compare u v = case comparing size u v of EQ -> comparing toList u v x -> x instance Size CLongArray where size (CArr _ n) = n {-# INLINE size #-} -- Conversions -- ----------- -- | Construct an array from a list of elements. fromList :: [Int] -> CLongArray fromList xs = CArr p (length xs) where p = inlinePerformIO $ newForeignPtr finalizerFree =<< newArray (map fromIntegral xs) {-# INLINE fromList #-} -- | The list of elements. toList :: CLongArray -> [Int] toList w = map fromIntegral . inlinePerformIO . unsafeWith w $ peekArray (size w) {-# INLINE toList #-} -- | Slice a 'CLongArray' into contiguous segments of the given -- sizes. Each segment size must be positive and they must sum to the -- size of the array. slice :: [Int] -> CLongArray -> [CLongArray] slice ks w | any (<=0) ks = error "Sym.Internal.CLongArray.slice: zero or negative parts" | sum ks /= size w = error "Sym.Internal.CLongArray.slice: parts doesn't sum to size of array" | otherwise = unsafeSlice ks w -- | Like 'slice' but without range checking. unsafeSlice :: [Int] -> CLongArray -> [CLongArray] unsafeSlice parts w = inlinePerformIO . unsafeWith w $ go parts where go [] _ = return [] go (k:ks) p = do vs <- go ks (advancePtr p k) v <- unsafeNew k $ \q -> copyArray q p k return (v:vs) -- Accessors -- --------- -- | @w \`at\` i@ is the value of @w@ at @i@, where @i@ is in @[0..size w-1]@. at :: CLongArray -> Int -> Int at w i = let n = size w in if i < 0 || i >= n then error $ "Sym.Internal.CLongArray.at: " ++ show i ++ " not in [0.." ++ show (n-1) ++ "]" else unsafeAt w i {-# INLINE at #-} -- | Like 'at' but without range checking. unsafeAt :: CLongArray -> Int -> Int unsafeAt w = fromIntegral . inlinePerformIO . unsafeWith w . flip peekElemOff {-# INLINE unsafeAt #-} -- | The indices of all elements equal to the query element, in -- ascending order. elemIndices :: CLong -> CLongArray -> [Int] elemIndices x w = inlinePerformIO $ unsafeWith w (go 0) where n = size w go i p | i >= n = return [] | otherwise = do y <- peek p ([ i | y == x ] ++) `fmap` go (i+1) (advancePtr p 1) -- Map and Zip -- ----------- -- | Apply a function to every element of an array and its index. imap :: (Int -> CLong -> CLong) -> CLongArray -> CLongArray imap f w = inlinePerformIO . unsafeWith w $ \p -> unsafeNew n (go 0 p) where n = size w go i p q | i >= n = return () | otherwise = do x <- peek p poke q (f i x) go (i+1) (advancePtr p 1) (advancePtr q 1) -- | Apply a function to corresponding pairs of elements and their (shared) index. izipWith :: (Int -> CLong -> CLong -> CLong) -> CLongArray -> CLongArray -> CLongArray izipWith f u v = inlinePerformIO . unsafeWith u $ \p -> unsafeWith v $ \q -> unsafeNew n (go 0 p q) where n = min (size u) (size v) go i p q r | i >= n = return () | otherwise = do x <- peek p y <- peek q poke r (f i x y) go (i+1) (advancePtr p 1) (advancePtr q 1) (advancePtr r 1) -- Low level functions -- ------------------- -- | Create a new array of the given size that is initialized through -- an IO action. unsafeNew :: Int -> (Ptr CLong -> IO ()) -> IO CLongArray unsafeNew n act = do q <- newForeignPtr finalizerFree =<< mallocArray n withForeignPtr q act return $ CArr q n {-# INLINE unsafeNew #-} -- | Pass a pointer to the array to an IO action; the array may not be -- modified through the pointer. unsafeWith :: CLongArray -> (Ptr CLong -> IO a) -> IO a unsafeWith (CArr p _) = withForeignPtr p {-# INLINE unsafeWith #-}