interval-1.20160821: Data/Interval/Sieve.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.Interval.Sieve where
import Control.Applicative (Applicative(..))
import Control.Exception (assert)
import Data.Bool
import Data.Eq (Eq(..))
import Data.FingerTree (FingerTree, ViewL(..), ViewR(..), (><), (<|), (|>))
import qualified Data.FingerTree as FT
import Data.Foldable (Foldable)
import qualified Data.Foldable as Foldable
import Data.Function (($), (.))
import Data.Functor ((<$>))
import qualified Data.List
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Monoid (Monoid(..))
import Data.Ord (Ord(..), Ordering(..))
import Prelude (Bounded(..), undefined)
import Text.Show (Show(..))
import qualified Data.Interval as Interval
import Data.Interval ( Interval(..)
, low, high
, (..<..), (..<<..)
, Position(..), position
, flip_limit
, Pretty(..) )
-- * Type 'Sieve'
-- | '..<<..'-ordered union of 'Interval's.
--
-- __Ressources:__
--
-- * Ralf Hinze and Ross Paterson,
-- \"Finger trees: a simple general-purpose data structure\",
-- /Journal of Functional Programming/ 16:2 (2006) pp 197-217.
-- <http://staff.city.ac.uk/~ross/papers/FingerTree.html>
-- * <https://hackage.haskell.org/package/fingertree/docs/Data-IntervalMap-FingerTree.html>
newtype Sieve x =
Sieve { unSieve :: FingerTree (Measure x) (Interval x) }
instance (Ord x, Show x) => Show (Pretty (Sieve x)) where
show (Pretty s) | Data.Interval.Sieve.null s = "empty"
show (Pretty s) = Data.List.intercalate " u " $ Data.List.map (show . Pretty) $ intervals s
-- | Like 'Data.Functor.fmap' but working on 'Interval's.
fmap_interval
:: (Ord x, Ord y)
=> (Interval x -> Interval y) -> Sieve x -> Sieve y
fmap_interval f (Sieve ft) = Sieve (FT.fmap' f ft)
-- | Like 'Data.Functor.fmap' but working on 'Interval's,
-- and safe only if 'Measure' is preserved.
fmap_interval_unsafe
:: Ord x
=> (Interval x -> Interval x)
-> Sieve x -> Sieve x
fmap_interval_unsafe f (Sieve ft) = Sieve (FT.unsafeFmap f ft)
-- | Like 'Data.Traversable.traverse' but working on 'Interval's.
traverse_interval
:: (Ord x, Ord y, Applicative f)
=> (Interval x -> f (Interval y))
-> Sieve x -> f (Sieve y)
traverse_interval f (Sieve ft) = Sieve <$> FT.traverse' f ft
-- | Like 'Data.Traversable.traverse' but working on 'Interval's,
-- and safe only if 'Measure' is preserved.
traverse_interval_unsafe
:: (Ord x, Applicative f)
=> (Interval x -> f (Interval x))
-> Sieve x -> f (Sieve x)
traverse_interval_unsafe f (Sieve ft) = Sieve <$> FT.unsafeTraverse f ft
-- | 'FT.Measure' of each leaf or node of the 'Sieve' 'FingerTree'.
data Measure x
= Measure_Empty -- ^ Measure of 'FT.empty' 'Fingertree'.
| Measure
{ max_high_of_max_low :: Interval x
-- ^ An __'Interval' with the max 'high' 'Limit'__
-- __amongst those having the max 'low' 'Limit'__
-- (which is the 'max' 'Interval'
-- because of lexicographical ordering).
{- NOTE: not useful in the particular case of '..<..'-ordered 'Interval's
, max_high :: Interval x
-- ^ An __'Interval' with the max 'high' 'Limit'__
-- (which may be a different 'Interval'
-- as it can have a lower 'low' 'Limit',
-- and thus not be the 'max' 'Interval').
-}
}
instance Ord x => Monoid (Measure x) where
mempty = Measure_Empty
Measure_Empty `mappend` i = i
i `mappend` Measure_Empty = i
_i `mappend` j = Measure (max_high_of_max_low j)
{- NOTE: not useful in the particular case of '..<..'-ordered 'Interval's
i `mappend` j =
Measure (max_high_of_max_low j) $
case compare_without_adherence (high (max_high i)) (high (max_high j)) of
LT -> max_high j
EQ ->
case (adherence (high (max_high i)), adherence (high (max_high j))) of
(In , In) -> max_high i
(In , Out) -> max_high i
(Out, In) -> max_high j
(Out, Out) -> max_high i
GT -> max_high i
-}
instance Ord x => FT.Measured (Measure x) (Interval x) where
measure = Measure
empty :: Ord x => Sieve x
empty = Sieve FT.empty
-- | Return the 'True' iif. the given 'Sieve' is 'empty'.
null :: Ord x => Sieve x -> Bool
null (Sieve ft) = FT.null ft
singleton :: Ord x => Interval x -> Sieve x
singleton = Sieve . FT.singleton
-- | Return an 'Interval' with:
--
-- * the 'Interval.low' 'Interval.Limit' of the 'min' 'Interval',
-- * the 'Interval.high' 'Interval.Limit' of the 'max' 'Interval'.
interval :: Ord x => Sieve x -> Maybe (Interval x)
interval (Sieve ft) =
case FT.viewl ft of
FT.EmptyL -> Nothing
-- l :< ls | FT.null ls -> Just l
l :< _ ->
case FT.viewr ft of
FT.EmptyR -> Nothing
_ :> r -> Just $ Interval (low l, high r)
-- | All the 'Interval's of the 'Sieve' in '..<<..' order.
intervals :: Ord x => Sieve x -> [Interval x]
intervals (Sieve t) = Foldable.toList t
-- * Union
-- | Return a 'Sieve' merging the given 'Sieve's with 'Interval.union'.
union :: Ord x => Sieve x -> Sieve x -> Sieve x
union (Sieve s0) (Sieve s1) =
Sieve (merge s0 s1)
where
merge is js =
case FT.viewl is of
FT.EmptyL -> js
i :< gt_i ->
let (js_not_away_lt_i, js_away_gt_i ) = FT.split ( (i ..<<..) . max_high_of_max_low) js in
let (js_away_lt_i , js_not_away_i) = FT.split (not . (..<<.. i) . max_high_of_max_low) js_not_away_lt_i in
js_away_lt_i ><
-- NOTE: flip merge when possible
-- (i.e. when high i is majoring high-s of intersecting Interval-s)
-- to preserve complexity over commutativity.
case FT.viewl js_not_away_i of
FT.EmptyL -> i <| merge js_away_gt_i gt_i
lu :< us ->
case FT.viewr us of
FT.EmptyR ->
let u = lu in
case position i u of
(Adjacent, LT) -> merge gt_i (Interval (low i, high u) <| js_away_gt_i) -- flip: NO
(Overlap , LT) -> merge gt_i (Interval (low i, high u) <| js_away_gt_i) -- flip: NO
(Prefix , LT) -> merge gt_i (Interval (low i, high u) <| js_away_gt_i) -- flip: NO
(Include , GT) -> merge gt_i (u <| js_away_gt_i) -- flip: NO
(Suffixed, LT) -> i <| merge js_away_gt_i gt_i -- flip: YES
(Include , LT) -> i <| merge js_away_gt_i gt_i -- flip: YES
(Equal , _ ) -> i <| merge js_away_gt_i gt_i -- flip: YES
(Adjacent, GT) -> Interval (low u, high i) <| merge js_away_gt_i gt_i -- flip: YES
(Overlap , GT) -> Interval (low u, high i) <| merge js_away_gt_i gt_i -- flip: YES
(Prefix , GT) -> Interval (low u, high i) <| merge js_away_gt_i gt_i -- flip: YES
(Suffixed, GT) -> Interval (low u, high i) <| merge js_away_gt_i gt_i -- flip: YES
_ -> assert False undefined
_ :> hu ->
let l = low $
case position i lu of
(Adjacent, GT) -> lu
(Overlap , GT) -> lu
(Prefix , GT) -> lu
(Suffixed, GT) -> lu
_ -> i in
case position i hu of
(Adjacent, LT) -> merge gt_i (Interval (l, high hu) <| js_away_gt_i) -- flip: NO
(Overlap , LT) -> merge gt_i (Interval (l, high hu) <| js_away_gt_i) -- flip: NO
(Prefix , LT) -> merge gt_i (Interval (l, high hu) <| js_away_gt_i) -- flip: NO
_ -> Interval (l, high i) <| merge js_away_gt_i gt_i -- flip: YES
-- | Return a 'Sieve' merging the 'Interval's in the given 'Foldable' with 'Interval.union'.
from_Foldable :: (Foldable f, Ord x) => f (Interval x) -> Sieve x
from_Foldable = Foldable.foldr (union . singleton) empty
-- * Intersection
-- | Return a 'Sieve' merging the given 'Sieve's with 'Interval.intersection'.
intersection :: Ord x => Sieve x -> Sieve x -> Sieve x
intersection (Sieve s0) (Sieve s1) =
Sieve (merge s0 s1)
where
intersect i j = fromMaybe (assert False undefined) $ Interval.intersection i j
merge is js =
case FT.viewl is of
FT.EmptyL -> FT.empty
i :< gt_i ->
let (_, js_not_lt_i) = FT.split (not . (..<.. i) . max_high_of_max_low) js in
let (js_intersecting_i, _) = FT.split ( (i ..<..) . max_high_of_max_low) js_not_lt_i in
case FT.viewl js_intersecting_i of
li :< li' ->
intersect li i <|
case FT.viewr li' of
hi' :> hi -> hi' |> intersect i hi
_ -> li'
>< merge js_not_lt_i gt_i
_ -> merge js_not_lt_i gt_i
-- NOTE: swap merging to preserve complexity over commutativity
-- | All 'Interval's having a non-'Nothing' 'Interval.intersection' with the given 'Interval',
-- in '..<<..' order.
intersecting :: Ord x => Interval x -> Sieve x -> [Interval x]
intersecting i = Foldable.toList . unSieve . intersection (singleton i)
-- * Complement
-- | Return the 'Sieve' spanning over all the values not within the given 'Sieve'.
complement :: (Ord x, Bounded (Interval.Limit x)) => Sieve x -> Sieve x
complement = complement_with (Interval (minBound, maxBound))
-- | Return the 'Sieve' spanning over all the values not within the given 'Sieve',
-- but within the given 'Interval' which MUST be 'Interval.onto' every 'Interval' inside the 'Sieve'.
complement_with :: Ord x => Interval x -> Sieve x -> Sieve x
complement_with b (Sieve s) =
let (last_low, c) =
Foldable.foldr
(\i (previous_low, ft) ->
( low i
, if (Interval.HH $ high i) < (Interval.HH $ high b)
then Interval (flip_limit $ high i, flip_limit previous_low) <| ft
else ft
))
(flip_limit $ high b, FT.empty) s in
Sieve $
case compare (Interval.LL $ low b) (Interval.LL last_low) of
LT -> Interval (low b, flip_limit last_low) <| c
EQ | low b == high b && FT.null s -> FT.singleton b
_ -> c