{-|
    Invariants: Count >= 0, End >= Start, Start >= 0

    Start/End are 0-based indicies

    Count may be maxBound::Int!

    The rangeStartCount and rangeStartEnd methods ensure the invariants,
    modifying the range if necessary
-}

module Data.Range(
    Range,
    rangeStart, rangeCount, rangeEnd,
    rangeStartCount, rangeStartEnd,
    listRange, mergeRanges
    ) where

import Data.List
import General.Code


data Range = Range {rangeStart :: Int, rangeCount :: Int}


instance Show Range where
    show r = "Range {rangeStart = " ++ show (rangeStart r) ++
                  ", rangeCount = " ++ show (rangeCount r) ++
                  ", rangeEnd = " ++ show (rangeEnd r) ++ "}"


nat = max (0::Int)


rangeEnd :: Range -> Int
rangeEnd r = rangeStart r + rangeCount r - 1


rangeStartCount :: Int -> Int -> Range
rangeStartCount start count = Range (nat start) (nat count)


rangeStartEnd :: Int -> Int -> Range
rangeStartEnd start end = Range start2 (nat $ end - start2 + 1)
    where start2 = nat start


listRange :: Range -> [a] -> [a]
listRange r = take (rangeCount r) . drop (rangeStart r)


-- make sure in the resultant range
-- end r_{n} < (start r_{n+1} - 1)
mergeRanges :: [Range] -> [Range]
mergeRanges = foldr f [] . sortOn rangeStart
    where
        f (Range s1 c1) (Range s2 c2 : rs) | s2 <= s1+c1 = r12 : rs
            where r12 = Range s1 (max c1 (s2-s1 + c2))
        f r rs = [r | rangeCount r > 0] ++ rs