packages feed

rangeset-0.0.1.0: src/ghc/Data/RangeSet/Builders.hs

{-# LANGUAGE BangPatterns, ScopedTypeVariables, Safe #-}
module Data.RangeSet.Builders (fromRanges, fromDistinctAscRanges, insertRange, fromList, fromDistinctAscList) where

import Prelude hiding (id, (.))
import Data.RangeSet.Internal
import Data.RangeSet.Primitives

id :: SRangeList -> SRangeList
id ss = ss

(.) :: (SRangeList -> SRangeList) -> (SRangeList -> SRangeList) -> SRangeList -> SRangeList
(f . g) ss = f (g ss)

{-|
Constructs a `RangeSet` given a list of ranges.

@since 0.0.1.0
-}
fromRanges :: forall a. Enum a => [(a, a)] -> RangeSet a
fromRanges [] = Tip
fromRanges ((x, y):rs) = go rs ey (SRangeCons ex ey) 1
  where
    !ex = fromEnum x
    !ey = fromEnum y
    go :: [(a, a)] -> E -> (SRangeList -> SRangeList) -> Int -> RangeSet a
    go [] !_ k !n = fromDistinctAscRangesSz (k SNil) n
    go ((x, y):rs) z k n
      -- ordering and disjointness of the ranges broken
      | ex <= z || ey <= z = insertRangeE ex ey (foldr (uncurry insertRange) (fromDistinctAscRangesSz (k SNil) n) rs)
      | otherwise          = go rs ey (k . SRangeCons ex ey) (n + 1)
      where
        !ex = fromEnum x
        !ey = fromEnum y

{-|
Constructs a `RangeSet` given a list of ranges that are in ascending order and do not overlap (this is unchecked).

@since 0.0.1.0
-}
fromDistinctAscRanges :: forall a. Enum a => [(a, a)] -> RangeSet a
fromDistinctAscRanges rs = go rs id 0
  where
    go :: [(a, a)] -> (SRangeList -> SRangeList) -> Int -> RangeSet a
    go [] k !n = fromDistinctAscRangesSz (k SNil) n
    go ((x, y):rs) k n = go rs (k . SRangeCons (fromEnum x) (fromEnum y)) (n + 1)

{-|
Inserts a range into a `RangeSet`.

@since 0.0.1.0
-}
{-# INLINE insertRange #-}
insertRange :: Enum a => a -> a -> RangeSet a -> RangeSet a
insertRange l u t =
  let !le = fromEnum l
      !ue = fromEnum u
  in insertRangeE le ue t

{-|
Builds a `RangeSet` from a given list of elements.

@since 0.0.1.0
-}
fromList :: forall a. Enum a => [a] -> RangeSet a
fromList [] = Tip
fromList (x:xs) = go xs (fromEnum x) (fromEnum x) id 1
  where
    go :: [a] -> E -> E -> (SRangeList -> SRangeList) -> Int -> RangeSet a
    go [] !l !u k !n = fromDistinctAscRangesSz (k (SRangeCons l u SNil)) n
    go (!x:xs) l u k n
      -- ordering or uniqueness is broken
      | ex <= u      = insertE ex (foldr insert (fromDistinctAscRangesSz (k (SRangeCons l u SNil)) n) xs)
      -- the current range is expanded
      | ex == succ u = go xs l ex k n
      -- a new range begins
      | otherwise    = go xs ex ex (k . SRangeCons l u) (n + 1)
      where !ex = fromEnum x


-- not sure if this one is any use, it avoids one comparison per element...
{-|
Builds a `RangeSet` from a given list of elements that are in ascending order with no duplicates (this is unchecked).

@since 0.0.1.0
-}
fromDistinctAscList :: forall a. Enum a => [a] -> RangeSet a
fromDistinctAscList [] = Tip
fromDistinctAscList (x:xs) = go xs (fromEnum x) (fromEnum x) id 1
  where
    go :: [a] -> E -> E -> (SRangeList -> SRangeList) -> Int -> RangeSet a
    go [] !l !u k !n = fromDistinctAscRangesSz (k (SRangeCons l u SNil)) n
    go (!x:xs) l u k n
      -- the current range is expanded
      | ex == succ u = go xs l ex k n
      -- a new range begins
      | otherwise    = go xs ex ex (k . SRangeCons l u) (n + 1)
      where !ex = fromEnum x