hsc3-server-0.5.0: Sound/SC3/Server/Allocator/Range.hs
{-# LANGUAGE ScopedTypeVariables #-}
module Sound.SC3.Server.Allocator.Range (
Range
, range
, sized
, empty
, begin
, end
, last
, size
, null
, toList
-- *Interval operations
, within
, adjoins
, overlaps
, contains
, split
, join
) where
import Prelude hiding (last, null)
-- $setup
-- >>> import Control.Applicative ((<$>), (<*>))
-- >>> import Control.Monad (liftM)
-- >>> import System.Random (Random)
-- >>> import Test.QuickCheck (Arbitrary(..))
-- >>> instance (Arbitrary i, Ord i) => Arbitrary (Range i) where arbitrary = Range <$> arbitrary <*> arbitrary
-- | Open ended interval [begin, end).
data Range a = Range {
begin :: !a
, end :: !a
} deriving (Eq)
instance Show a => Show (Range a) where
show r = unwords ["Range", show (begin r), show (end r)]
mkRange :: a -> a -> Range a
mkRange a b = Range a b
-- | Construct a range from a lower bound (included) and an upper bound (excluded).
--
-- prop> \(r :: Range Int) -> begin r == end r
range :: Ord a => a -> a -> Range a
range a b | a <= b = mkRange a b
| otherwise = mkRange b a
-- | Construct a range from a size and a lower bound.
--
-- >>> sized 20 10
-- Range 10 30
sized :: Num a => Int -> a -> Range a
sized n a = mkRange a (a + fromIntegral n)
-- | The empty range starting at some value.
--
-- >>> empty 10
-- Range 10 10
--
-- null (empty 10)
-- True
--
-- size (empty 10)
-- 0
empty :: Num a => a -> Range a
empty a = mkRange a a
-- | The last value in the range.
--
-- >>> last (range 10 20)
-- 19
last :: Enum a => Range a -> a
last = pred . end
-- | The size of the range.
--
-- >>> size (range 10 20)
-- 10
--
-- >>> size (sized 100 10)
-- 100
size :: Integral a => Range a -> Int
size a = fromIntegral (end a - begin a)
-- | True if range is empty.
--
-- >>> null (range 10 10)
-- True
--
-- >>> null (range 10 20)
-- False
null :: Eq a => Range a -> Bool
null a = begin a == end a
-- | Convert range to a list of its values.
toList :: Enum a => Range a -> [a]
toList a = [begin a..last a]
-- | Return true if a given value is contained within the range.
--
-- >>> within 12 (sized 3 10)
-- True
--
-- >>> within 20 (range 10 20)
-- False
--
-- >>> within 30 (range 30 30)
-- False
within :: Ord a => a -> Range a -> Bool
x `within` a = x >= begin a && x < end a
-- | Return true if two ranges adjoin each other.
--
-- >>> range 10 20 `adjoins` range 20 30
-- True
--
-- >>> range 10 20 `adjoins` range 21 30
-- False
adjoins :: Eq a => Range a -> Range a -> Bool
a `adjoins` b = (end a == begin b) || (end b == begin a)
-- | Return true if two ranges overlap each other.
overlaps :: Ord a => Range a -> Range a -> Bool
a `overlaps` b = (end a > begin b) || (end b > begin a)
-- | Return true if the second range lies completely within the first range.
contains :: Ord a => Range a -> Range a -> Bool
a `contains` b = begin b >= begin a && end b <= end a
-- | Split a range at an index.
--
-- >>> let (r1, r2) = split 6 (range 10 20)
-- >>> size r1
-- 6
-- >>> size r2
-- 4
--
-- >>> let (r1, r2) = split 6 (empty 6)
-- >>> null r1 && null r2
-- True
--
-- >>> let (r1, r2) = split 10 (sized 4 10)
-- >>> size r1
-- 4
-- >>> size r2
-- 0
split :: Integral a => Int -> Range a -> (Range a, Range a)
split n r@(Range l u)
| n <= 0 = (empty l, r)
| n >= size r = (r, empty u)
| otherwise = (mkRange l (l+k), mkRange (l+k) u)
where k = fromIntegral n
join :: Ord a => Range a -> Range a -> Range a
join a b = mkRange (min (begin a) (begin b)) (max (end a) (end b))