nonempty-containers-0.3.5.0: src/Data/IntMap/NonEmpty/Internal.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_HADDOCK not-home #-}
-- |
-- Module : Data.IntMap.NonEmpty.Internal
-- Copyright : (c) Justin Le 2018
-- License : BSD3
--
-- Maintainer : justin@jle.im
-- Stability : experimental
-- Portability : non-portable
--
-- Unsafe internal-use functions used in the implementation of
-- "Data.IntMap.NonEmpty". These functions can potentially be used to
-- break the abstraction of 'NEIntMap' and produce unsound maps, so be
-- wary!
module Data.IntMap.NonEmpty.Internal (
-- * Non-Empty IntMap type
NEIntMap (..),
Key,
singleton,
nonEmptyMap,
withNonEmpty,
fromList,
toList,
map,
insertWith,
union,
unions,
elems,
size,
toMap,
-- * Folds
foldr,
foldr',
foldr1,
foldl,
foldl',
foldl1,
-- * Traversals
traverseWithKey,
traverseWithKey1,
foldMapWithKey,
-- * Unsafe IntMap Functions
insertMinMap,
insertMaxMap,
-- * Debug
valid,
) where
import Control.Applicative
import Control.Comonad
import Control.DeepSeq
import Control.Monad
import qualified Data.Aeson as A
import Data.Coerce
import Data.Data
import qualified Data.Foldable as F
import Data.Function
import Data.Functor.Alt
import Data.Functor.Classes
import Data.Functor.Invariant
import qualified Data.IntMap as M
import Data.IntMap.Internal (IntMap (..), Key)
import qualified Data.List as L
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe
import Data.Semigroup
import Data.Semigroup.Foldable (Foldable1 (fold1))
import qualified Data.Semigroup.Foldable as F1
import Data.Semigroup.Traversable (Traversable1 (..))
import Text.Read
import Prelude hiding (Foldable (..), map)
-- | A non-empty (by construction) map from integer keys to values @a@. At
-- least one key-value pair exists in an @'NEIntMap' v@ at all times.
--
-- Functions that /take/ an 'NEIntMap' can safely operate on it with the
-- assumption that it has at least one key-value pair.
--
-- Functions that /return/ an 'NEIntMap' provide an assurance that the result
-- has at least one key-value pair.
--
-- "Data.IntMap.NonEmpty" re-exports the API of "Data.IntMap", faithfully
-- reproducing asymptotics, typeclass constraints, and semantics.
-- Functions that ensure that input and output maps are both non-empty
-- (like 'Data.IntMap.NonEmpty.insert') return 'NEIntMap', but functions that
-- might potentially return an empty map (like 'Data.IntMap.NonEmpty.delete')
-- return a 'IntMap' instead.
--
-- You can directly construct an 'NEIntMap' with the API from
-- "Data.IntMap.NonEmpty"; it's more or less the same as constructing a normal
-- 'IntMap', except you don't have access to 'Data.IntMap.empty'. There are also
-- a few ways to construct an 'NEIntMap' from a 'IntMap':
--
-- 1. The 'nonEmptyMap' smart constructor will convert a @'IntMap' k a@ into
-- a @'Maybe' ('NEIntMap' k a)@, returning 'Nothing' if the original 'IntMap'
-- was empty.
-- 2. You can use the 'Data.IntMap.NonEmpty.insertIntMap' family of functions to
-- insert a value into a 'IntMap' to create a guaranteed 'NEIntMap'.
-- 3. You can use the 'Data.IntMap.NonEmpty.IsNonEmpty' and
-- 'Data.IntMap.NonEmpty.IsEmpty' patterns to "pattern match" on a 'IntMap'
-- to reveal it as either containing a 'NEIntMap' or an empty map.
-- 4. 'withNonEmpty' offers a continuation-based interface for
-- deconstructing a 'IntMap' and treating it as if it were an
-- 'NEIntMap'.
--
-- You can convert an 'NEIntMap' into a 'IntMap' with 'toMap' or
-- 'Data.IntMap.NonEmpty.IsNonEmpty', essentially "obscuring" the non-empty
-- property from the type.
data NEIntMap a
= NEIntMap
{ neimK0 :: !Key
-- ^ invariant: must be smaller than smallest key in map
, neimV0 :: a
, neimIntMap :: !(IntMap a)
}
deriving (Typeable)
instance Eq a => Eq (NEIntMap a) where
t1 == t2 =
M.size (neimIntMap t1) == M.size (neimIntMap t2)
&& toList t1 == toList t2
instance Ord a => Ord (NEIntMap a) where
compare = compare `on` toList
(<) = (<) `on` toList
(>) = (>) `on` toList
(<=) = (<=) `on` toList
(>=) = (>=) `on` toList
instance Eq1 NEIntMap where
liftEq eq m1 m2 =
M.size (neimIntMap m1) == M.size (neimIntMap m2)
&& liftEq (liftEq eq) (toList m1) (toList m2)
instance Ord1 NEIntMap where
liftCompare cmp m n =
liftCompare (liftCompare cmp) (toList m) (toList n)
instance Show1 NEIntMap where
liftShowsPrec sp sl d m =
showsUnaryWith (liftShowsPrec sp' sl') "fromList" d (toList m)
where
sp' = liftShowsPrec sp sl
sl' = liftShowList sp sl
instance Read1 NEIntMap where
liftReadsPrec rp rl =
readsData $
readsUnaryWith (liftReadsPrec rp' rl') "fromList" fromList
where
rp' = liftReadsPrec rp rl
rl' = liftReadList rp rl
instance Read e => Read (NEIntMap e) where
readPrec = parens $ prec 10 $ do
Ident "fromList" <- lexP
xs <- parens . prec 10 $ readPrec
return (fromList xs)
readListPrec = readListPrecDefault
instance Show a => Show (NEIntMap a) where
showsPrec d m =
showParen (d > 10) $
showString "fromList (" . shows (toList m) . showString ")"
instance NFData a => NFData (NEIntMap a) where
rnf (NEIntMap k v a) = rnf k `seq` rnf v `seq` rnf a
-- Data instance code from Data.IntMap.Internal
--
-- Copyright : (c) Daan Leijen 2002
-- (c) Andriy Palamarchuk 2008
-- (c) wren romano 2016
#if MIN_VERSION_base(4,16,0)
instance Data a => Data (NEIntMap a) where
gfoldl f z im = z fromList `f` toList im
toConstr _ = fromListConstr
gunfold k z c = case constrIndex c of
1 -> k (z fromList)
_ -> error "gunfold"
dataTypeOf _ = intMapDataType
dataCast1 = gcast1
#else
#ifndef __HLINT__
instance Data a => Data (NEIntMap a) where
gfoldl f z im = z fromList `f` toList im
toConstr _ = fromListConstr
gunfold k z c = case constrIndex c of
1 -> k (z fromList)
_ -> error "gunfold"
dataTypeOf _ = intMapDataType
dataCast1 f = gcast1 f
#endif
#endif
fromListConstr :: Constr
fromListConstr = mkConstr intMapDataType "fromList" [] Prefix
intMapDataType :: DataType
intMapDataType = mkDataType "Data.IntMap.NonEmpty.Internal.NEIntMap" [fromListConstr]
instance A.ToJSON a => A.ToJSON (NEIntMap a) where
toJSON = A.toJSON . toMap
toEncoding = A.toEncoding . toMap
instance A.FromJSON a => A.FromJSON (NEIntMap a) where
parseJSON =
withNonEmpty (fail err) pure
<=< A.parseJSON
where
err = "NEIntMap: Non-empty map expected, but empty map found"
-- | @since 0.3.4.4
instance Alt NEIntMap where
(<!>) = union
-- | /O(n)/. Fold the values in the map using the given right-associative
-- binary operator, such that @'foldr' f z == 'Prelude.foldr' f z . 'elems'@.
--
-- > elemsList map = foldr (:) [] map
--
-- > let f a len = len + (length a)
-- > foldr f 0 (fromList ((5,"a") :| [(3,"bbb")])) == 4
foldr :: (a -> b -> b) -> b -> NEIntMap a -> b
foldr f z (NEIntMap _ v m) = v `f` M.foldr f z m
{-# INLINE foldr #-}
-- | /O(n)/. A strict version of 'foldr'. Each application of the operator
-- is evaluated before using the result in the next application. This
-- function is strict in the starting value.
foldr' :: (a -> b -> b) -> b -> NEIntMap a -> b
foldr' f z (NEIntMap _ v m) = v `f` y
where
!y = M.foldr' f z m
{-# INLINE foldr' #-}
-- | /O(n)/. A version of 'foldr' that uses the value at the maximal key in
-- the map as the starting value.
--
-- Note that, unlike 'Data.Foldable.foldr1' for 'IntMap', this function is
-- total if the input function is total.
foldr1 :: (a -> a -> a) -> NEIntMap a -> a
foldr1 f (NEIntMap _ v m) =
maybe v (f v . uncurry (M.foldr f))
. M.maxView
$ m
{-# INLINE foldr1 #-}
-- | /O(n)/. Fold the values in the map using the given left-associative
-- binary operator, such that @'foldl' f z == 'Prelude.foldl' f z . 'elems'@.
--
-- > elemsList = reverse . foldl (flip (:)) []
--
-- > let f len a = len + (length a)
-- > foldl f 0 (fromList ((5,"a") :| [(3,"bbb")])) == 4
foldl :: (a -> b -> a) -> a -> NEIntMap b -> a
foldl f z (NEIntMap _ v m) = M.foldl f (f z v) m
{-# INLINE foldl #-}
-- | /O(n)/. A strict version of 'foldl'. Each application of the operator
-- is evaluated before using the result in the next application. This
-- function is strict in the starting value.
foldl' :: (a -> b -> a) -> a -> NEIntMap b -> a
foldl' f z (NEIntMap _ v m) = M.foldl' f x m
where
!x = f z v
{-# INLINE foldl' #-}
-- | /O(n)/. A version of 'foldl' that uses the value at the minimal key in
-- the map as the starting value.
--
-- Note that, unlike 'Data.Foldable.foldl1' for 'IntMap', this function is
-- total if the input function is total.
foldl1 :: (a -> a -> a) -> NEIntMap a -> a
foldl1 f (NEIntMap _ v m) = M.foldl f v m
{-# INLINE foldl1 #-}
-- | /O(n)/. Fold the keys and values in the map using the given semigroup,
-- such that
--
-- @'foldMapWithKey' f = 'Data.Semigroup.Foldable.fold1' . 'Data.IntMap.NonEmpty.mapWithKey' f@
--
-- __WARNING__: Differs from @Data.IntMap.foldMapWithKey@, which traverses
-- positive items first, then negative items.
--
-- This can be an asymptotically faster than
-- 'Data.IntMap.NonEmpty.foldrWithKey' or 'Data.IntMap.NonEmpty.foldlWithKey' for
-- some monoids.
-- TODO: benchmark against maxView method
foldMapWithKey ::
Semigroup m =>
(Key -> a -> m) ->
NEIntMap a ->
m
foldMapWithKey f = F1.foldMap1 (uncurry f) . toList
{-# INLINE foldMapWithKey #-}
-- | /O(n)/. IntMap a function over all values in the map.
--
-- > map (++ "x") (fromList ((5,"a") :| [(3,"b")])) == fromList ((3, "bx") :| [(5, "ax")])
map :: (a -> b) -> NEIntMap a -> NEIntMap b
map f (NEIntMap k0 v m) = NEIntMap k0 (f v) (M.map f m)
{-# NOINLINE [1] map #-}
{-# RULES
"map/map" forall f g xs. map f (map g xs) = map (f . g) xs
#-}
{-# RULES
"map/coerce" map coerce = coerce
#-}
-- | /O(m*log(n\/m + 1)), m <= n/.
-- The expression (@'union' t1 t2@) takes the left-biased union of @t1@ and
-- @t2@. It prefers @t1@ when duplicate keys are encountered, i.e.
-- (@'union' == 'Data.IntMap.NonEmpty.unionWith' 'const'@).
--
-- > union (fromList ((5, "a") :| [(3, "b")])) (fromList ((5, "A") :| [(7, "C")])) == fromList ((3, "b") :| [(5, "a"), (7, "C")])
union ::
NEIntMap a ->
NEIntMap a ->
NEIntMap a
union n1@(NEIntMap k1 v1 m1) n2@(NEIntMap k2 v2 m2) = case compare k1 k2 of
LT -> NEIntMap k1 v1 . M.union m1 . toMap $ n2
EQ -> NEIntMap k1 v1 . M.union m1 $ m2
GT -> NEIntMap k2 v2 . M.union (toMap n1) $ m2
{-# INLINE union #-}
-- | The left-biased union of a non-empty list of maps.
--
-- > unions (fromList ((5, "a") :| [(3, "b")]) :| [fromList ((5, "A") :| [(7, "C")]), fromList ((5, "A3") :| [(3, "B3")])])
-- > == fromList [(3, "b"), (5, "a"), (7, "C")]
-- > unions (fromList ((5, "A3") :| [(3, "B3")]) :| [fromList ((5, "A") :| [(7, "C")]), fromList ((5, "a") :| [(3, "b")])])
-- > == fromList ((3, "B3") :| [(5, "A3"), (7, "C")])
unions ::
Foldable1 f =>
f (NEIntMap a) ->
NEIntMap a
unions (F1.toNonEmpty -> (m :| ms)) = F.foldl' union m ms
{-# INLINE unions #-}
-- | /O(n)/.
-- Return all elements of the map in the ascending order of their keys.
--
-- > elems (fromList ((5,"a") :| [(3,"b")])) == ("b" :| ["a"])
elems :: NEIntMap a -> NonEmpty a
elems (NEIntMap _ v m) = v :| M.elems m
{-# INLINE elems #-}
-- | /O(1)/. The number of elements in the map. Guaranteed to be greater
-- than zero.
--
-- > size (singleton 1 'a') == 1
-- > size (fromList ((1,'a') :| [(2,'c'), (3,'b')])) == 3
size :: NEIntMap a -> Int
size (NEIntMap _ _ m) = 1 + M.size m
{-# INLINE size #-}
-- | /O(log n)/.
-- Convert a non-empty map back into a normal possibly-empty map, for usage
-- with functions that expect 'IntMap'.
--
-- Can be thought of as "obscuring" the non-emptiness of the map in its
-- type. See the 'Data.IntMap.NonEmpty.IsNotEmpty' pattern.
--
-- 'nonEmptyMap' and @'maybe' 'Data.IntMap.empty' 'toMap'@ form an isomorphism: they
-- are perfect structure-preserving inverses of eachother.
--
-- > toMap (fromList ((3,"a") :| [(5,"b")])) == Data.IntMap.fromList [(3,"a"), (5,"b")]
toMap :: NEIntMap a -> IntMap a
toMap (NEIntMap k v m) = insertMinMap k v m
{-# INLINE toMap #-}
-- | /O(n)/.
-- @'traverseWithKey' f m == 'fromList' <$> 'traverse' (\(k, v) -> (,) k <$> f k v) ('toList' m)@
-- That is, behaves exactly like a regular 'traverse' except that the traversing
-- function also has access to the key associated with a value.
--
-- /Use 'traverseWithKey1'/ whenever possible (if your 'Applicative'
-- also has 'Apply' instance). This version is provided only for types
-- that do not have 'Apply' instance, since 'Apply' is not at the moment
-- (and might not ever be) an official superclass of 'Applicative'.
--
-- __WARNING__: Differs from @Data.IntMap.traverseWithKey@, which traverses
-- positive items first, then negative items.
--
-- @
-- 'traverseWithKey' f = 'unwrapApplicative' . 'traverseWithKey1' (\\k -> WrapApplicative . f k)
-- @
traverseWithKey ::
Applicative t =>
(Key -> a -> t b) ->
NEIntMap a ->
t (NEIntMap b)
traverseWithKey f (NEIntMap k v m0) =
NEIntMap k
<$> f k v
<*> M.traverseWithKey f m0
{-# INLINE traverseWithKey #-}
-- | /O(n)/.
-- @'traverseWithKey1' f m == 'fromList' <$> 'traverse1' (\(k, v) -> (,) k <$> f k v) ('toList' m)@
--
-- That is, behaves exactly like a regular 'traverse1' except that the traversing
-- function also has access to the key associated with a value.
--
-- __WARNING__: Differs from @Data.IntMap.traverseWithKey@, which traverses
-- positive items first, then negative items.
--
-- Is more general than 'traverseWithKey', since works with all 'Apply',
-- and not just 'Applicative'.
-- TODO: benchmark against maxView-based methods
traverseWithKey1 ::
Apply t =>
(Key -> a -> t b) ->
NEIntMap a ->
t (NEIntMap b)
traverseWithKey1 f (NEIntMap k0 v m0) = case runMaybeApply m1 of
Left m2 -> NEIntMap k0 <$> f k0 v <.> m2
Right m2 -> flip (NEIntMap k0) m2 <$> f k0 v
where
m1 = M.traverseWithKey (\k -> MaybeApply . Left . f k) m0
{-# INLINEABLE traverseWithKey1 #-}
-- | /O(n)/. Convert the map to a non-empty list of key\/value pairs.
--
-- > toList (fromList ((5,"a") :| [(3,"b")])) == ((3,"b") :| [(5,"a")])
toList :: NEIntMap a -> NonEmpty (Key, a)
toList (NEIntMap k v m) = (k, v) :| M.toList m
{-# INLINE toList #-}
-- | /O(log n)/. Smart constructor for an 'NEIntMap' from a 'IntMap'. Returns
-- 'Nothing' if the 'IntMap' was originally actually empty, and @'Just' n@
-- with an 'NEIntMap', if the 'IntMap' was not empty.
--
-- 'nonEmptyMap' and @'maybe' 'Data.IntMap.empty' 'toMap'@ form an
-- isomorphism: they are perfect structure-preserving inverses of
-- eachother.
--
-- See 'Data.IntMap.NonEmpty.IsNonEmpty' for a pattern synonym that lets you
-- "match on" the possiblity of a 'IntMap' being an 'NEIntMap'.
--
-- > nonEmptyMap (Data.IntMap.fromList [(3,"a"), (5,"b")]) == Just (fromList ((3,"a") :| [(5,"b")]))
nonEmptyMap :: IntMap a -> Maybe (NEIntMap a)
nonEmptyMap = (fmap . uncurry . uncurry) NEIntMap . M.minViewWithKey
{-# INLINE nonEmptyMap #-}
-- | /O(log n)/. A general continuation-based way to consume a 'IntMap' as if
-- it were an 'NEIntMap'. @'withNonEmpty' def f@ will take a 'IntMap'. If map is
-- empty, it will evaluate to @def@. Otherwise, a non-empty map 'NEIntMap'
-- will be fed to the function @f@ instead.
--
-- @'nonEmptyMap' == 'withNonEmpty' 'Nothing' 'Just'@
withNonEmpty ::
-- | value to return if map is empty
r ->
-- | function to apply if map is not empty
(NEIntMap a -> r) ->
IntMap a ->
r
withNonEmpty def f = maybe def f . nonEmptyMap
{-# INLINE withNonEmpty #-}
-- | /O(n*log n)/. Build a non-empty map from a non-empty list of
-- key\/value pairs. See also 'Data.IntMap.NonEmpty.fromAscList'. If the list
-- contains more than one value for the same key, the last value for the
-- key is retained.
--
-- > fromList ((5,"a") :| [(3,"b"), (5, "c")]) == fromList ((5,"c") :| [(3,"b")])
-- > fromList ((5,"c") :| [(3,"b"), (5, "a")]) == fromList ((5,"a") :| [(3,"b")])
-- TODO: write manually and optimize to be equivalent to
-- 'fromDistinctAscList' if items are ordered, just like the actual
-- 'M.fromList'.
fromList :: NonEmpty (Key, a) -> NEIntMap a
fromList ((k, v) :| xs) =
withNonEmpty (singleton k v) (insertWith (const id) k v)
. M.fromList
$ xs
{-# INLINE fromList #-}
-- | /O(1)/. A map with a single element.
--
-- > singleton 1 'a' == fromList ((1, 'a') :| [])
-- > size (singleton 1 'a') == 1
singleton :: Key -> a -> NEIntMap a
singleton k v = NEIntMap k v M.empty
{-# INLINE singleton #-}
-- | /O(log n)/. Insert with a function, combining new value and old value.
-- @'insertWith' f key value mp@ will insert the pair (key, value) into
-- @mp@ if key does not exist in the map. If the key does exist, the
-- function will insert the pair @(key, f new_value old_value)@.
--
-- See 'Data.IntMap.NonEmpty.insertIntMapWith' for a version where the first
-- argument is a 'IntMap'.
--
-- > insertWith (++) 5 "xxx" (fromList ((5,"a") :| [(3,"b")])) == fromList ((3, "b") :| [(5, "xxxa")])
-- > insertWith (++) 7 "xxx" (fromList ((5,"a") :| [(3,"b")])) == fromList ((3, "b") :| [(5, "a"), (7, "xxx")])
insertWith ::
(a -> a -> a) ->
Key ->
a ->
NEIntMap a ->
NEIntMap a
insertWith f k v n@(NEIntMap k0 v0 m) = case compare k k0 of
LT -> NEIntMap k v . toMap $ n
EQ -> NEIntMap k (f v v0) m
GT -> NEIntMap k0 v0 $ M.insertWith f k v m
{-# INLINE insertWith #-}
-- | Left-biased union
instance Semigroup (NEIntMap a) where
(<>) = union
{-# INLINE (<>) #-}
sconcat = unions
{-# INLINE sconcat #-}
instance Functor NEIntMap where
fmap = map
{-# INLINE fmap #-}
x <$ NEIntMap k _ m = NEIntMap k x (x <$ m)
{-# INLINE (<$) #-}
-- | @since 0.3.4.4
instance Invariant NEIntMap where
invmap f _ = fmap f
{-# INLINE invmap #-}
-- | Traverses elements in order of ascending keys.
--
-- __WARNING:__ 'F.fold' and 'F.foldMap' are different than for the
-- 'IntMap' instance. They traverse elements in order of ascending keys,
-- while 'IntMap' traverses positive keys first, then negative keys.
--
-- 'Data.Foldable.foldr1', 'Data.Foldable.foldl1', 'Data.Foldable.minimum',
-- 'Data.Foldable.maximum' are all total.
#if MIN_VERSION_base(4,11,0)
instance F.Foldable NEIntMap where
fold (NEIntMap _ v m) = v <> F.fold (M.elems m)
{-# INLINE fold #-}
foldMap f (NEIntMap _ v m) = f v <> F.foldMap f (M.elems m)
{-# INLINE foldMap #-}
foldr = foldr
{-# INLINE foldr #-}
foldr' = foldr'
{-# INLINE foldr' #-}
foldr1 = foldr1
{-# INLINE foldr1 #-}
foldl = foldl
{-# INLINE foldl #-}
foldl' = foldl'
{-# INLINE foldl' #-}
foldl1 = foldl1
{-# INLINE foldl1 #-}
null _ = False
{-# INLINE null #-}
length = size
{-# INLINE length #-}
elem x (NEIntMap _ v m) = F.elem x m
|| x == v
{-# INLINE elem #-}
-- TODO: use build
toList = F.toList . elems
{-# INLINE toList #-}
#else
instance F.Foldable NEIntMap where
fold (NEIntMap _ v m) = v `mappend` F.fold (M.elems m)
{-# INLINE fold #-}
foldMap f (NEIntMap _ v m) = f v `mappend` F.foldMap f (M.elems m)
{-# INLINE foldMap #-}
foldr = foldr
{-# INLINE foldr #-}
foldr' = foldr'
{-# INLINE foldr' #-}
foldr1 = foldr1
{-# INLINE foldr1 #-}
foldl = foldl
{-# INLINE foldl #-}
foldl' = foldl'
{-# INLINE foldl' #-}
foldl1 = foldl1
{-# INLINE foldl1 #-}
null _ = False
{-# INLINE null #-}
length = size
{-# INLINE length #-}
elem x (NEIntMap _ v m) = F.elem x m
|| x == v
{-# INLINE elem #-}
-- TODO: use build
toList = F.toList . elems
{-# INLINE toList #-}
#endif
-- | Traverses elements in order of ascending keys
--
-- __WARNING:__ Different than for the 'IntMap' instance. They traverse
-- elements in order of ascending keys, while 'IntMap' traverses positive
-- keys first, then negative keys.
instance Traversable NEIntMap where
traverse f = traverseWithKey (const f)
{-# INLINE traverse #-}
-- | Traverses elements in order of ascending keys
--
-- __WARNING:__ 'F1.fold1' and 'F1.foldMap1' are different than 'F.fold' and
-- 'F.foldMap' for the 'IntMap' instance of 'Foldable'. They traverse
-- elements in order of ascending keys, while 'IntMap' traverses positive
-- keys first, then negative keys.
#if MIN_VERSION_base(4,11,0)
instance Foldable1 NEIntMap where
fold1 (NEIntMap _ v m) = maybe v (v <>)
. F.foldMap Just
. M.elems
$ m
{-# INLINE fold1 #-}
foldMap1 f = foldMapWithKey (const f)
{-# INLINE foldMap1 #-}
toNonEmpty = elems
{-# INLINE toNonEmpty #-}
#else
instance Foldable1 NEIntMap where
fold1 (NEIntMap _ v m) = option v (v <>)
. F.foldMap (Option . Just)
. M.elems
$ m
{-# INLINE fold1 #-}
foldMap1 f = foldMapWithKey (const f)
{-# INLINE foldMap1 #-}
toNonEmpty = elems
{-# INLINE toNonEmpty #-}
#endif
-- | Traverses elements in order of ascending keys
--
-- __WARNING:__ 'traverse1' and 'sequence1' are different 'traverse' and
-- 'sequence' for the 'IntMap' instance of 'Traversable'. They traverse
-- elements in order of ascending keys, while 'IntMap' traverses positive
-- keys first, then negative keys.
instance Traversable1 NEIntMap where
traverse1 f = traverseWithKey1 (const f)
{-# INLINE traverse1 #-}
-- | 'extract' gets the value at the minimal key, and 'duplicate' produces
-- a map of maps comprised of all keys from the original map greater than
-- or equal to the current key.
--
-- @since 0.1.1.0
instance Comonad NEIntMap where
extract = neimV0
{-# INLINE extract #-}
-- We'd like to use 'M.mapAccumWithKey', but it traverses things in the
-- wrong order.
duplicate n0@(NEIntMap k0 _ m0) =
NEIntMap k0 n0
. M.fromDistinctAscList
. snd
. L.mapAccumL go m0
. M.toList
$ m0
where
go m (k, v) = (m', (k, NEIntMap k v m'))
where
!m' = M.deleteMin m
{-# INLINE duplicate #-}
-- | /O(n)/. Test if the internal map structure is valid.
valid :: NEIntMap a -> Bool
valid (NEIntMap k _ m) = all ((k <) . fst . fst) (M.minViewWithKey m)
-- | /O(log n)/. Insert new key and value into a map where keys are
-- /strictly greater than/ the new key. That is, the new key must be
-- /strictly less than/ all keys present in the 'IntMap'. /The precondition
-- is not checked./
--
-- At the moment this is simply an alias for @Data.IntSet.insert@, but it's
-- left here as a placeholder in case this eventually gets implemented in
-- a more efficient way.
-- TODO: implementation
insertMinMap :: Key -> a -> IntMap a -> IntMap a
insertMinMap = M.insert
{-# INLINEABLE insertMinMap #-}
-- | /O(log n)/. Insert new key and value into a map where keys are
-- /strictly less than/ the new key. That is, the new key must be
-- /strictly greater than/ all keys present in the 'IntMap'. /The
-- precondition is not checked./
--
-- At the moment this is simply an alias for @Data.IntSet.insert@, but it's
-- left here as a placeholder in case this eventually gets implemented in
-- a more efficient way.
-- TODO: implementation
insertMaxMap :: Key -> a -> IntMap a -> IntMap a
insertMaxMap = M.insert
{-# INLINEABLE insertMaxMap #-}