dph-prim-seq-0.6.0.1: Data/Array/Parallel/Unlifted/Sequential/USegd.hs
{-# LANGUAGE CPP #-}
{-# OPTIONS -fno-warn-orphans #-}
#include "fusion-phases.h"
-- | Segment Descriptors.
--
-- See "Data.Array.Parallel.Unlifted" for how this works.
--
module Data.Array.Parallel.Unlifted.Sequential.USegd
( -- * Types
USegd(..)
-- * Constructors
, mkUSegd, valid
, empty, singleton
, fromLengths
-- * Projections
, length
, takeLengths, takeIndices, takeElements
, getSeg
-- * Operations
, append, slice, extract)
where
import qualified Data.Array.Parallel.Unlifted.Sequential.Vector as U
import Data.Array.Parallel.Unlifted.Sequential.Vector (Vector)
import Data.Array.Parallel.Pretty hiding (empty)
import Prelude hiding (length)
here :: String -> String
here s = "Data.Array.Parallel.Unlifted.Sequential.USegd." ++ s
-- | Segment descriptor.
data USegd
= USegd
{ usegd_lengths :: !(Vector Int) -- ^ Length of each segment.
, usegd_indices :: !(Vector Int) -- ^ Starting index of each segment.
, usegd_elements :: !Int -- ^ Total number of elements in the flat array.
} deriving (Show, Eq)
instance PprPhysical USegd where
pprp (USegd lengths indices elements)
= text "USegd"
$$ (nest 7 $ vcat
[ text "lengths: " <+> (text $ show $ U.toList lengths)
, text "indices: " <+> (text $ show $ U.toList indices)
, text "elements:" <+> (text $ show elements)])
-- Constructors ---------------------------------------------------------------
-- | O(1). Construct a new segment descriptor.
mkUSegd
:: Vector Int -- ^ Length of each segment.
-> Vector Int -- ^ Starting index of each segment.
-> Int -- ^ Total number of elements in the flat array.
-> USegd
mkUSegd = USegd
{-# INLINE_U mkUSegd #-}
-- | O(1). Check the internal consistency of a segment descriptor.
--
-- As the indices and elemens field can be generated based on the segment
-- lengths, we check the consistency by rebuilding these fields and
-- comparing the rebuilt ones against the originals.
valid :: USegd -> Bool
valid usegd@(USegd lengths _ _)
= usegd == fromLengths lengths
{-# NOINLINE valid #-}
-- NOINLINE because it's only enabled during debugging anyway.
-- | O(1). Construct an empty segment descriptor, with no elements or segments.
empty :: USegd
empty = USegd U.empty U.empty 0
{-# INLINE_U empty #-}
-- | O(1). Construct a singleton segment descriptor.
-- The single segment covers the given number of elements.
singleton :: Int -> USegd
singleton n
= USegd (U.singleton n) (U.singleton 0) n
{-# INLINE_U singleton #-}
-- | O(segs). Convert an array of segment lengths into a segment descriptor.
--
-- The array contains the length of each segment, and we compute the
-- indices from that.
fromLengths :: Vector Int -> USegd
fromLengths lens
= USegd lens (U.scanl (+) 0 lens) (U.sum lens)
{-# INLINE_U fromLengths #-}
-- Projections ----------------------------------------------------------------
-- INLINE trivial projections as they'll expand to a single record selector.
-- | O(1). Yield the overall number of segments.
length :: USegd -> Int
length = U.length . usegd_lengths
{-# INLINE length #-}
-- | O(1). Yield the lengths of the individual segments.
takeLengths :: USegd -> Vector Int
takeLengths = usegd_lengths
{-# INLINE takeLengths #-}
-- | O(1). Yield the segment indices of a segment descriptor.
takeIndices :: USegd -> Vector Int
takeIndices = usegd_indices
{-# INLINE takeIndices #-}
-- | O(1). Yield the number of data elements.
takeElements :: USegd -> Int
takeElements = usegd_elements
{-# INLINE takeElements #-}
-- | O(1). Get the length and segment index of a segment
getSeg :: USegd -> Int -> (Int, Int)
getSeg (USegd lengths indices _ ) ix
= ( U.index (here "getSeg") lengths ix
, U.index (here "getSeg") indices ix)
{-# INLINE_U getSeg #-}
-- Operators ------------------------------------------------------------------
-- | O(segs). Produce a segment descriptor that describes the result of appending
-- two arrays.
append :: USegd -> USegd -> USegd
append (USegd lengths1 indices1 elems1)
(USegd lengths2 indices2 elems2)
= USegd (lengths1 U.++ lengths2)
(indices1 U.++ U.map (+ elems1) indices2)
(elems1 + elems2)
{-# INLINE_U append #-}
-- | O(segs) Extract a slice of a segment descriptor, avoiding copying where possible.
--
-- We can share the segment lengths with the original segment descriptor,
-- but still need to recompute the starting indices of each. Hence
-- runtime is O(segs) in the number of segments sliced out.
-- NOTE: In the new segment descriptor, the starting index of the first
-- segment will be 0.
slice
:: USegd -- ^ Source segment descriptor.
-> Int -- ^ Index of first segment.
-> Int -- ^ Number of segments to slice out.
-> USegd
slice segd i n
= fromLengths $ U.unsafeSlice (takeLengths segd) i n
{-# INLINE_U slice #-}
-- | Extract a slice of a segment descriptor, copying everything.
--
-- In contrast to `slice`, this function copies the array of
-- segment lengths as well as recomputing the starting indices of each.
-- NOTE: In the new segment descriptor, the starting index of the first
-- segment will be 0.
extract
:: USegd -- ^ Source segment desciptor.
-> Int -- ^ Undex of the first segment.
-> Int -- ^ Number of segments to extract out.
-> USegd
extract segd i n
= fromLengths $ U.extract (takeLengths segd) i n
{-# INLINE_U extract #-}