module Data.Time.Patterns.Internal(
IntervalSequence(..),
IntervalSequence',
never,
union,
diag,
take,
cycle,
stopAt,
stopAt',
before,
andThen,
every,
filter,
elementOf,
occurrencesFrom,
elementsFrom,
mapSequence,
skip,
skipUntil,
except,
except',
firstOccurrenceIn,
intersect,
elements
) where
import Numeric.Interval
import Numeric.Interval.Internal
import Data.Monoid (Monoid(..))
import Prelude hiding (cycle, elem, filter, take)
newtype IntervalSequence t s = IntervalSequence { nextInterval :: t -> Maybe (Interval s, IntervalSequence t s)}
type IntervalSequence' t = IntervalSequence t t
instance (Ord s) => Monoid (IntervalSequence t s) where
mappend = union
mempty = never
mapSequence :: (a -> b) -> IntervalSequence t a -> IntervalSequence t b
mapSequence f s =
IntervalSequence (\t ->
nextInterval s t >>= \t' ->
return (mapInterval f (fst t'),
mapSequence f (snd t')))
where mapInterval f' (I a b) = I (f' a) (f' b)
mapInterval _ Empty = Empty
never :: IntervalSequence t s
never = IntervalSequence $ const $ Nothing
union :: Ord s => IntervalSequence t s -> IntervalSequence t s -> IntervalSequence t s
union a b = IntervalSequence $ \d ->
case (nextInterval a d, nextInterval b d) of
(Nothing, Nothing) -> Nothing
(Nothing, b') -> b'
(a', Nothing) -> a'
(Just (ia, sa), Just (ib, sb)) ->
case (sup ia <= sup ib) of
True -> return (ia, union sa (ib `andThen` sb))
False -> return (ib, union (ia `andThen` sa) sb)
diag :: IntervalSequence t s -> IntervalSequence t s -> IntervalSequence t s
diag a b = IntervalSequence (nOcc' a b) where
nOcc' a' b' d = do
(na, sa) <- nextInterval a' d
return (na, diag b' sa)
take :: (Num i, Ord i) => i -> IntervalSequence t s -> IntervalSequence t s
take n IntervalSequence{..}
| n < 1 = never
| otherwise = IntervalSequence $ \d ->
nextInterval d >>= \r -> Just (fst r, take (n 1) $ snd r)
cycle :: Interval s -> IntervalSequence t s
cycle i = IntervalSequence $ const $ Just (i, cycle i)
stopAt :: Ord s => Interval s -> IntervalSequence t s -> IntervalSequence t s
stopAt p IntervalSequence{..} = IntervalSequence ni' where
ni' d = nextInterval d >>= \(p', q) -> case (p' `contains` p) of
True -> Nothing
False -> return (p', stopAt p q)
stopAt' :: Ord s => s -> IntervalSequence t s -> IntervalSequence t s
stopAt' p IntervalSequence{..} = IntervalSequence ni' where
ni' d = nextInterval d >>= \(p', q) -> case (sup p' >= p) of
True -> Nothing
False -> return (p', stopAt' p q)
before :: Ord s => Interval s -> IntervalSequence t s -> IntervalSequence t s
before p IntervalSequence{..} = IntervalSequence ni' where
ni' d = nextInterval d >>= \(p', q) -> case (p >=! p') of
False -> Nothing
True -> return (p', stopAt p q)
andThen :: Interval s -> IntervalSequence t s -> IntervalSequence t s
andThen i sq = IntervalSequence $ \_ -> Just (i, sq)
every :: (Num i, Ord i) => i -> IntervalSequence' t -> IntervalSequence' t
every n sq@IntervalSequence{..}
| n < 1 = never
| otherwise = IntervalSequence $ nextOcc 1
where
nextOcc n' d
| n' == n = nextInterval d >>= \s -> return (fst s, every n sq)
| otherwise = nextInterval d >>= nextOcc (n' + 1) . sup . fst
filter :: (Interval t -> Bool) -> IntervalSequence' t -> IntervalSequence' t
filter f IntervalSequence{..} = IntervalSequence nOcc' where
nOcc' t = nextInterval t >>= checkCondition
checkCondition (p,q) = case (f p) of
True -> Just (p, filter f q)
False -> nOcc' $ sup p
elementOf :: Ord t => t -> IntervalSequence' t -> Bool
elementOf t IntervalSequence{..} = maybe False (\(p,_) -> (elem t p) && (<) t (sup p)) (nextInterval t)
occurrencesFrom :: t -> IntervalSequence' t -> [Interval t]
occurrencesFrom start IntervalSequence{..} = case (nextInterval start) of
Nothing -> []
Just (res, sq') -> res : occurrencesFrom (sup res) sq'
elementsFrom :: Enum t => t -> IntervalSequence' t -> [t]
elementsFrom start sq = concat $ fmap elements $ occurrencesFrom start sq
skip :: (Num i, Ord i) => i -> IntervalSequence' t -> IntervalSequence' t
skip n sq
| n < 0 = never
| otherwise = IntervalSequence $ nextOcc (nextInterval sq) n
where
nextOcc ni n' d
| n' < 1 = ni d
| otherwise = ni d >>= \(p, q) -> nextOcc (nextInterval q) (n' 1) (sup p)
skipUntil :: Ord t => Interval t -> IntervalSequence' t -> IntervalSequence' t
skipUntil = stopAt' . inf
except :: (Enum t, Ord t) => t -> IntervalSequence' t -> IntervalSequence' t
except p = except' (p ... succ p)
except' :: Ord t => Interval t -> IntervalSequence' t -> IntervalSequence' t
except' p IntervalSequence{..} = IntervalSequence ni' where
ni' d = nextInterval d >>= \(p', q) -> case (p' `contains` p) of
False -> return (p', except' p q)
True -> ni' $ sup p
firstOccurrenceIn :: (Enum t, Ord t) => t -> Interval t -> IntervalSequence' t -> Maybe (Interval t, IntervalSequence' t)
firstOccurrenceIn s i IntervalSequence{..} = firstOcc s where
firstOcc start = do
(p, q) <- nextInterval start
case (i `contains` p) of
True -> return (p, q)
False -> case (sup p < sup i) of
True -> firstOcc $ sup p
False -> Nothing
intersect :: (Ord t, Enum t) => IntervalSequence' t -> IntervalSequence' t -> IntervalSequence' t
intersect a b = IntervalSequence (nOcc' a b) where
nOcc' a' b' d = do
(ia, sa) <- nextInterval a' d
(ib, sb) <- nextInterval b' $ inf ia
case ((sup ia == sup ib) && (inf ia == inf ib)) of
True -> return (ib, intersect sa sb)
False -> nOcc' b' sa $ sup ia
elements :: Enum a => Interval a -> [a]
elements i = enumFromTo (inf i) (pred $ sup i)