1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE MultiParamTypeClasses #-}
4 {-# LANGUAGE TupleSections #-}
5 module Hcompta.Lib.Interval where
7 import Control.DeepSeq (NFData(..))
9 import Data.Data (Data(..))
10 import Data.Foldable (concat)
11 import Data.Functor (Functor(..))
12 import qualified Data.Functor
13 import Data.Maybe (Maybe(..))
14 import Data.Ord (Ord(..), Ordering(..))
16 import Data.Typeable (Typeable)
17 import Prelude (($), Bounded(..), Eq(..), Show(..), flip, seq)
23 { adherence :: Adherence
25 deriving (Eq, Data, Show, Typeable)
27 instance Functor Limit where
28 fmap f (Limit a x) = Limit a (f x)
29 instance NFData x => NFData (Limit x) where
30 rnf (Limit _a l) = rnf l
32 data Adherence = Out | In
33 deriving (Eq, Data, Show, Typeable)
35 -- | Return given 'Limit' with its 'adherence' set to the opposite one.
36 flip_limit :: Limit x -> Limit x
37 flip_limit (Limit a x) = Limit (case a of { In -> Out; Out -> In }) x
39 -- ** Comparing 'Limit's
41 -- | Compare two 'low' 'Limit's.
42 newtype LL x = LL { unLL :: x }
44 instance Ord x => Ord (LL (Limit x)) where
45 compare (LL x) (LL y) =
46 case compare (limit x) (limit y) of
48 case (adherence x, adherence y) of
54 -- | Compare two 'high' 'Limit's.
55 newtype HH x = HH { unHH :: x }
57 instance Ord x => Ord (HH (Limit x)) where
58 compare (HH x) (HH y) =
59 case compare (limit x) (limit y) of
61 case (adherence x, adherence y) of
71 = Interval (Limit x, Limit x)
72 deriving (Eq, Show, Data, Typeable)
73 instance (NFData x, Ord x) => NFData (Interval x) where
74 rnf (Interval (x, y)) = rnf x `seq` rnf y
76 low :: Ord x => Interval x -> Limit x
77 low (Interval t) = fst t
79 high :: Ord x => Interval x -> Limit x
80 high (Interval t) = snd t
82 -- | Return 'Interval' with given 'low' then 'high' 'Limit's,
83 -- if they form a valid 'Interval'.
84 interval :: Ord x => Limit x -> Limit x -> Maybe (Interval x)
86 case compare_without_adherence x y of
87 LT -> Just $ Interval (x, y)
89 case (adherence x, adherence y) of
90 (In, In) -> Just $ Interval (x, y)
94 -- | Like 'Data.Functor.fmap', but may return 'Nothing', if mapped 'Interval' is not valid.
95 fmap :: (Ord x, Ord y) => (x -> y) -> Interval x -> Maybe (Interval y)
96 fmap f (Interval (il, ih)) = interval (Data.Functor.fmap f il) (Data.Functor.fmap f ih)
98 -- | Like 'Data.Functor.fmap', but only safe if given map preserves 'Ordering'.
99 fmap_unsafe :: (Ord x, Ord y) => (x -> y) -> Interval x -> Interval y
100 fmap_unsafe f (Interval (il, ih)) = Interval (Data.Functor.fmap f il, Data.Functor.fmap f ih)
103 -- | Like 'Data.Functor.fmap', but on 'Limit's,
104 -- and may return 'Nothing', if mapped 'Interval' is not valid.
105 fmap_limits :: (Ord x, Ord y) => (Limit x -> Limit y) -> Interval x -> Maybe (Interval y)
106 fmap_limits f (Interval (il, ih)) = interval (f il) (f ih)
108 -- | Like 'Data.Functor.fmap', but on 'Limit's
109 -- and only safe if given map preserves 'Ordering'.
110 fmap_limits_unsafe :: (Ord x, Ord y) => (Limit x -> Limit y) -> Interval x -> Interval y
111 fmap_limits_unsafe f (Interval (il, ih)) = Interval (f il, f ih)
114 -- | Lexicographical order, handling 'Adherence' correctly.
115 instance Ord x => Ord (Interval x) where
116 compare (Interval (il, ih)) (Interval (jl, jh)) =
117 case compare (LL il) (LL jl) of
118 EQ -> compare (HH ih) (HH jh)
121 -- | Return 'limit's of given 'Interval' as a tuple.
122 limits :: Ord x => Interval x -> (Limit x, Limit x)
123 limits (Interval t) = t
125 -- | Return an 'Interval' spanning over a single 'limit'.
126 point :: Ord x => x -> Interval x
127 point x = Interval (Limit In x, Limit In x)
129 -- | Return given 'Interval' with 'flip_limit' applied to its 'limit's.
130 flip_limits :: Ord x => Interval x -> Interval x
131 flip_limits (Interval (l, h)) = Interval (flip_limit l, flip_limit h)
133 -- | Return 'Ordering' comparing given 'Interval's according to their 'limit's.
134 compare_without_adherence :: Ord x => Limit x -> Limit x -> Ordering
135 compare_without_adherence (Limit _ x) (Limit _ y) = compare x y
139 -- * 'LT': if given value is lower than all values in given 'Interval'.
140 -- * 'EQ': if given value is into the given 'Interval'.
141 -- * 'GT': if given value is higher than all values in given 'Interval'.
142 locate :: Ord x => x -> Interval x -> Ordering
143 locate x (Interval (l, h)) =
144 case compare x (limit l) of
146 EQ | adherence l == In -> EQ
149 case compare x (limit h) of
151 EQ | adherence h == In -> EQ
155 -- | Return 'True' iif. given value is into the given 'Interval'.
156 within :: Ord x => x -> Interval x -> Bool
157 within x i = locate x i == EQ
159 -- | Return 'True' iif. every value of the first 'Interval' is into the second 'Interval'.
160 into :: Ord x => Interval x -> Interval x -> Bool
163 (Prefix , LT) -> True
164 (Suffixed, GT) -> True
165 (Include , GT) -> True
169 -- | Return 'True' iif. every value of the second 'Interval' is into the first 'Interval'.
170 onto :: Ord x => Interval x -> Interval x -> Bool
174 (<=..<=) :: Ord x => x -> x -> Maybe (Interval x)
177 LT -> Just $ Interval (Limit In x, Limit In y)
178 EQ -> Just $ Interval (Limit In x, Limit In y)
182 (<..<=) :: Ord x => x -> x -> Maybe (Interval x)
185 LT -> Just $ Interval (Limit Out x, Limit In y)
190 (<=..<) :: Ord x => x -> x -> Maybe (Interval x)
193 LT -> Just $ Interval (Limit In x, Limit Out y)
198 (<..<) :: Ord x => x -> x -> Maybe (Interval x)
201 LT -> Just $ Interval (Limit Out x, Limit Out y)
208 = Away -- ^ @-_|@ ('LT') or @|_-@ ('GT')
209 | Adjacent -- ^ @-|@ ('LT') or @|-@ ('GT')
210 | Overlap -- ^ @-+|@ ('LT') or @|+-@ ('GT')
211 | Prefix -- ^ @+|@ ('LT') or @+-@ ('GT')
212 | Suffixed -- ^ @-+@ ('LT') or @|+@ ('GT')
213 | Include -- ^ @-+-@ ('LT') or @|+|@ ('GT')
214 | Equal -- ^ @+@ ('EQ')
217 position :: Ord x => Interval x -> Interval x -> (Position, Ordering)
218 position (Interval (il, ih)) (Interval (jl, jh)) =
219 case compare (LL il) (LL jl) of
221 case compare_without_adherence ih jl of
222 LT -> Away -- PATTERN: -_|
224 case (adherence ih, adherence jl) of
225 (In , In) -> Overlap -- PATTERN: -+|
226 (Out, Out) -> Away -- PATTERN: -_|
227 _ -> Adjacent -- PATTERN: -|
229 case compare (HH ih) (HH jh) of
230 LT -> Overlap -- PATTERN: -+|
231 EQ -> Suffixed -- PATTERN: -+
232 GT -> Include -- PATTERN: -+-
234 case compare (HH ih) (HH jh) of
235 LT -> (Prefix, LT) -- PATTERN: +|
236 EQ -> (Equal , EQ) -- PATTERN: +
237 GT -> (Prefix, GT) -- PATTERN: +-
239 case compare_without_adherence il jh of
241 case compare (HH ih) (HH jh) of
242 LT -> Include -- PATTERN: |+|
243 EQ -> Suffixed -- PATTERN: |+
244 GT -> Overlap -- PATTERN: |+-
246 case (adherence il, adherence jh) of
247 (In , In) -> Overlap -- PATTERN: |+-
248 (Out, Out) -> Away -- PATTERN: |_-
249 _ -> Adjacent -- PATTERN: |-
250 GT -> Away -- PATTERN: |_-
253 -- | Return 'True' iif. 'Position' of given 'Interval's is ('Away', 'LT').
254 (..<<..) :: Ord x => Interval x -> Interval x -> Bool
255 (..<<..) i j = case position i j of
260 -- | Return 'True' iif. 'Position' of given 'Interval's is ('Away', 'GT').
261 (..>>..) :: Ord x => Interval x -> Interval x -> Bool
262 (..>>..) i j = case position i j of
267 -- | Return 'True' iif. 'Position' of given 'Interval's is ('Away', 'LT') or ('Adjacent', 'LT').
268 (..<..) :: Ord x => Interval x -> Interval x -> Bool
269 (..<..) i j = case position i j of
271 (Adjacent, LT) -> True
274 -- | Return 'True' iif. 'Position' of given 'Interval's is ('Away', 'GT') or ('Adjacent', 'GT').
275 (..>..) :: Ord x => Interval x -> Interval x -> Bool
276 (..>..) i j = case position i j of
278 (Adjacent, GT) -> True
282 -- | Return 'True' iif. 'Position' of given 'Interval's is ('Away', 'LT'), ('Adjacent', 'LT'), ('Overlap', 'LT'), ('Prefix', 'LT'), ('Suffixed', 'LT'), ('Include', 'GT'), or ('Equal', _).
283 (..<=..) :: Ord x => Interval x -> Interval x -> Bool
284 (..<=..) i j = case position i j of
286 (Adjacent, LT) -> True
287 (Overlap , LT) -> True
288 (Prefix , LT) -> True
289 (Suffixed, LT) -> True
290 (Include , GT) -> True
295 -- | Return 'True' iif. 'Position' of given 'Interval's is ('Away', 'GT'), ('Adjacent', 'GT'), ('Overlap', 'GT'), ('Prefix', 'GT'), ('Suffixed', 'GT'), ('Include', 'LT'), or ('Equal', _).
296 (..>=..) :: Ord x => Interval x -> Interval x -> Bool
297 (..>=..) i j = case position i j of
299 (Adjacent, GT) -> True
300 (Overlap , GT) -> True
301 (Prefix , GT) -> True
302 (Suffixed, GT) -> True
303 (Include , LT) -> True
309 union :: Ord x => Interval x -> Interval x -> Maybe (Interval x)
312 (Away, _) -> -- PATTERN: -_| or |_-
316 LT -> Just $ Interval (low i, high j) -- PATTERN: -|
318 GT -> Just $ Interval (low j, high i) -- PATTERN: |-
321 LT -> Just $ Interval (low i, high j) -- PATTERN: -+|
323 GT -> Just $ Interval (low j, high i) -- PATTERN: |+-
326 LT -> Just $ j -- PATTERN: +|
328 GT -> Just $ i -- PATTERN: +-
331 LT -> Just $ i -- PATTERN: -+
333 GT -> Just $ j -- PATTERN: |+
336 LT -> Just $ i -- PATTERN: -+-
338 GT -> Just $ j -- PATTERN: |+|
339 (Equal, _) -> -- PATTERN: +
342 intersection :: Ord x => Interval x -> Interval x -> Maybe (Interval x)
345 (Away, _) -> -- PATTERN: -_| or |_-
347 (Adjacent, _) -> -- PATTERN: -| or |-
351 LT -> Just $ Interval (low j, high i) -- PATTERN: -+|
353 GT -> Just $ Interval (low i, high j) -- PATTERN: |+-
356 LT -> Just $ i -- PATTERN: +|
358 GT -> Just $ j -- PATTERN: +-
361 LT -> Just $ j -- PATTERN: -+
363 GT -> Just $ i -- PATTERN: |+
366 LT -> Just $ j -- PATTERN: -+-
368 GT -> Just $ i -- PATTERN: |+|
369 (Equal, _) -> -- PATTERN: +
372 span :: Ord x => Interval x -> Interval x -> Interval x
375 ( unLL (min (LL $ low i) (LL $ low j))
376 , unHH (max (HH $ high i) (HH $ high j))
379 -- * Type 'Unlimitable'
383 | Limited { limited :: x }
385 deriving (Eq, Ord, Show)
386 instance Functor Unlimitable where
387 fmap _f Unlimited_low = Unlimited_low
388 fmap _f Unlimited_high = Unlimited_high
389 fmap f (Limited x) = Limited (f x)
390 instance Bounded (Unlimitable x) where
391 minBound = Unlimited_low
392 maxBound = Unlimited_high
393 instance Bounded (Limit (Unlimitable x)) where
394 minBound = Limit In Unlimited_low
395 maxBound = Limit In Unlimited_high
397 unlimited :: Ord x => Interval (Unlimitable x)
398 unlimited = Interval ( Limit In Unlimited_low
399 , Limit In Unlimited_high )
401 unlimit :: Ord x => Interval x -> Interval (Unlimitable x)
402 unlimit = fmap_unsafe Limited
404 (<..) :: Ord x => x -> Interval (Unlimitable x)
405 (<..) x = Interval (Limit Out (Limited x), Limit In Unlimited_high)
407 (<=..) :: Ord x => x -> Interval (Unlimitable x)
408 (<=..) x = Interval (Limit In (Limited x), Limit In Unlimited_high)
410 (..<) :: Ord x => x -> Interval (Unlimitable x)
411 (..<) x = Interval (Limit In Unlimited_low, Limit Out (Limited x))
413 (..<=) :: Ord x => x -> Interval (Unlimitable x)
414 (..<=) x = Interval (Limit In Unlimited_low, Limit In (Limited x))
418 newtype Pretty x = Pretty x
420 instance (Ord x, Show x) => Show (Pretty (Interval x)) where
423 [ case adherence (low i) of
426 , show (limit $ low i)
428 , show (limit $ high i)
429 , case adherence (high i) of
433 instance (Ord x, Show x) => Show (Pretty (Unlimitable x)) where
436 Unlimited_low -> "-oo"
438 Unlimited_high -> "+oo"