packages feed

interval-patterns 0.2.0.1 → 0.3.0.0

raw patch · 6 files changed

+151/−126 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Interval: imapS :: (Ord x, Ord y) => (Levitated x -> Levitated y) -> Interval x -> Interval y
- Data.Interval: itraverseS :: (Ord x, Ord y, Applicative f) => (Levitated x -> f (Levitated y)) -> Interval x -> f (Interval y)
- Data.Timeframe: Calendar :: Map ev (Event n) -> Calendar ev n
- Data.Timeframe: [getCalendar] :: Calendar ev n -> Map ev (Event n)
- Data.Timeframe: addEvent :: (Ord ev, Num n) => ev -> Event n -> Calendar ev n -> Calendar ev n
- Data.Timeframe: calendar :: (Ord ev, Num n) => ev -> Timeframe -> Calendar ev n
- Data.Timeframe: event :: Num n => Timeframe -> Event n
- Data.Timeframe: instance (GHC.Classes.Eq ev, GHC.Classes.Eq n) => GHC.Classes.Eq (Data.Timeframe.Calendar ev n)
- Data.Timeframe: instance (GHC.Classes.Ord ev, GHC.Classes.Ord n) => GHC.Classes.Ord (Data.Timeframe.Calendar ev n)
- Data.Timeframe: instance (GHC.Classes.Ord ev, GHC.Num.Num n) => GHC.Base.Monoid (Data.Timeframe.Calendar ev n)
- Data.Timeframe: instance (GHC.Classes.Ord ev, GHC.Num.Num n) => GHC.Base.Semigroup (Data.Timeframe.Calendar ev n)
- Data.Timeframe: instance (GHC.Show.Show ev, GHC.Show.Show n) => GHC.Show.Show (Data.Timeframe.Calendar ev n)
- Data.Timeframe: newtype Calendar ev n
- Data.Timeframe: singleton :: (Ord ev, Num n) => ev -> Event n -> Calendar ev n
- Data.Timeframe: totalDuration :: forall ev n. (Ord ev, Real n) => ev -> Calendar ev n -> Maybe NominalDiffTime
- Data.Timeframe: type Event n = Layers UTCTime (Sum n)
+ Data.Interval: (...) :: Ord x => (Levitated x, Extremum) -> (Levitated x, Extremum) -> Interval x
+ Data.Interval: imapLev :: (Ord x, Ord y) => (Levitated x -> Levitated y) -> Interval x -> Interval y
+ Data.Interval: isSubsetOf :: Ord x => Interval x -> Interval x -> Bool
+ Data.Interval: itraverseLev :: (Ord x, Ord y, Applicative f) => (Levitated x -> f (Levitated y)) -> Interval x -> f (Interval y)
+ Data.Interval.Borel: isSubsetOf :: Ord x => Borel x -> Borel x -> Bool
+ Data.Interval.Layers: clip :: (Ord x, Semigroup y) => Interval x -> Layers x y -> Layers x y
+ Data.Interval.Layers: difference :: (Ord x, Group y) => Layers x y -> Layers x y -> Layers x y
+ Data.Interval.Layers: toStepFunction :: (Ord x, Monoid y) => Layers x y -> [(Levitated x, y)]
- Data.Interval: pattern (:|-|:) :: Ord x => Levitated x -> Levitated x -> Interval x
+ Data.Interval: pattern (:---:) :: forall x. Ord x => Levitated x -> Levitated x -> Interval x

Files

