packages feed

typed-range-0.1.0.0: Data/Range/Typed/Ranges.hs

-- | This module provides a simpler interface than the 'Data.Range' module, allowing you to work with
-- multiple ranges at the same time.
--
-- One of the main advantages of this module is that it implements 'Monoid' for 'Ranges' which lets you
-- write code like:
module Data.Range.Typed.Ranges
  ( -- * Range creation
    (+=+),
    (+=*),
    (*=+),
    (*=*),
    lbi,
    lbe,
    ubi,
    ube,
    inf,

    -- * Comparison functions
    inRanges,
    aboveRanges,
    belowRanges,

    -- * Set operations
    union,
    intersection,
    difference,
    invert,

    -- * Enumerable methods
    fromRanges,
    joinRanges,

    -- * Data types
    Ranges (..),
  )
where

import qualified Data.Range.Typed as R

-- TODO Can we make this use a Range Algebra internally ?
newtype Ranges a = Ranges {unRanges :: [R.AnyRange a]}

instance (Show a) => Show (Ranges a) where
  showsPrec i (Ranges xs) = (<>) "Ranges " . showsPrec i xs

instance (Ord a) => Semigroup (Ranges a) where
  (<>) (Ranges a) (Ranges b) = Ranges . R.mergeRanges $ a <> b

instance (Ord a) => Monoid (Ranges a) where
  mempty = Ranges []
  mconcat = Ranges . R.mergeRanges . concatMap unRanges

instance Functor Ranges where
  fmap f (Ranges xs) = Ranges . fmap (fmap f) $ xs

(+=+) :: a -> a -> Ranges a
(+=+) a b = Ranges $ pure $ R.anyRange $ (R.+=+) a b

(+=*) :: a -> a -> Ranges a
(+=*) a b = Ranges $ pure $ R.anyRange $ (R.+=*) a b

(*=+) :: a -> a -> Ranges a
(*=+) a b = Ranges $ pure $ R.anyRange $ (R.*=+) a b

(*=*) :: a -> a -> Ranges a
(*=*) a b = Ranges $ pure $ R.anyRange $ (R.*=*) a b

lbi :: a -> Ranges a
lbi = Ranges . pure . R.anyRange . R.lbi

lbe :: a -> Ranges a
lbe = Ranges . pure . R.anyRange . R.lbe

ubi :: a -> Ranges a
ubi = Ranges . pure . R.anyRange . R.ubi

ube :: a -> Ranges a
ube = Ranges . pure . R.anyRange . R.ube

inf :: Ranges a
inf = Ranges [R.anyRange R.inf]

inRanges :: (Ord a) => Ranges a -> a -> Bool
inRanges (Ranges xs) = R.inRanges xs

-- | Checks if the value provided is above all of the ranges provided.
aboveRanges :: (Ord a) => Ranges a -> a -> Bool
aboveRanges (Ranges xs) = R.aboveRanges xs

-- | Checks if the value provided is below all of the ranges provided.
belowRanges :: (Ord a) => Ranges a -> a -> Bool
belowRanges (Ranges rs) = R.belowRanges rs

union :: (Ord a) => Ranges a -> Ranges a -> Ranges a
union (Ranges a) (Ranges b) = Ranges $ R.union a b

intersection :: (Ord a) => Ranges a -> Ranges a -> Ranges a
intersection (Ranges a) (Ranges b) = Ranges $ R.intersection a b

difference :: (Ord a) => Ranges a -> Ranges a -> Ranges a
difference (Ranges a) (Ranges b) = Ranges $ R.difference a b

invert :: (Ord a) => Ranges a -> Ranges a
invert = Ranges . R.invert . unRanges

fromRanges :: (Ord a, Enum a) => Ranges a -> [a]
fromRanges = R.fromRanges . unRanges

joinRanges :: (Ord a, Enum a) => Ranges a -> Ranges a
joinRanges = Ranges . R.joinRanges . unRanges