mappings-0.3.0.0: src/Data/Mapping/Piecewise.hs
{-# LANGUAGE
CPP,
DerivingVia
#-}
module Data.Mapping.Piecewise where
#if MIN_VERSION_GLASGOW_HASKELL(9,6,0,0)
#else
import Control.Applicative (liftA2)
#endif
import Control.Applicative (liftA3)
import Data.Algebra.Boolean
import qualified Data.Map.Internal as MI
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import qualified Data.Set as S
import Data.Mapping
-- | A data structure storing mappings that are constant on
-- intervals.
--
-- If the space of keys not discrete, then these mappings are
-- right-continuous: values are in general defined on intervals $a
-- \leq x < b$ which are closed on the left and open on the right.
data Piecewise k v = Piecewise {
-- | The value taken for sufficiently small keys
leftEnd :: v,
starts :: Map k v
} deriving (Eq, Ord)
-- | The value taken for sufficiently large keys
rightEnd :: Piecewise k v -> v
rightEnd (Piecewise a m) = case M.lookupMax m of
Nothing -> a
Just (_,b) -> b
-- | Assumes the keys are distinct and increasing (but consecutive
-- values may be the same, in which case the intervening keys are
-- removed)
fromAscList :: (Eq v) => v -> [(k,v)] -> Piecewise k v
fromAscList = let
inner _ [] = []
inner a ((y,b):r)
| a == b = inner a r
| otherwise = (y,b):inner b r
run x = Piecewise x . M.fromDistinctAscList . inner x
in run
instance (Show k, Show v) => Show (Piecewise k v) where
showsPrec d (Piecewise k m) =
("fromAscList " <>) .
showsPrec d k .
(" " <>) .
showList (M.toList m)
-- | Assumes that the keys are distinct and increasing, and also that
-- consecutive values are distinct
fromAscListUnsafe :: v -> [(k,v)] -> Piecewise k v
fromAscListUnsafe k = Piecewise k . M.fromDistinctAscList
-- | Takes value `a` for keys less than `x` and `b` otherwise
changeAt :: v -> k -> v -> Piecewise k v
changeAt a x b = Piecewise a $ M.singleton x b
-- | Is the value greater than or equal to `k`?
greaterThanOrEqual :: k -> Piecewise k Bool
greaterThanOrEqual k = changeAt False k True
-- | Is the value less than `k`?
lessThan :: k -> Piecewise k Bool
lessThan k = changeAt True k False
-- | Is the value greater than `k`? This is subject to the usual
-- concerns about `Enum` (it not to be used with floating-point
-- arithmetic, for example)
greaterThan :: Enum k => k -> Piecewise k Bool
greaterThan = greaterThanOrEqual . succ
-- | Is the value less than or equal to `k`? This is subject to the
-- usual concerns about `Enum` (it not to be used with floating-point
-- arithmetic, for example)
lessThanOrEqual :: Enum k => k -> Piecewise k Bool
lessThanOrEqual = lessThan . succ
-- | All values, in order of increasing key
values :: Piecewise k v -> [v]
values (Piecewise x m) = x : M.elems m
instance (Eq k) => Functor (Piecewise k) where
fmap p (Piecewise a f) = fromAscListUnsafe (p a) (fmap p <$> M.toList f)
instance Foldable (Piecewise k) where
foldMap f (Piecewise a m) = f a <> foldMap f m
instance Ord k => Mapping k (Piecewise k) where
cst x = Piecewise x M.empty
act (Piecewise a f) x = case M.lookupLE x f of
Nothing -> a
Just (_,b) -> b
isConst (Piecewise a f) = if M.null f then Just a else Nothing
mmap = fmap
mtraverse p (Piecewise a f) = liftA2 fromAscList (p a) (traverse (traverse p) $ M.toList f)
merge p = let
inner a b c r@((x,a'):r') s@((y,b'):s') = case compare x y of
LT -> let
c' = p a' b
in if c' == c then inner a' b c r' s else (x,c'):inner a' b c' r' s
GT -> let
c' = p a b'
in if c' == c then inner a b' c r s' else (y,c'):inner a b' c' r s'
EQ -> let
c' = p a' b'
in if c' == c then inner a' b' c r' s' else (x,c'):inner a' b' c' r' s'
inner a _ c [] ((y,b'):s') = let
c' = p a b'
in if c' == c then inner a b' c [] s' else (y,c'):inner a b' c' [] s'
inner _ b c ((x,a'):r') [] = let
c' = p a' b
in if c' == c then inner a' b c r' [] else (x,c'):inner a' b c' r' []
inner _ _ _ [] [] = []
run (Piecewise a f) (Piecewise b g) = let
c = p a b
l = inner a b c (M.toList f) (M.toList g)
in Piecewise c $ M.fromList l
in run
mergeA p = let
maybePrepend x u v l
| u == v = l
| otherwise = (x,v):l
inner a b c r@((x,a'):r') s@((y,b'):s') = case compare x y of
LT -> let
c' = p a' b
in liftA3 (maybePrepend x) c c' $ inner a' b c' r' s
GT -> let
c' = p a b'
in liftA3 (maybePrepend y) c c' $ inner a b' c' r s'
EQ -> let
c' = p a' b'
in liftA3 (maybePrepend x) c c' $ inner a' b' c' r' s'
inner a _ c [] ((y,b'):s') = let
c' = p a b'
in liftA3 (maybePrepend y) c c' $ inner a b' c' [] s'
inner _ b c ((x,a'):r') [] = let
c' = p a' b
in liftA3 (maybePrepend x) c c' $ inner a' b c' r' []
inner _ _ _ [] [] = pure []
run (Piecewise a f) (Piecewise b g) = let
c = p a b
l = inner a b c (M.toList f) (M.toList g)
in liftA2 Piecewise c (M.fromList <$> l)
in run
instance Neighbourly (Piecewise k) where
neighbours m = let
v = values m
in S.fromList $ zip v (tail v)
deriving via (AlgebraWrapper k (Piecewise k) b)
instance (Ord k, Ord b, Semigroup b) => Semigroup (Piecewise k b)
deriving via (AlgebraWrapper k (Piecewise k) b)
instance (Ord k, Ord b, Monoid b) => Monoid (Piecewise k b)
deriving via (AlgebraWrapper k (Piecewise k) b)
instance (Ord k, Ord b, Num b) => Num (Piecewise k b)
deriving via (AlgebraWrapper k (Piecewise k) b)
instance (Ord k, Ord b, Boolean b) => Boolean (Piecewise k b)
-- | Alter keys according to a function, assumed to be monotone (not checked)
mapKeysMonotonic :: (k -> l) -> Piecewise k v -> Piecewise l v
mapKeysMonotonic f (Piecewise a m) = Piecewise a (M.mapKeysMonotonic f m)
-- | Alter keys according to a function, assumed to be antitone (not checked)
mapKeysAntitonic :: (k -> l) -> Piecewise k v -> Piecewise l v
mapKeysAntitonic f = let
inner a MI.Tip = (a, MI.Tip)
inner a (MI.Bin s x b l r) = let
(a', l') = inner a l
(b', r') = inner b r
in (b', MI.Bin s (f x) a' r' l')
start (Piecewise a m) = uncurry Piecewise $ inner a m
in start
-- | Split in two: one which assumes keys are less than `k`, and one
-- which assumes them greater than or equal to `k`.
splitPiecewise :: Ord k => k -> Piecewise k v -> (Piecewise k v, Piecewise k v)
splitPiecewise k (Piecewise a m) = case M.splitLookup k m of
(m1, Just b, m2) -> (Piecewise a m1, Piecewise b m2)
(m1, Nothing, m2) -> let
p1 = Piecewise a m1
in (p1, Piecewise (rightEnd p1) m2)
-- | Assemble two maps; it is assumed that all keys in the left-hand
-- map are less than `k` and all keys in the right-hand map are
-- greater than or equal to `k` (which is not checked).
gluePiecewise :: (Eq v) => Piecewise k v -> k -> Piecewise k v -> Piecewise k v
gluePiecewise p@(Piecewise a m) k (Piecewise c n) = let
b = rightEnd p
in Piecewise a (if b == c then MI.link2 m n else MI.link k c m n)
-- | This is almost a monad (with `cst` as `pure`) except that we need
-- an `Eq` instance on the values.
mjoin :: (Ord k, Eq w) => (v -> Piecewise k w) -> Piecewise k v -> Piecewise k w
mjoin f (Piecewise a m) = let
inner p [] = p
inner p ((k,q):l) = let
(p', _) = splitPiecewise k p
(_ , q') = splitPiecewise k q
in gluePiecewise p' k $ inner q' l
in inner (f a) (fmap f <$> M.toList m)