data-interval-2.0.0: src/Data/IntervalMap/Strict.hs
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE CPP, BangPatterns, TupleSections #-}
{-# LANGUAGE Safe #-}
-----------------------------------------------------------------------------
-- |
-- Module : Data.IntervalMap.Base
-- Copyright : (c) Masahiro Sakai 2016
-- License : BSD-style
--
-- Maintainer : masahiro.sakai@gmail.com
-- Stability : provisional
-- Portability : non-portable (BangPatterns, TupleSections)
--
-- Mapping from intervals to values.
--
-- API of this module is strict in both the keys and the values.
-- If you need value-lazy maps, use "Data.IntervalMap.Lazy" instead.
-- The 'IntervalMap' type itself is shared between the lazy and strict modules,
-- meaning that the same 'IntervalMap' value can be passed to functions in
-- both modules (although that is rarely needed).
--
-- These modules are intended to be imported qualified, to avoid name
-- clashes with Prelude functions, e.g.
--
-- > import Data.IntervalMap.Strict (IntervalMap)
-- > import qualified Data.IntervalMap.Strict as IntervalMap
--
-----------------------------------------------------------------------------
module Data.IntervalMap.Strict
(
-- * Strictness properties
-- $strictness
-- * IntervalMap type
IntervalMap
, module Data.ExtendedReal
-- * Operators
, (!)
, (\\)
-- * Query
, null
, member
, notMember
, lookup
, findWithDefault
, span
-- * Construction
, whole
, empty
, singleton
-- ** Insertion
, insert
, insertWith
-- ** Delete\/Update
, delete
, adjust
, update
, alter
-- * Combine
, union
, unionWith
, unions
, unionsWith
, intersection
, intersectionWith
, difference
-- * Traversal
, map
, mapKeysMonotonic
-- * Conversion
, elems
, keys
, assocs
, keysSet
-- ** List
, fromList
, fromListWith
, toList
-- ** Ordered List
, toAscList
, toDescList
-- * Filter
, filter
, split
-- * Submap
, isSubmapOf
, isSubmapOfBy
, isProperSubmapOf
, isProperSubmapOfBy
)
where
import Prelude hiding (null, lookup, map, filter, span)
import Data.ExtendedReal
import Data.Interval (Interval)
import qualified Data.Interval as Interval
import Data.IntervalMap.Base hiding
( whole
, singleton
, insert
, insertWith
, adjust
, update
, alter
, unionWith
, unionsWith
, intersectionWith
, map
, fromList
, fromListWith
)
import qualified Data.IntervalMap.Base as B
import qualified Data.IntervalSet as IntervalSet
import Data.List (foldl')
import qualified Data.Map.Strict as Map
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative ((<$>))
#endif
-- $strictness
--
-- This module satisfies the following strictness properties:
--
-- 1. Key arguments are evaluated to WHNF;
--
-- 2. Keys and values are evaluated to WHNF before they are stored in
-- the map.
--
-- Here's an example illustrating the first property:
--
-- > delete undefined m == undefined
--
-- Here are some examples that illustrate the second property:
--
-- > map (\ v -> undefined) m == undefined -- m is not empty
-- > mapKeysMonotonic (\ k -> undefined) m == undefined -- m is not empty
-- | The map that maps whole range of k to a.
whole :: Ord k => a -> IntervalMap k a
whole !a = B.whole a
-- | A map with a single interval.
singleton :: Ord k => Interval k -> a -> IntervalMap k a
singleton i !a = B.singleton i a
-- | insert a new key and value in the map.
-- If the key is already present in the map, the associated value is
-- replaced with the supplied value.
insert :: Ord k => Interval k -> a -> IntervalMap k a -> IntervalMap k a
insert i !a m = B.insert i a m
-- | Insert with a function, combining new value and old value.
-- @'insertWith' f key value mp@ will insert the pair (interval, value) into @mp@.
-- If the interval overlaps with existing entries, the value for the entry is replace
-- with @(f new_value old_value)@.
insertWith :: Ord k => (a -> a -> a) -> Interval k -> a -> IntervalMap k a -> IntervalMap k a
insertWith _ i _ m | Interval.null i = m
insertWith f i !a m = alter g i m
where
g Nothing = Just a
g (Just a') = Just $! f a a'
-- | Update a value at a specific interval with the result of the provided function.
-- When the interval does not overlatp with the map, the original map is returned.
adjust :: Ord k => (a -> a) -> Interval k -> IntervalMap k a -> IntervalMap k a
adjust f = update (Just . f)
-- | The expression (@'update' f i map@) updates the value @x@
-- at @i@ (if it is in the map). If (@f x@) is 'Nothing', the element is
-- deleted. If it is (@'Just' y@), the key @i@ is bound to the new value @y@.
update :: Ord k => (a -> Maybe a) -> Interval k -> IntervalMap k a -> IntervalMap k a
update _ i m | Interval.null i = m
update f i m =
case split i m of
(IntervalMap m1, IntervalMap m2, IntervalMap m3) ->
IntervalMap $ Map.unions [m1, Map.mapMaybe (\(j,a) -> (\b -> seq b (j,b)) <$> f a) m2, m3]
-- | The expression (@'alter' f i map@) alters the value @x@ at @i@, or absence thereof.
-- 'alter' can be used to insert, delete, or update a value in a 'IntervalMap'.
alter :: Ord k => (Maybe a -> Maybe a) -> Interval k -> IntervalMap k a -> IntervalMap k a
alter _ i m | Interval.null i = m
alter f i m =
case split i m of
(IntervalMap m1, IntervalMap m2, IntervalMap m3) ->
let m2' = Map.mapMaybe (\(j,a) -> (\b -> seq b (j,b)) <$> f (Just a)) m2
js = IntervalSet.singleton i `IntervalSet.difference` keysSet (IntervalMap m2)
IntervalMap m2'' =
case f Nothing of
Nothing -> empty
Just !a -> B.fromList [(j,a) | j <- IntervalSet.toList js]
in seq m2' $ IntervalMap $ Map.unions [m1, m2', m2'', m3]
-- ------------------------------------------------------------------------
-- Combine
-- | Union with a combining function.
unionWith :: Ord k => (a -> a -> a) -> IntervalMap k a -> IntervalMap k a -> IntervalMap k a
unionWith f m1 m2 =
foldl' (\m (i,a) -> insertWith f i a m) m2 (toList m1)
-- | The union of a list of maps, with a combining operation:
-- (@'unionsWith' f == 'Prelude.foldl' ('unionWith' f) 'empty'@).
unionsWith :: Ord k => (a -> a -> a) -> [IntervalMap k a] -> IntervalMap k a
unionsWith f = foldl' (unionWith f) empty
-- | Intersection with a combining function.
intersectionWith :: Ord k => (a -> b -> c) -> IntervalMap k a -> IntervalMap k b -> IntervalMap k c
intersectionWith f im1@(IntervalMap m1) im2@(IntervalMap m2)
| Map.size m1 >= Map.size m2 = g f im1 im2
| otherwise = g (flip f) im2 im1
where
g :: Ord k => (a -> b -> c) -> IntervalMap k a -> IntervalMap k b -> IntervalMap k c
g h jm1 (IntervalMap m3) = IntervalMap $ Map.unions $ go jm1 (Map.elems m3)
where
go _ [] = []
go im ((i,b) : xs) =
case split i im of
(_, IntervalMap m, jm2) ->
Map.map (\(j, a) -> (j,) $! h a b) m : go jm2 xs
-- ------------------------------------------------------------------------
-- Traversal
-- | Map a function over all values in the map.
map :: (a -> b) -> IntervalMap k a -> IntervalMap k b
map f (IntervalMap m) = IntervalMap $ Map.map (\(i, a) -> (i,) $! f a) m
-- ------------------------------------------------------------------------
-- Conversion
-- | Build a map from a list of key\/value pairs.
-- If the list contains more than one value for the same key, the last value
-- for the key is retained.
fromList :: Ord k => [(Interval k, a)] -> IntervalMap k a
fromList = foldl' (\m (i,a) -> insert i a m) empty
-- | Build a map from a list of key\/value pairs with a combining function.
fromListWith :: Ord k => (a -> a -> a) -> [(Interval k, a)] -> IntervalMap k a
fromListWith f = foldl' (\m (i,a) -> insertWith f i a m) empty