{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -funbox-strict-fields #-}
{-# OPTIONS_HADDOCK hide #-}
module Data.Range
where
import Data.Array.Accelerate.Error
import Prelude hiding ( take, splitAt )
import GHC.Base ( quotInt )
import Text.Printf
import Data.Sequence ( Seq )
import qualified Data.Sequence as Seq
data Range
= Empty
| IE !Int !Int
deriving Eq
instance Show Range where
show Empty = "empty"
show (IE u v)
| u == pred v = printf "singleton %d" u
| otherwise = printf "[%d...%d]" u (pred v)
{-# INLINE empty #-}
empty :: Range
empty = Empty
{-# INLINE null #-}
null :: Range -> Bool
null Empty = True
null _ = False
{-# INLINE singleton #-}
singleton :: Int -> Range
singleton !a = IE a (succ a)
{-# INLINE (...) #-}
(...) :: Int -> Int -> Range
u ... v
| u <= v = IE u (succ v)
| otherwise = Empty
infix 3 ...
{-# INLINE size #-}
size :: Range -> Int
size range =
case range of
Empty -> 0
IE u v -> v - u
{-# INLINE bisect #-}
bisect :: Range -> (Range, Range)
bisect range =
case range of
Empty -> (Empty, Empty)
IE u v ->
let n = size range
m = (n + 1) `quotInt` 2
o = u+m
x = IE u o
y | o < v = IE o v
| otherwise = Empty
in
(x, y)
{-# INLINE take #-}
take :: Int -> Range -> Range
take !n !_ | n <= 0 = Empty
take !n !range =
case range of
Empty -> Empty
IE u v -> IE u ((u+n) `min` v)
{-# INLINE splitAt #-}
splitAt :: Int -> Range -> (Range, Range)
splitAt !n !range | n <= 0 = (Empty, range)
splitAt !n !range =
case range of
Empty -> (Empty, Empty)
IE u v ->
let m = u+n
x = IE u (m `min` v)
y | m < v = IE m v
| otherwise = Empty
in
(x, y)
{-# INLINE merge #-}
merge :: Range -> Range -> Maybe Range
merge (IE u v) (IE x y)
| v == x = Just (IE u y)
| otherwise = Nothing
merge _ _ = $internalError "merge" "empty range encountered"
{-# INLINEABLE append #-}
append :: Seq Range -> Range -> Seq Range
append rs Empty = rs
append rs next =
case Seq.viewr rs of
Seq.EmptyR -> Seq.singleton next
rs' Seq.:> prev
| Just r <- merge prev next -> rs' Seq.|> r
| otherwise -> rs Seq.|> next
{-# INLINEABLE compress #-}
compress :: Seq Range -> Seq Range
compress = squash . Seq.unstableSortBy cmp
where
cmp (IE u _) (IE v _) = compare u v
cmp _ _ = $internalError "compress" "empty range encountered"
squash rrs =
case Seq.viewl rrs of
Seq.EmptyL -> Seq.empty
r1 Seq.:< rs -> case Seq.viewl rs of
Seq.EmptyL -> rrs
r2 Seq.:< rs'
| Just r12 <- merge r1 r2 -> squash $ r12 Seq.<| rs'
| otherwise -> r1 Seq.<| squash rs