Copyright | (c) Lackmann Phymetric |
---|---|
License | GPL-3 |
Maintainer | olaf.klinke@phymetric.de |
Stability | experimental |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
This module provides the two-parameter type class Interval
of types
that represent closed intervals (meaning the end-points are included)
possibly with some extra annotation.
This approach is shared by the Data.IntervalMap.Generic.Interval module of the
IntervalMap package.
A particular use case are time intervals annotated with event data.
The simplest example of an interval type i
with end points of type e
is the type i = (e,e)
.
The functions exported from this module are mainly concerned with overlap queries,
that is, to identify which intervals in a collection overlap a given interval
and if so, to what extent.
This functionality is encapsuled in the class IntersectionQuery
.
If the collection of intervals is known to overlap in end-points only,
one can simply use a sequence ordered by left end-point as the search structure.
For arbitrary collections we provide the ITree
structure
(centered interval tree) which stores intervals in subtrees and bins
that are annotated with their convex hull, so that it can be decided
easily whether there is an interval inside which overlaps a given interval.
The behaviour of the functions is undefined for intervals that violate the implicit assumption that the left end-point is less than or equal to the right end-point.
The functionality provided is similar to the Interval data type in the data-interval package but we focus on closed intervals and let the user decide which concrete data type to use.
Most functions are property-checked for correctness. Checks were implemented by Henning Thielemann.
Synopsis
- class Ord e => Interval e i | i -> e where
- class Foldable f => IntersectionQuery t e f | t -> f where
- getIntersects :: (Interval e i, Interval e j) => i -> t j -> f j
- getProperIntersects :: (Interval e i, Interval e j) => i -> t j -> f j
- someIntersects :: (Interval e i, Interval e j) => i -> t j -> Bool
- someProperlyIntersects :: (Interval e i, Interval e j) => i -> t j -> Bool
- class Interval e i => Adjust e i | i -> e where
- adjustBounds :: (e -> e) -> (e -> e) -> i -> i
- shift :: (e -> e) -> i -> i
- class TimeDifference t where
- diffTime :: t -> t -> NominalDiffTime
- addTime :: NominalDiffTime -> t -> t
- intersects :: (Interval e i, Interval e j) => i -> j -> Bool
- properlyIntersects :: (Interval e i, Interval e j) => i -> j -> Bool
- contains :: (Interval e i, Interval e j) => i -> j -> Bool
- properlyContains :: (Interval e i, Interval e j) => i -> j -> Bool
- covered :: (Interval e i, Interval e j, Adjust e j) => i -> Seq j -> Seq j
- coveredBy :: (Interval e i, Interval e j, Foldable f) => i -> f j -> Bool
- overlapTime :: (TimeDifference t, Interval t i, Interval t j) => i -> j -> NominalDiffTime
- prevailing :: (Interval t i, Interval t j, TimeDifference t) => i -> Seq (a, j) -> Maybe a
- fractionCovered :: (TimeDifference t, Interval t i, Interval t j, Fractional a) => j -> Seq i -> a
- overlap :: Interval e i => i -> i -> Ordering
- intervalDuration :: (TimeDifference t, Interval t i) => i -> NominalDiffTime
- maybeUnion :: (Interval e j, Interval e i, Adjust e i) => j -> i -> Maybe i
- maybeIntersection :: (Interval e j, Interval e i, Adjust e i) => j -> i -> Maybe i
- hull :: (Interval e i, Foldable f, Functor f) => f i -> Maybe (e, e)
- hullSeq :: Interval e i => Seq i -> Maybe (e, e)
- without :: (Adjust e i, Interval e j) => i -> j -> [i]
- contiguous :: Interval e i => [i] -> [[i]]
- components :: (Interval e i, Adjust e i) => [i] -> [i]
- componentsSeq :: (Interval e i, Adjust e i) => Seq i -> Seq i
- sortByLeft :: Interval e i => Seq i -> Seq i
- fromEndPoints :: Ord e => [e] -> Seq (e, e)
- data ITree e i
- itree :: Interval e i => Int -> Seq i -> ITree e i
- emptyITree :: ITree e i
- insert :: Interval e i => i -> ITree e i -> ITree e i
- hullOfTree :: Interval e i => ITree e i -> Maybe (e, e)
- intersecting :: (Interval e i, Interval e j) => j -> Seq i -> Seq i
- getIntersectsIT :: (Interval e i, Interval e j) => i -> ITree e j -> Seq j
- getProperIntersectsIT :: (Interval e i, Interval e j) => i -> ITree e j -> Seq j
- someIntersectsIT :: (Interval e i, Interval e j) => i -> ITree e j -> Bool
- someProperlyIntersectsIT :: (Interval e i, Interval e j) => i -> ITree e j -> Bool
- leftmostInterval :: Interval e i => ITree e i -> Maybe i
- findSeq :: (Interval e i, Interval e j) => (i -> (e, e) -> Bool) -> i -> Seq j -> Seq j
- existsSeq :: (Interval e i, Interval e j) => (i -> (e, e) -> Bool) -> i -> Seq j -> Bool
- hullSeqNonOverlap :: Interval e i => Seq i -> Maybe (e, e)
- invariant :: Interval e i => ITree e i -> Bool
- toTree :: Interval e i => ITree e i -> Tree (e, e)
- intersectingProperly :: (Interval e i, Interval e j) => j -> Seq i -> Seq i
- filterM :: (Applicative f, Traversable t, Alternative m) => (a -> f Bool) -> t a -> f (m a)
- joinSeq :: SplitSeq a -> Seq a
- splitSeq :: Seq a -> SplitSeq a
Type classes
class Ord e => Interval e i | i -> e where Source #
class of intervals with end points in a totally ordered type
:: i | |
-> e | lower bound |
:: i | |
-> e | upper bound |
:: i | |
-> (e, e) | end points (inclusive) |
class Foldable f => IntersectionQuery t e f | t -> f where Source #
class of search structures for interval intersection queries,
returning a Foldable
of intervals.
getIntersects :: (Interval e i, Interval e j) => i -> t j -> f j Source #
all intervalls touching the first one
getProperIntersects :: (Interval e i, Interval e j) => i -> t j -> f j Source #
all intervals properly intersecting the first one
someIntersects :: (Interval e i, Interval e j) => i -> t j -> Bool Source #
does any interval touch the first one?
someProperlyIntersects :: (Interval e i, Interval e j) => i -> t j -> Bool Source #
does any interval properly intersect the first one?
Instances
Ord e => IntersectionQuery Seq e Seq Source # | |
Defined in Data.Interval getIntersects :: (Interval e i, Interval e j) => i -> Seq j -> Seq j Source # getProperIntersects :: (Interval e i, Interval e j) => i -> Seq j -> Seq j Source # someIntersects :: (Interval e i, Interval e j) => i -> Seq j -> Bool Source # someProperlyIntersects :: (Interval e i, Interval e j) => i -> Seq j -> Bool Source # | |
Ord e => IntersectionQuery (ITree e) e Seq Source # | |
Defined in Data.Interval getIntersects :: (Interval e i, Interval e j) => i -> ITree e j -> Seq j Source # getProperIntersects :: (Interval e i, Interval e j) => i -> ITree e j -> Seq j Source # someIntersects :: (Interval e i, Interval e j) => i -> ITree e j -> Bool Source # someProperlyIntersects :: (Interval e i, Interval e j) => i -> ITree e j -> Bool Source # |
class Interval e i => Adjust e i | i -> e where Source #
class of Intervals whose bounds can be adjusted
:: (e -> e) | |
-> (e -> e) | |
-> i | |
-> i | adjust lower and upper bound |
:: (e -> e) | |
-> i | |
-> i | change both bounds using the same function |
class TimeDifference t where Source #
Time types supporting differences
diffTime :: t -> t -> NominalDiffTime Source #
addTime :: NominalDiffTime -> t -> t Source #
Instances
TimeDifference ZonedTime Source # |
|
Defined in Data.Interval | |
TimeDifference LocalTime Source # | |
Defined in Data.Interval | |
TimeDifference UTCTime Source # | |
Defined in Data.Interval |
Comparing intervals
intersects :: (Interval e i, Interval e j) => i -> j -> Bool Source #
intersection query.
>>>
((1,2)::(Int,Int)) `intersects` ((2,3)::(Int,Int))
True
genInterval /\* \i j -> (lb i <= ub i && lb j <= ub j && i `intersects` j) == (max (lb i) (lb j) <= min (ub i) (ub j))
properlyIntersects :: (Interval e i, Interval e j) => i -> j -> Bool Source #
proper intersection.
genInterval /\* \i j -> ((i `intersects` j) && not (i `properlyIntersects` j)) == (ub i == lb j || ub j == lb i)
contains :: (Interval e i, Interval e j) => i -> j -> Bool Source #
subset containment
genInterval /\ \i -> i `contains` i
genInterval /\* \i j -> (i `contains` j && j `contains` i) == (i==j)
genInterval /\* \i j -> i `contains` j == (maybeUnion i j == Just i)
properlyContains :: (Interval e i, Interval e j) => i -> j -> Bool Source #
proper subset containment
covered :: (Interval e i, Interval e j, Adjust e j) => i -> Seq j -> Seq j Source #
compute the components of the part of i
covered by the intervals.
genInterval /\ \i -> genIntervalSeq /\ \js -> all (contains i) (covered i js)
genInterval /\ \i -> genIntervalSeq /\ \js -> covered i (covered i js) == covered i js
coveredBy :: (Interval e i, Interval e j, Foldable f) => i -> f j -> Bool Source #
True
if the first interval is completely covered by the given intervals
genInterval /\* \i j -> j `contains` i == i `coveredBy` [j]
genInterval /\ \i -> genSortedIntervals /\ \js -> i `coveredBy` js ==> any (flip contains i) (components js)
overlapTime :: (TimeDifference t, Interval t i, Interval t j) => i -> j -> NominalDiffTime Source #
Find out the overlap of two time intervals.
genInterval /\ \i -> overlapTime i i == intervalDuration i
genInterval /\* \i j -> not (i `properlyIntersects` j) ==> overlapTime i j == 0
genInterval /\* \i j -> overlapTime i j == (sum $ fmap intervalDuration $ maybeIntersection i j)
prevailing :: (Interval t i, Interval t j, TimeDifference t) => i -> Seq (a, j) -> Maybe a Source #
Prevailing annotation in the first time interval
genInterval /\ \i c -> prevailing i (Seq.singleton (c,i)) == Just (c::Char)
genInterval /\ \i -> genLabeledSeq /\ \js -> isJust (prevailing i js) == any (intersects i . snd) js
genInterval /\ \i -> genLabeledSeq /\* \js ks -> all (flip elem $ catMaybes [prevailing i js, prevailing i ks]) $ prevailing i (js<>ks)
fractionCovered :: (TimeDifference t, Interval t i, Interval t j, Fractional a) => j -> Seq i -> a Source #
percentage of coverage of the first interval by the second sequence of intervals
genNonEmptyInterval /\ \i -> genIntervalSeq /\ \js -> i `coveredBy` js == (fractionCovered i js >= (1::Rational))
genNonEmptyInterval /\ \i -> genNonEmptyIntervalSeq /\ \js -> any (properlyIntersects i) js == (fractionCovered i js > (0::Rational))
intervalDuration :: (TimeDifference t, Interval t i) => i -> NominalDiffTime Source #
Operations on intervals
maybeUnion :: (Interval e j, Interval e i, Adjust e i) => j -> i -> Maybe i Source #
the union of two intervals is an interval if they intersect.
genInterval /\* \i j -> isJust (maybeUnion i j) ==> fromJust (maybeUnion i j) `contains` i && fromJust (maybeUnion i j) `contains` j
genInterval /\* \i j -> i `intersects` j ==> (maybeUnion i j >>= maybeIntersection i) == Just i
maybeIntersection :: (Interval e j, Interval e i, Adjust e i) => j -> i -> Maybe i Source #
the intersection of two intervals is either empty or an interval.
genInterval /\* \i j -> i `intersects` j ==> i `contains` fromJust (maybeIntersection i j)
hull :: (Interval e i, Foldable f, Functor f) => f i -> Maybe (e, e) Source #
convex hull
\xs -> isJust (hull xs) ==> all (\x -> fromJust (hull xs) `contains` x) (xs :: [(Int,Int)])
hullSeq :: Interval e i => Seq i -> Maybe (e, e) Source #
convex hull of a sorted sequence of intervals. the lower bound is guaranteed to be in the leftmost interval, but we have no guarantee of the upper bound.
genSortedIntervalSeq /\ \xs -> hullSeq xs == hull (toList xs)
without :: (Adjust e i, Interval e j) => i -> j -> [i] Source #
Set difference. The resulting list has zero, one or two elements.
>>>
without' (1,5) (4,5)
[(1,4)]>>>
without' (1,5) (2,3)
[(1,2),(3,5)]>>>
without' (1,5) (1,5)
[]>>>
without' (1,5) (0,1)
[(1,5)]
genInterval /\* \i j -> length (i `without` j) <= 2
genInterval /\ \i -> i `without` i == []
genInterval /\* \i j -> all (contains i) (i `without` j)
genInterval /\* \i j -> not $ any (properlyIntersects j) (i `without` j)
contiguous :: Interval e i => [i] -> [[i]] Source #
intersects
is not an equivalence relation, because it is not transitive.
Hence groupBy
intersects
does not do what one might expect.
This function does the expected and groups overlapping intervals
into contiguous blocks.
genSortedIntervals /\ all (\xs -> and $ List.zipWith intersects xs (tail xs)) . contiguous
components :: (Interval e i, Adjust e i) => [i] -> [i] Source #
Connected components of a list sorted by sortByLeft
,
akin to groupBy
intersects
.
The precondition is not checked.
genSortedIntervals /\ \xs -> all (\i -> any (flip contains i) (components xs)) xs
componentsSeq :: (Interval e i, Adjust e i) => Seq i -> Seq i Source #
same as components
. Is there a way to unify both?
genSortedIntervals /\ \xs -> componentsSeq (Seq.fromList xs) == Seq.fromList (components xs)
fromEndPoints :: Ord e => [e] -> Seq (e, e) Source #
construct a sorted sequence of intervals from a sorted sequence of bounds. Fails if the input sequence is not sorted.
genSortedList /\ \xs -> (components $ toList $ fromEndPoints xs) == if length xs < 2 then [] else [(head xs, last xs)]
Interval search tree
Search tree of intervals.
Instances
Functor (ITree e) Source # | |
Foldable (ITree e) Source # | |
Defined in Data.Interval fold :: Monoid m => ITree e m -> m # foldMap :: Monoid m => (a -> m) -> ITree e a -> m # foldMap' :: Monoid m => (a -> m) -> ITree e a -> m # foldr :: (a -> b -> b) -> b -> ITree e a -> b # foldr' :: (a -> b -> b) -> b -> ITree e a -> b # foldl :: (b -> a -> b) -> b -> ITree e a -> b # foldl' :: (b -> a -> b) -> b -> ITree e a -> b # foldr1 :: (a -> a -> a) -> ITree e a -> a # foldl1 :: (a -> a -> a) -> ITree e a -> a # elem :: Eq a => a -> ITree e a -> Bool # maximum :: Ord a => ITree e a -> a # minimum :: Ord a => ITree e a -> a # | |
Ord e => IntersectionQuery (ITree e) e Seq Source # | |
Defined in Data.Interval getIntersects :: (Interval e i, Interval e j) => i -> ITree e j -> Seq j Source # getProperIntersects :: (Interval e i, Interval e j) => i -> ITree e j -> Seq j Source # someIntersects :: (Interval e i, Interval e j) => i -> ITree e j -> Bool Source # someProperlyIntersects :: (Interval e i, Interval e j) => i -> ITree e j -> Bool Source # |
itree :: Interval e i => Int -> Seq i -> ITree e i Source #
Construct an interval tree with bins of maximal given size. The function first sorts the intervals, then splits into chunks of given size. The leftmost endpoints of the chunks define boundary points. Next, all intervals properly overlapping a boundary are removed from the chunks and kept separately. The chunks are arranged as the leaves of a binary search tree. Then the intervals overlapping boundaries are placed at internal nodes of the tree. Hence if all intervals are mutually non-overlapping properly, the resulting tree is a pure binary search tree with bins of given size as leaves.
emptyITree :: ITree e i Source #
the empty ITree
insert :: Interval e i => i -> ITree e i -> ITree e i Source #
insert the interval at the deepest possible location into the tree. Does not change the overall structure, in particular no re-balancing is performed.
hullOfTree :: Interval e i => ITree e i -> Maybe (e, e) Source #
smallest interval covering the entire tree. Nothing
if the tree is empty.
intersecting :: (Interval e i, Interval e j) => j -> Seq i -> Seq i Source #
extract all intervals intersecting a given one.
getIntersectsIT :: (Interval e i, Interval e j) => i -> ITree e j -> Seq j Source #
Intersection query. O(binsize+log(n/binsize)).
genInterval /\ \i -> genIntervalSeq /\ \t -> on (==) sortByLeft (getIntersectsIT i $ itree 2 t) (i `intersecting` t)
getProperIntersectsIT :: (Interval e i, Interval e j) => i -> ITree e j -> Seq j Source #
Intersection query. O(binsize+log(n/binsize)).
genInterval /\ \i -> genIntervalSeq /\ \t -> on (==) sortByLeft (getProperIntersectsIT i $ itree 2 t) (i `intersectingProperly` t)
someIntersectsIT :: (Interval e i, Interval e j) => i -> ITree e j -> Bool Source #
When the actual result of getIntersectsIT
is not important,
only whether there are intersections.
someProperlyIntersectsIT :: (Interval e i, Interval e j) => i -> ITree e j -> Bool Source #
When the actual result of getIntersectsIT
is not important,
only whether there are intersections.
leftmostInterval :: Interval e i => ITree e i -> Maybe i Source #
retrieve the left-most interval from the tree, or Nothing
if it is empty.
Non-overlapping intervals
hullSeqNonOverlap :: Interval e i => Seq i -> Maybe (e, e) Source #
O(1) bounds of an ordered sequence of intervals. Nothing
, if empty.
genDisjointIntervalSeq /\ \xs -> hullSeqNonOverlap xs == hullSeq xs
Debug
invariant :: Interval e i => ITree e i -> Bool Source #
invariant to be maintained for proper intersection queries
invariant . itree 4 . fmap (\(x,y) -> (x, x + QC.getNonNegative y :: Integer))
toTree :: Interval e i => ITree e i -> Tree (e, e) Source #
transform the interval tree into the tree of hulls
Testing
intersectingProperly :: (Interval e i, Interval e j) => j -> Seq i -> Seq i Source #
extract all intervals properly intersecting a given one.
filterM :: (Applicative f, Traversable t, Alternative m) => (a -> f Bool) -> t a -> f (m a) Source #
generalises Control.Monad.filterM