CHANGELOG.md view
@@ -1,5 +1,20 @@ # Revision history for interval-patterns +## 0.3.0.0 - 2022-06-08++* new pattern synonym `(:---:) :: (Ord x) => Levitated x -> Levitated x -> Interval x`+  * only works as a pattern; cannot be used as an expression+  * disregards the open/closedness of the boundary+* fix `openclosed` and `closedopen`+* rename `imapS -> imapLev` and `itraverseS -> itraverseLev` following `Suspension -> Levitated`+* `(...)` as a value-level analogue to `interval`+* `Data.Interval.isSubsetOf`+* `Data.Interval.Borel.isSubsetOf`+* `Data.Interval.Layers.difference`+* `Data.Interval.Layers.clip`+* `Data.Interval.Layers.toStepFunction`+* `Data.Calendar.happeningAt`+ ## 0.2.0.1 - 2022-05-30  * export `clip` from `Data.Interval.Borel`
interval-patterns.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: interval-patterns-version: 0.2.0.1+version: 0.3.0.0 author: Melanie Brown description: A library for easy manipulation of intervals according to their overlap. maintainer: brown.m@pm.me
src/Data/Interval.hs view
@@ -16,18 +16,21 @@   oppose,   Interval (..),   imap,-  imapS,+  imapLev,   itraverse,-  itraverseS,+  itraverseLev,   pattern (:<->:),   pattern (:<-|:),   pattern (:|->:),   pattern (:|-|:),+  pattern (:---:),   pattern (:<>:),   pattern (:<|:),   pattern (:|>:),   pattern (:||:),   pattern Whole,+  (+/-),+  (...),   bounds,   lower,   lowerBound,@@ -66,7 +69,7 @@   measure,   measuring,   hausdorff,-  (+/-),+  isSubsetOf, ) where  import Algebra.Lattice.Levitated@@ -281,12 +284,12 @@   l :|-|: u -> fmap f l :|-|: fmap f u  -- | Same as 'imap' but on the 'Levitated' of the underlying type.-imapS ::+imapLev ::   (Ord x, Ord y) =>   (Levitated x -> Levitated y) ->   Interval x ->   Interval y-imapS f = \case+imapLev f = \case   l :<->: u -> f l :<->: f u   l :|->: u -> f l :|->: f u   l :<-|: u -> f l :<-|: f u@@ -306,12 +309,12 @@   l :|-|: u -> liftA2 (:|-|:) (traverse f l) (traverse f u)  -- | Same as 'itraverse' but on the 'Levitated' of the underlying type.-itraverseS ::+itraverseLev ::   (Ord x, Ord y, Applicative f) =>   (Levitated x -> f (Levitated y)) ->   Interval x ->   f (Interval y)-itraverseS f = \case+itraverseLev f = \case   l :<->: u -> liftA2 (:<->:) (f l) (f u)   l :|->: u -> liftA2 (:|->:) (f l) (f u)   l :<-|: u -> liftA2 (:<-|:) (f l) (f u)@@ -372,6 +375,11 @@  {-# COMPLETE (:<->:), (:<-|:), (:|->:), (:|-|:) #-} +pattern (:---:) :: forall x. (Ord x) => Levitated x -> Levitated x -> Interval x+pattern l :---: u <- (bounds -> (SomeBound (unBound -> l), SomeBound (unBound -> u)))++{-# COMPLETE (:---:) #-}+ infix 5 :<>:  infix 5 :<|:@@ -414,7 +422,7 @@           sup = Levitate (max b1 b2)        in case compare inf sup of             EQ -> Min inf :|--|: Max sup-            _ -> Inf inf :<--|: Max sup+            _ -> Min inf :|-->: Sup sup  -- | A pattern synonym matching finite closed intervals. pattern (:||:) :: forall x. (Ord x) => x -> x -> Interval x@@ -484,6 +492,26 @@   (Max u, Inf l) -> l :<-|: u   _ -> error "cannot make an interval with the given bounds" +-- | Given limits and 'Extremum's, try to make an interval.+(...) ::+  (Ord x) =>+  (Levitated x, Extremum) ->+  (Levitated x, Extremum) ->+  Interval x+(x, b1) ... (y, b2) = case (b1, b2) of+  (Minimum, Supremum) -> l :|->: u+  (Minimum, Maximum) -> l :|-|: u+  (Infimum, Supremum) -> l :<->: u+  (Infimum, Maximum) -> l :<-|: u+  (Supremum, Minimum) -> l :|->: u+  (Supremum, Infimum) -> l :<->: u+  (Maximum, Minimum) -> l :|-|: u+  (Maximum, Infimum) -> l :<-|: u+  _ -> error "cannot make an interval with the given bounds"+ where+  l = min x y+  u = max x y+ -- | According to -- [Allen](https://en.wikipedia.org/wiki/Allen%27s_interval_algebra), -- two intervals can be "adjacent" in 13 different ways,@@ -544,79 +572,49 @@  -- | Test whether a point is contained in the interval. within :: (Ord x) => x -> Interval x -> Bool-within (Levitate -> x) = \case-  l :<->: u -> l < x && x < u-  l :<-|: u -> l < x && x <= u-  l :|->: u -> l <= x && x < u-  l :|-|: u -> l <= x && x <= u+within (Levitate -> x) (l :---: u) = l < x && x < u  -- | Create the closed-closed interval at a given point. point :: (Ord x) => x -> Interval x point = join (:||:) +-- | Get the infimum of an interval, weakening if necessary.+iinf :: (Ord x) => Interval x -> Bound Infimum (Levitated x)+iinf (x :---: _) = Inf x+ -- | Get the minimum of an interval, if it exists. imin :: (Ord x) => Interval x -> Maybe (Bound Minimum (Levitated x)) imin = \case-  (_ :<-->: _) -> Nothing-  (_ :<--|: _) -> Nothing   (x :|-->: _) -> Just x   (x :|--|: _) -> Just x---- | Get the infimum of an interval, weakening if necessary.-iinf :: (Ord x) => Interval x -> Bound Infimum (Levitated x)-iinf = \case-  (x :<->: _) -> Inf x-  (x :<-|: _) -> Inf x-  (x :|->: _) -> Inf x-  (x :|-|: _) -> Inf x---- | Get the supremum of an interval, weakening if necessary.-isup :: (Ord x) => Interval x -> Bound Supremum (Levitated x)-isup = \case-  (_ :<->: x) -> Sup x-  (_ :<-|: x) -> Sup x-  (_ :|->: x) -> Sup x-  (_ :|-|: x) -> Sup x+  _ -> Nothing  -- | Get the maximum of an interval if it exists. imax :: (Ord x) => Interval x -> Maybe (Bound Maximum (Levitated x)) imax = \case-  (_ :<-->: _) -> Nothing   (_ :<--|: x) -> Just x-  (_ :|-->: _) -> Nothing   (_ :|--|: x) -> Just x+  _ -> Nothing +-- | Get the supremum of an interval, weakening if necessary.+isup :: (Ord x) => Interval x -> Bound Supremum (Levitated x)+isup (_ :---: x) = Sup x+ -- | Open both bounds of the given interval. open :: (Ord x) => Interval x -> Interval x-open = \case-  l :<->: u -> l :<->: u-  l :<-|: u -> l :<->: u-  l :|->: u -> l :<->: u-  l :|-|: u -> l :<->: u+open (l :---: u) = l :<->: u  -- | Close both bounds of the given interval. close :: (Ord x) => Interval x -> Interval x-close = \case-  l :<->: u -> l :|-|: u-  l :<-|: u -> l :|-|: u-  l :|->: u -> l :|-|: u-  l :|-|: u -> l :|-|: u+close (l :---: u) = l :|-|: u  -- | Make the interval open-closed, leaving the endpoints unchanged. openclosed :: (Ord x) => Interval x -> Interval x-openclosed = \case-  l :<->: u -> l :<->: u-  l :<-|: u -> l :<->: u-  l :|->: u -> l :<->: u-  l :|-|: u -> l :<->: u+openclosed (l :---: u) = l :<-|: u  -- | Make the interval closed-open, leaving the endpoints unchanged. closedopen :: (Ord x) => Interval x -> Interval x-closedopen = \case-  l :<->: u -> l :|-|: u-  l :<-|: u -> l :|-|: u-  l :|->: u -> l :|-|: u-  l :|-|: u -> l :|-|: u+closedopen (l :---: u) = l :|->: u  -- | Make the lower bound open, leaving the endpoints unchanged. openLower :: (Ord x) => Interval x -> Interval x@@ -822,6 +820,16 @@ -- Just (Two (Bottom :|-|: Levitate 3) (Levitate 4 :|-|: Top)) -- -- @+--+-- Note that infinitely-open intervals will return the points at infinity+-- toward which they are infinite in their result:+--+-- @+--+-- >>> complement (Levitate 3 :<->: Top)+-- Just (Two (Bottom :|-|: Levitate 3) (Top :|-|: Top))+--+-- @ complement :: forall x. (Ord x) => Interval x -> Maybe (OneOrTwo (Interval x)) complement = \case   Whole -> Nothing@@ -933,14 +941,8 @@ measuring ::   forall y x. (Ord x, Num y) => (x -> x -> y) -> Interval x -> Maybe y measuring f = \case-  Levitate l :|-|: Levitate u -> Just (f l u)-  Levitate l :|->: Levitate u -> Just (f l u)-  Levitate l :<-|: Levitate u -> Just (f l u)-  Levitate l :<->: Levitate u -> Just (f l u)-  l :|-|: u -> if l == u then Just 0 else Nothing-  l :|->: u -> if l == u then Just 0 else Nothing-  l :<-|: u -> if l == u then Just 0 else Nothing-  l :<->: u -> if l == u then Just 0 else Nothing+  Levitate l :---: Levitate u -> Just (f l u)+  l :---: u -> if l == u then Just 0 else Nothing  -- | Get the distance between two intervals, or 0 if they adjacency. --@@ -966,3 +968,20 @@ -- For the open interval, simply write @'open' (x '+/-' y)@. (+/-) :: (Ord x, Num x) => x -> x -> Interval x m +/- r = m - r :||: m + r++-- | Full containment.+isSubsetOf :: (Ord x) => Interval x -> Interval x -> Bool+isSubsetOf i j = case adjacency i j of+  Before{} -> False+  Meets{} -> False+  Overlaps{} -> False+  Starts{} -> True+  During{} -> True+  Finishes{} -> True+  Identical{} -> True+  FinishedBy{} -> False+  Contains{} -> False+  StartedBy{} -> False+  OverlappedBy{} -> False+  MetBy{} -> False+  After{} -> False
src/Data/Interval/Borel.hs view
@@ -19,6 +19,7 @@   intersection,   intersections,   hull,+  isSubsetOf, ) where  import Algebra.Heyting@@ -165,3 +166,6 @@ hull (Borel is)   | Set.null is = Nothing   | otherwise = Just $ uncurry (foldr I.hull) (Set.deleteFindMin is)++isSubsetOf :: (Ord x) => Borel x -> Borel x -> Bool+isSubsetOf is js = difference is js == mempty
src/Data/Interval/Layers.hs view
@@ -10,14 +10,18 @@   thickest,   remove,   baseline,+  difference,+  clip,+  toStepFunction,    -- ** Helper functions   nestings,   nestingsAsc, ) where +import Algebra.Lattice.Levitated import Data.Group (Group (..))-import Data.Interval (Adjacency (..), Interval, pattern Whole, pattern (:<>:))+import Data.Interval (Adjacency (..), Interval, pattern Whole, pattern (:---:), pattern (:<>:)) import Data.Interval qualified as I import Data.Interval.Borel (Borel) import Data.Interval.Borel qualified as Borel@@ -68,6 +72,39 @@ squash :: (Ord x) => Layers x y -> Borel x squash (Layers s) = foldMap Borel.singleton (Map.keys s) +-- | @insert ix y l@ draws over @l@ a rectangle with base @ix@ of thickness @y@.+insert ::+  (Ord x, Semigroup y) =>+  Interval x ->+  y ->+  Layers x y ->+  Layers x y+insert ix y = (<>) (singleton ix y)++-- | Take away a thickness over a given base from the 'Layers'.+remove :: (Ord x, Group y) => y -> Interval x -> Layers x y -> Layers x y+remove y ix = insert ix (invert y)++-- | Add the given thickness to every point.+baseline :: (Ord x, Semigroup y) => y -> Layers x y -> Layers x y+baseline = insert Whole++-- | "Excavate" the second argument from the first.+difference :: (Ord x, Group y) => Layers x y -> Layers x y -> Layers x y+difference layers (Layers s) =+  foldr (uncurry (flip remove)) layers (Map.toAscList s)++-- | Restrict the range of the 'Layers' to the given 'Interval'.+clip :: (Ord x, Semigroup y) => Interval x -> Layers x y -> Layers x y+clip ix (Layers s) =+  Map.foldlWithKey'+    ( \acc jx y -> case I.intersect ix jx of+        Nothing -> acc+        Just x -> insert x y acc+    )+    empty+    s+ -- | Get the thickness of the 'Layers' at a point. thickness :: (Ord x, Monoid y) => x -> Layers x y -> y thickness x (Layers s) = case Map.lookupLE (x :<>: x) s of@@ -85,14 +122,19 @@     Nothing     s --- | @insert ix y l@ draws over @l@ a rectangle with base @ix@ of thickness @y@.-insert ::-  (Ord x, Semigroup y) =>-  Interval x ->-  y ->-  Layers x y ->-  Layers x y-insert ix y = mappend (singleton ix y)+-- | Convert the 'Layers' into a list of beginning-points and heights,+-- that define a step function piecewise.+toStepFunction :: (Ord x, Monoid y) => Layers x y -> [(Levitated x, y)]+toStepFunction s = g (Data.Interval.Layers.toList $ baseline mempty s)+ where+  g [(il :---: iu, iy), (j@(jl :---: Top), jy)]+    | iu == jl = (il, iy) : g [(j, jy)]+    | otherwise = (il, iy) : (iu, mempty) : g [(j, jy)]+  g ((il :---: iu, iy) : (j@(jl :---: _), jy) : is)+    | iu == jl = (il, iy) : g ((j, jy) : is)+    | otherwise = (il, iy) : (iu, mempty) : g ((j, jy) : is)+  g [] = []+  g [(il :---: iu, iy)] = [(il, iy), (iu, mempty)]  nestings ::   (Ord x, Semigroup y) =>@@ -148,11 +190,3 @@     MetBy i j k -> (i, iy) : nestingsAsc ((j, iy <> jy) : (k, jy) : js)     After i j -> (i, iy) : nestingsAsc ((j, jy) : js)   x -> x---- | Take away a thickness over a given base from the 'Layers'.-remove :: (Ord x, Group y) => y -> Interval x -> Layers x y -> Layers x y-remove y ix = insert ix (invert y)---- | Add the given thickness to every point.-baseline :: (Ord x, Semigroup y) => y -> Layers x y -> Layers x y-baseline = insert Whole
src/Data/Timeframe.hs view
@@ -5,19 +5,9 @@   localTimeframe,   pureLocalTimeframe,   duration,-  Event,-  event,-  Calendar (..),-  singleton,-  calendar,-  addEvent,-  totalDuration, ) where  import Data.Interval-import Data.Interval.Layers (Layers)-import Data.Interval.Layers qualified as Layers-import Data.Map.Strict qualified as Map import Data.Time.Compat import GHC.IO (unsafePerformIO) @@ -38,40 +28,3 @@  duration :: Timeframe -> Maybe NominalDiffTime duration = measuring diffUTCTime---- | An 'Event' is something that happens for a period of time.------ > type Event n = Layers UTCTime (Sum n)-type Event n = Layers UTCTime (Sum n)--event :: (Num n) => Timeframe -> Event n-event = (`Layers.singleton` 1)--newtype Calendar ev n = Calendar {getCalendar :: Map ev (Event n)}-  deriving (Eq, Ord, Show, Typeable)--instance (Ord ev, Num n) => Semigroup (Calendar ev n) where-  Calendar a <> Calendar b = Calendar (Map.unionWith (<>) a b)--instance (Ord ev, Num n) => Monoid (Calendar ev n) where-  mempty = Calendar mempty--singleton :: (Ord ev, Num n) => ev -> Event n -> Calendar ev n-singleton ev cvg = Calendar (Map.singleton ev cvg)--calendar :: (Ord ev, Num n) => ev -> Timeframe -> Calendar ev n-calendar ev tf = singleton ev (Layers.singleton tf 1)--addEvent :: (Ord ev, Num n) => ev -> Event n -> Calendar ev n -> Calendar ev n-addEvent ev cvg (Calendar c) = Calendar (Map.insertWith (<>) ev cvg c)--totalDuration :: forall ev n. (Ord ev, Real n) => ev -> Calendar ev n -> Maybe NominalDiffTime-totalDuration ev (Calendar c) = case c Map.!? ev of-  Nothing -> Just 0-  Just is -> foldr f (Just 0) (Layers.toList is)- where-  f :: (Timeframe, Sum n) -> Maybe NominalDiffTime -> Maybe NominalDiffTime-  f _ Nothing = Nothing-  f (tf, Sum n) (Just x) = case (realToFrac n *) <$> duration tf of-    Nothing -> Nothing-    Just y -> Just (x + y)