{-# LANGUAGE FlexibleContexts #-}
module Data.IntervalIntMap
#ifndef IS_BUILDING_TEST
( IntervalIntMap
#else
( IntervalIntMap(..)
#endif
, IntervalIntMapAccumulator
, IM.Interval(..)
, fromList
, elems
, new
, insert
, unsafeFreeze
, lookup
, map
, overlaps
, overlapsWithKeys
) where
import Prelude hiding (lookup, map)
import qualified Data.IntervalIntMap.Internal.IntervalIntIntMap as IM
import qualified Data.IntervalIntMap.Internal.GrowableVector as GV
import qualified Data.Vector.Storable as VS
import qualified Data.IntSet as IS
import Foreign.Storable (Storable(..))
import Control.Monad.Primitive (PrimMonad, PrimState)
import Control.Monad (forM_)
import Control.Monad.ST (runST)
import Control.Arrow (second)
import Control.DeepSeq (NFData(..))
{-| The typical interval map structure models a function of the type @ f :: Int
- -> Maybe a@. That is, each position in the domain is either annotated by an
- interval or it is not. When you attempt to insert an interval that overlaps
- with an existing one, the new value may either (1) replace or (2) by
- combined with the older one.
-
- This is **not** the model here. The model here is @f :: Int -> [a]@! An
- interval map is a bag of intervals which may overlap. When they do overlap
- and you query at a position where multiple ones could be active, you get all
- of them (in some reliable, but unspecified, order; currently insertion
- order, but this is not an API guarantee).
-
- The API uses two objects:
-
- 'IntervalIntMapAccumulator': allows insertion. This is a Mutable object and
- insertions should be in a `PrimMonad`
-
- 'IntervalIntMap': allows querying and operations are pure.
-
-}
data IntervalIntMap a = IntervalIntMap !IM.IntervalIntMap
!(VS.Vector a)
instance NFData (IntervalIntMap a) where
rnf (IntervalIntMap im v) = rnf im `seq` rnf v
data IntervalIntMapAccumulator s a = IntervalIntMapAccumulator
!(GV.GrowableVector s (IM.IntervalValue))
!(GV.GrowableVector s a)
-- |Create an 'IntervalIntMap' from a list of (key, value)
fromList :: Storable a => [(IM.Interval, a)] -> IntervalIntMap a
fromList vs = runST $ do
acc <- new
forM_ vs $ \(i,v) -> insert i v acc
unsafeFreeze acc
elems :: Storable a => IntervalIntMap a -> [a]
elems (IntervalIntMap _ vals) = VS.toList vals
-- |New (empty) accumulator
new :: (PrimMonad m, Storable a) => m (IntervalIntMapAccumulator (PrimState m) a)
new = IntervalIntMapAccumulator <$> GV.new <*> GV.new
-- |Insert a value into an accumulator
insert :: (PrimMonad m, Storable a) => IM.Interval -> a -> IntervalIntMapAccumulator (PrimState m) a -> m ()
insert (IM.Interval s e) v (IntervalIntMapAccumulator ivs dat) = do
ix <- GV.length dat
GV.pushBack v dat
GV.pushBack (IM.IntervalValue (toEnum s) (toEnum e) (toEnum ix)) ivs
-- |Transform an 'IntervalIntMapAccumulator' into an 'IntervalIntMap'. This is
--unsafe as the accumulator should **not** be used after this operation is
--performed.
unsafeFreeze :: (PrimMonad m, Storable a) => IntervalIntMapAccumulator (PrimState m) a -> m (IntervalIntMap a)
unsafeFreeze (IntervalIntMapAccumulator ivs values) =
IntervalIntMap
<$> (IM.freeze <$> GV.unsafeFreeze ivs)
<*> GV.unsafeFreeze values
indexAll :: Storable a => VS.Vector a -> IS.IntSet -> [a]
indexAll values = (fmap $ (VS.!) values) . IS.toList
-- |Lookup all values whose keys intersect the given position
lookup :: Storable a => Int -> IntervalIntMap a -> [a]
lookup p (IntervalIntMap imap values) = indexAll values $ IM.lookup p imap
-- |Map: note that both the input and output types must be instances of
-- Storable, so this is not a functor.
map :: (Storable a, Storable b) => (a -> b) -> IntervalIntMap a -> IntervalIntMap b
map f (IntervalIntMap im vs) = IntervalIntMap im (VS.map f vs)
-- |Lookup all values that overlap with the given input
overlaps :: Storable a => IM.Interval -> IntervalIntMap a -> [a]
overlaps i = fmap snd . overlapsWithKeys i
-- |Lookup all values that overlap with the given input
overlapsWithKeys :: Storable a => IM.Interval -> IntervalIntMap a -> [(IM.Interval,a)]
overlapsWithKeys i (IntervalIntMap imap values) = fmap (second $ (VS.!) values) $ IM.overlapsWithKeys i imap