packages feed

range 0.2.0.0 → 0.2.1.0

raw patch · 8 files changed

+298/−82 lines, 8 files

Files

Data/Range/Algebra.hs view
@@ -9,6 +9,27 @@ -- evaluate them all at once. This is not only useful for efficiency but for -- parsing too. Build up @RangeExpr@'s whenever you wish to perform multiple -- operations in a row, and evaluate it in one step to be as efficient as possible.+--+-- __Note:__ This module is based on F-Algebras to do much of the heavy conceptual+-- lifting. If you have never seen F-Algebras before then I highly recommend reading+-- through <https://www.schoolofhaskell.com/user/bartosz/understanding-algebras this introductory content>+-- from the School of Haskell.+--+-- == Examples+--+-- A simple example of using this module would look like this:+--+-- @+-- ghci> import qualified Data.Range.Algebra as A+-- ghci> (A.eval . A.invert $ A.const [SingletonRange 5]) :: [Range Integer]+-- [LowerBoundRange 6,UpperBoundRange 4]+-- (0.01 secs, 597,656 bytes)+-- ghci>+-- @+--+-- You can also use this module to evaluate range predicates.+--+-- module Data.Range.Algebra   ( RangeExpr     -- ** Operations@@ -50,6 +71,7 @@ -- of a range, so that a range expression of the same type can be evaluated, yielding -- that representation. class RangeAlgebra a where+  -- | This function is used to convert your built expressions into ranges.   eval :: Algebra RangeExpr a  -- | Multiple ranges represented by a list of disjoint ranges.
Data/Range/Algebra/Internal.hs view
@@ -49,6 +49,9 @@ newtype RangeExpr a = RangeExpr { getFree :: Free RangeExprF a }   deriving (Show, Eq, Functor) +-- | This is an F-Algebra. You don't need to know what this is in order to be able+-- to use this module, but, if you are interested you can+-- <https://www.schoolofhaskell.com/user/bartosz/understanding-algebras read more on School of Haskell>. type Algebra f a = f a -> a  rangeMergeAlgebra :: (Ord a, Enum a) => Algebra RangeExprF (RangeMerge a)
Data/Range/NestedRange.hs view
@@ -1,24 +1,69 @@ {-# LANGUAGE Safe #-}  -- | Nested Ranges are common in practical usage. They appear in such forms as library--- version numbers ("Version 1.4.5.6" for example). And it is very useful to be able to--- compare these ranges to one another. This module exists for the purpose of allowing--- these comparisons between nested ranges. The module builds upon the basic range concept--- from other parts of this library.-module Data.Range.NestedRange where+-- version numbers ("Version 1.4.5.6" for example).+--+-- It is very useful to be able to compare these ranges to one another. This module exists+-- for the purpose of allowing these comparisons between nested ranges. The module builds+-- upon the basic range concept from other parts of this library.+module Data.Range.NestedRange+  ( NestedRange(..)+  , fromVersion+  , inNestedRange+  ) where  import Data.Range.Range+import Data.Version  -- | The Nested Range is a structure that in a nested form of many ranges where there can -- be multiple ranges at every level.+--+-- For example, saying that you require a minimum version of 1.2.3 could be represented as:+--+-- @+-- NestedRange [[LowerBoundRange 1],[LowerBoundRange 2],[LowerBoundRange 3]]+-- @ data NestedRange a = NestedRange [[Range a]]-+  deriving(Eq, Show)  -- I wanted to know if a nested number of elements are in a given range. That way I can -- just immediately run a single function and tell things about ranges.  -- | Given a list of nested values and a nested range tell us wether the nested value -- exists inside the nested range.+--+-- == Examples+--+-- In a simple case:+--+-- @+-- ghci> inNestedRange [2, 8, 3] (NestedRange [[SpanRange 1 2]] :: NestedRange Integer)+-- True+-- (0.01 secs, 558,400 bytes)+-- ghci>+-- @+--+-- Not in the bounds:+--+-- @+-- ghci> inNestedRange [2, 8, 3] (NestedRange [[SpanRange 1 2], [UpperBoundRange 7]] :: NestedRange Integer)+-- False+-- (0.00 secs, 558,896 bytes)+-- ghci>+-- @+--+-- For something based on Data.Version:+--+-- @+-- ghci> version = Version [2, 8, 3] []+-- ghci> upperBound = Version [2, 7] []+-- ghci> inNestedRange (versionBranch version) (fromVersion UpperBoundRange upperBound)+-- False+-- ghci>+-- ghci> inNestedRange (versionBranch version) (fromVersion LowerBoundRange upperBound)+-- True+-- ghci>+-- @ inNestedRange :: Ord a => [a] -> NestedRange a -> Bool inNestedRange values (NestedRange ranges) = go values ranges    where@@ -27,3 +72,16 @@       go _  [] = True -- If you have already found the values you have to be in range then they are       go [] _  = False -- If you have not fully matched it yet then it is not in range.       go (value : vs) (range : rs) = inRanges range value && go vs rs++-- | This method converts the "Data.Version" datatype into a "NestedRange".+--+-- For example:+--+-- @+-- ghci> fromVersion LowerBoundRange (Version [1, 2, 3] [])+-- NestedRange [[LowerBoundRange 1],[LowerBoundRange 2],[LowerBoundRange 3]]+-- (0.01 secs, 624,736 bytes)+-- ghci>+-- @+fromVersion :: (Int -> Range Int) -> Version -> NestedRange Int+fromVersion bound = NestedRange . fmap (return . bound) . versionBranch
Data/Range/Parser.hs view
@@ -1,17 +1,30 @@ {-# LANGUAGE FlexibleContexts #-} --- | It should not be unexpected that you will be given a string representation of some--- ranges and you will need to parse them so that you can then do some further processing.--- This parser exists in order to make the most common forms of range strings easy to--- parse. It does not cover all cases however but you should not be too worried about--- that because you should be able to write your own parser using parsec or Alex/Happy and--- then you can convert everything that you parse into a RangeTree object for easier--- processing.-module Data.Range.Parser +-- | This package provides a simple range parser.+--+-- This range parser was designed to be a useful tool for CLI programs. For example, by+-- default, this example depicts how the parser works:+--+-- @+-- ghci> parseRanges "-5,8-10,13-15,20-" :: Either ParseError [Range Integer]+-- Right [UpperBoundRange 5,SpanRange 8 10,SpanRange 13 15,LowerBoundRange 20]+-- (0.01 secs, 681,792 bytes)+-- ghci>+-- @+--+-- And the * character translates to an infinite range. This is very useful for accepting+-- ranges as input in CLI programs, but not as useful for parsing .cabal or package.json files.+--+-- To handle more complex parsing cases it is recommended that you use the ranges library+-- in conjunction with parsec or Alex/Happy and convert the versions that you find into+-- ranges.+module Data.Range.Parser    ( parseRanges-   , ranges+   , customParseRanges    , RangeParserArgs(..)    , defaultArgs+   , ranges+   , ParseError    ) where  import Text.Parsec@@ -19,9 +32,8 @@  import Data.Range.Range --- | The arguments that are used, and can be modified, while parsing a standard range--- string.-data RangeParserArgs = Args +-- | These are the arguments that will be used when parsing a string as a range.+data RangeParserArgs = Args    { unionSeparator :: String -- ^ A separator that represents a union.    , rangeSeparator :: String -- ^ A separator that separates the two halves of a range.    , wildcardSymbol :: String -- ^ A separator that implies an unbounded range.@@ -30,28 +42,34 @@  -- | These are the default arguments that are used by the parser. Please feel free to use -- the default arguments for you own parser and modify it from the defaults at will.-defaultArgs :: RangeParserArgs +defaultArgs :: RangeParserArgs defaultArgs = Args    { unionSeparator = ","    , rangeSeparator = "-"    , wildcardSymbol = "*"    } --- | Given a string this function will either return a parse error back to the user or the--- list of ranges that are represented by the parsed string.+-- | Given a string, this function will either return a parse error back to the user or the+-- list of ranges that are represented by the parsed string. Very useful for CLI programs+-- that need to load ranges from a single-line string. parseRanges :: (Read a) => String -> Either ParseError [Range a] parseRanges = parse (ranges defaultArgs) "(range parser)" +-- | If you disagree with the default characters for separating ranges then this function can+-- be used to customise them, up to a point.+customParseRanges :: Read a => RangeParserArgs -> String -> Either ParseError [Range a]+customParseRanges args = parse (ranges args) "(range parser)"+ string_ :: Stream s m Char => String -> ParsecT s u m () string_ x = string x >> return () --- | Given the parser arguments this returns a parser that is capable of parsing a list of+-- | Given the parser arguments this returns a parsec parser that is capable of parsing a list of -- ranges. ranges :: (Read a) => RangeParserArgs -> Parser [Range a] ranges args = range `sepBy` (string $ unionSeparator args)-   where +   where       range :: (Read a) => Parser (Range a)-      range = choice +      range = choice          [ infiniteRange          , spanRange          , singletonRange
Data/Range/Range.hs view
@@ -1,17 +1,20 @@ {-# LANGUAGE Safe #-} --- | This entire library is concerned with ranges and this module implements the absolute--- basic range functions.+-- | This module provides a simple api to access range functionality. It provides standard+-- set operations on ranges, the ability to merge ranges together and, importantly, the ability+-- to check if a value is within a range.+--+-- __Note:__ It is intended that you will read the documentation in this module from top to bottom. module Data.Range.Range (       Range(..),       inRange,       inRanges,       rangesOverlap,       mergeRanges,-      invert,       union,       intersection,       difference,+      invert,       fromRanges    ) where @@ -21,23 +24,59 @@  -- | Performs a set union between the two input ranges and returns the resultant set of -- ranges.+--+-- For example:+--+-- @+-- ghci> union [SpanRange 1 10] [SpanRange 5 (15 :: Integer)]+-- [SpanRange 1 15]+-- (0.00 secs, 587,152 bytes)+-- ghci>+-- @ union :: (Ord a, Enum a) => [Range a] -> [Range a] -> [Range a] union a b = Alg.eval $ Alg.union (Alg.const a) (Alg.const b) {-# INLINE union #-}  -- | Performs a set intersection between the two input ranges and returns the resultant set of -- ranges.+--+-- For example:+--+-- @+-- ghci> intersection [SpanRange 1 10] [SpanRange 5 (15 :: Integer)]+-- [SpanRange 5 10]+-- (0.00 secs, 584,616 bytes)+-- ghci>+-- @ intersection :: (Ord a, Enum a) => [Range a] -> [Range a] -> [Range a] intersection a b = Alg.eval $ Alg.intersection (Alg.const a) (Alg.const b) {-# INLINE intersection #-}  -- | Performs a set difference between the two input ranges and returns the resultant set of -- ranges.+--+-- For example:+--+-- @+-- ghci> difference [SpanRange 1 10] [SpanRange 5 (15 :: Integer)]+-- [SpanRange 1 4]+-- (0.00 secs, 590,424 bytes)+-- ghci>+-- @ difference :: (Ord a, Enum a) => [Range a] -> [Range a] -> [Range a] difference a b = Alg.eval $ Alg.difference (Alg.const a) (Alg.const b) {-# INLINE difference #-}  -- | An inversion function, given a set of ranges it returns the inverse set of ranges.+--+-- For example:+--+-- @+-- ghci> invert [SpanRange 1 10, SpanRange 15 (20 :: Integer)]+-- [LowerBoundRange 21,UpperBoundRange 0,SpanRange 11 14]+-- (0.00 secs, 623,456 bytes)+-- ghci>+-- @ invert :: (Ord a, Enum a) => [Range a] -> [Range a] invert = Alg.eval . Alg.invert . Alg.const {-# INLINE invert #-}@@ -60,6 +99,24 @@  -- | Given a range and a value it will tell you wether or not the value is in the range. -- Remember that all ranges are inclusive.+--+-- The primary value of this library is performance and this method can be used to show+-- this quite clearly. For example, you can try and approximate basic range functionality+-- with "Data.List.elem" so we can generate an apples to apples comparison in GHCi:+--+-- @+-- ghci> :set +s+-- ghci> elem (10000000 :: Integer) [1..10000000]+-- True+-- (0.26 secs, 720,556,888 bytes)+-- ghci> inRange (SpanRange 1 10000000) (10000000 :: Integer)+-- True+-- (0.00 secs, 557,656 bytes)+-- ghci>+-- @+--+-- As you can see, this function is significantly more performant, in both speed and memory,+-- than using the elem function. inRange :: (Ord a) => Range a -> a -> Bool inRange (SingletonRange a) value = value == a inRange (SpanRange x y) value = isBetween value (x, y)@@ -72,30 +129,79 @@ inRanges :: (Ord a) => [Range a] -> a -> Bool inRanges rs a = any (`inRange` a) rs --- | When you create a range there may be overlaps in your ranges. However, for the sake--- of efficiency you probably want the list of ranges with no overlaps. The mergeRanges--- function takes a set of ranges and returns the same set specified by the minimum number--- of Range objects. A useful function for cleaning up your ranges. Please note that, if--- you use any of the other operations on sets of ranges like invert, union and--- intersection then this is automatically done for you. Which means that a function like--- this is redundant: mergeRanges . intersection+-- | An array of ranges may have overlaps; this function will collapse that array into as few+-- Ranges as possible. For example:+--+-- @+-- ghci> mergeRanges [LowerBoundRange 12, SpanRange 1 10, SpanRange 5 (15 :: Integer)]+-- [LowerBoundRange 1]+-- (0.01 secs, 588,968 bytes)+-- ghci>+-- @+--+-- As you can see, the mergeRanges method collapsed multiple ranges into a single range that+-- still covers the same surface area.+--+-- This may be useful for a few use cases:+--+--  * You are hyper concerned about performance and want to have the minimum number of ranges+--    for comparison in the inRanges function.+--  * You wish to display ranges to a human and want to show the minimum number of ranges to+--    avoid having to make people perform those calculations themselves.+--+-- Please note that the use of any of the operations on sets of ranges like invert, union and+-- intersection will have the same behaviour as mergeRanges as a side effect. So, for example,+-- this is redundant:+--+-- @+-- mergeRanges . intersection []+-- @ mergeRanges :: (Ord a, Enum a) => [Range a] -> [Range a]-mergeRanges = Alg.eval . Alg.const+mergeRanges = Alg.eval . Alg.union (Alg.const []) . Alg.const {-# INLINE mergeRanges #-} --- | A set of ranges represents a collection of real values without actually instantiating--- those values. This allows you to have infinite ranges. However, sometimes you wish to--- actually get the values that your range represents, or even get a sample set of the--- values. This function generates as many of the values that belong to your range as you--- like.+-- | Instantiate all of the values in a range.+--+-- __Warning__: This method is meant as a convenience method, it is not efficient.+--+-- A set of ranges represents a collection of real values without actually instantiating+-- those values. Not instantiating ranges, allows the range library to support infinite+-- ranges and be super performant.+--+-- However, sometimes you actually want to get the values that your range represents, or even+-- get a sample set of the values. This function generates as many of the values that belong+-- to your range as you like.+--+-- Because ranges can be infinite, it is highly recommended to combine this method with something like+-- "Data.List.take" to avoid an infinite recursion.+--+-- == Examples+--+-- A simple span:+--+-- @+-- ghci> take 5 . fromRanges $ [SpanRange 1 10 :: Range Integer]+-- [1,2,3,4,5]+-- (0.01 secs, 566,016 bytes)+-- ghci>+-- @+--+-- An infinite range:+--+-- @+-- ghci> take 5 . fromRanges $ [InfiniteRange :: Range Integer]+-- [0,1,-1,2,-2]+-- (0.00 secs, 566,752 bytes)+-- ghci>+-- @ fromRanges :: (Ord a, Enum a) => [Range a] -> [a]-fromRanges = concatMap fromRange+fromRanges = takeEvenly . fmap fromRange . mergeRanges    where       fromRange range = case range of          SingletonRange x -> [x]          SpanRange a b -> [a..b]          LowerBoundRange x -> iterate succ x          UpperBoundRange x -> iterate pred x-         InfiniteRange -> zero : takeEvenly (tail $ iterate succ zero) (tail $ iterate pred zero)+         InfiniteRange -> zero : takeEvenly [tail $ iterate succ zero, tail $ iterate pred zero]             where                zero = toEnum 0
Data/Range/RangeInternal.hs view
@@ -3,7 +3,6 @@ module Data.Range.RangeInternal where  import Data.Maybe (catMaybes)---import Data.Ord (comparing)  import Data.Range.Data import Data.Range.Spans@@ -14,7 +13,7 @@  - functions so that we can reason about what we are given.  -  - RangeMerge assumptions:- - * The span ranges will never overlap the bounds. + - * The span ranges will never overlap the bounds.  - * The span ranges are always sorted in ascending order by the first element.  - * The lower and upper bounds never overlap in such a way to make it an infinite range.  -}@@ -48,8 +47,8 @@ exportRangeMerge rm = putAll rm    where       putAll IRM = [InfiniteRange]-      putAll (RM lb up spans) = -         putLowerBound lb ++ putUpperBound up ++ putSpans spans+      putAll (RM lb up spans) =+         putUpperBound up ++ putSpans spans ++ putLowerBound lb        putLowerBound = maybe [] (return . LowerBoundRange)       putUpperBound = maybe [] (return . UpperBoundRange)@@ -64,7 +63,7 @@ intersectSpansRM :: (Ord a) => RangeMerge a -> RangeMerge a -> RangeMerge a intersectSpansRM one two = RM Nothing Nothing newSpans    where-      newSpans = intersectSpans (spanRanges one) (spanRanges two) +      newSpans = intersectSpans (spanRanges one) (spanRanges two)  intersectWith :: (Ord a) => (a -> (a, a) -> Maybe (a, a)) -> Maybe a -> [(a, a)] -> [(a, a)] intersectWith _ Nothing _ = []@@ -88,14 +87,14 @@    , largestUpperBound = newUpperBound    , spanRanges = joinedSpans    }-   where +   where       lowerOneSpans = intersectWith fixLower (largestLowerBound one) (spanRanges two)       lowerTwoSpans = intersectWith fixLower (largestLowerBound two) (spanRanges one)       upperOneSpans = intersectWith fixUpper (largestUpperBound one) (spanRanges two)       upperTwoSpans = intersectWith fixUpper (largestUpperBound two) (spanRanges one)-      intersectedSpans = intersectSpans (spanRanges one) (spanRanges two) +      intersectedSpans = intersectSpans (spanRanges one) (spanRanges two) -      sortedResults = foldr1 insertionSortSpans +      sortedResults = foldr1 insertionSortSpans          [ lowerOneSpans          , lowerTwoSpans          , upperOneSpans@@ -109,10 +108,10 @@       newLowerBound = calculateNewBound largestLowerBound max one two       newUpperBound = calculateNewBound largestUpperBound min one two -      calculateNewBound -         :: (Ord a) -         => (RangeMerge a -> Maybe a) -         -> (a -> a -> a) +      calculateNewBound+         :: (Ord a)+         => (RangeMerge a -> Maybe a)+         -> (a -> a -> a)          -> RangeMerge a -> RangeMerge a -> Maybe a       calculateNewBound ext comp one two = case (ext one, ext two) of          (Just x, Just y) -> Just $ comp x y@@ -123,17 +122,17 @@ calculateBoundOverlap one two = catMaybes [oneWay, secondWay]    where       oneWay = case (largestLowerBound one, largestUpperBound two) of-         (Just x, Just y) -> if y >= x +         (Just x, Just y) -> if y >= x             then Just (x, y)             else Nothing          _ -> Nothing        secondWay = case (largestLowerBound two, largestUpperBound one) of-         (Just x, Just y) -> if y >= x +         (Just x, Just y) -> if y >= x             then Just (x, y)             else Nothing          _ -> Nothing-      + unionRangeMerges :: (Ord a, Enum a) => RangeMerge a -> RangeMerge a -> RangeMerge a unionRangeMerges IRM _ = IRM unionRangeMerges _ IRM = IRM@@ -141,10 +140,10 @@    where       filterOne = foldr filterLowerBound boundedRM joinedSpans       filterTwo = foldr filterUpperBound (filterOne { spanRanges = [] }) (spanRanges filterOne)-      +       infiniteCheck :: (Ord a, Enum a) => RangeMerge a -> RangeMerge a       infiniteCheck IRM = IRM-      infiniteCheck rm@(RM (Just x) (Just y) _) = if x <= succ y +      infiniteCheck rm@(RM (Just x) (Just y) _) = if x <= succ y          then IRM          else rm       infiniteCheck rm = rm@@ -161,10 +160,10 @@          , spanRanges = []          } -      calculateNewBound -         :: (Ord a) -         => (RangeMerge a -> Maybe a) -         -> (a -> a -> a) +      calculateNewBound+         :: (Ord a)+         => (RangeMerge a -> Maybe a)+         -> (a -> a -> a)          -> RangeMerge a -> RangeMerge a -> Maybe a       calculateNewBound ext comp one two = case (ext one, ext two) of          (Just x, Just y) -> Just $ comp x y@@ -174,7 +173,7 @@ filterLowerBound :: (Ord a, Enum a) => (a, a) -> RangeMerge a -> RangeMerge a filterLowerBound _ IRM = IRM filterLowerBound a rm@(RM Nothing _ _) = rm { spanRanges = a : spanRanges rm }-filterLowerBound s@(lower, _) rm@(RM (Just lowestBound) _ _) = +filterLowerBound s@(lower, _) rm@(RM (Just lowestBound) _ _) =    case boundCmp lowestBound s of       GT -> rm { spanRanges = s : spanRanges rm }       LT -> rm@@ -196,7 +195,7 @@  appendSpanRM :: (Ord a, Enum a) => (a, a) -> RangeMerge a -> RangeMerge a appendSpanRM _ IRM = IRM-appendSpanRM sp@(lower, higher) rm = +appendSpanRM sp@(lower, higher) rm =    if (newUpper, newLower) == (lub, llb) && isLower lower newLower && (Just higher) > newUpper       then newRangesRM          { spanRanges = sp : spanRanges rm@@ -205,7 +204,7 @@          { spanRanges = spanRanges rm          }    where-      newRangesRM = rm +      newRangesRM = rm          { largestLowerBound = newLower          , largestUpperBound = newUpper          }@@ -257,7 +256,7 @@          Just upper -> [(succ upper, newUpperValue)]       lowerSpan = case largestLowerBound rm of          Nothing -> []-         Just lower -> [(newLowerValue, pred lower)] +         Just lower -> [(newLowerValue, pred lower)]        betweenSpans = invertSpans . spanRanges $ rm @@ -272,7 +271,7 @@ {- intersectSpansRM :: (Ord a) => RangeMerge a -> (a, a) -> [(a, a)] intersectSpansRM rm sp@(lower, upper) = intersectedSpans-   where +   where       spans = spanRanges rm       intersectedSpans = catMaybes $ map (intersectCompareSpan sp) spans 
Data/Range/Util.hs view
@@ -2,6 +2,8 @@  module Data.Range.Util where +import Data.Maybe (catMaybes)+ -- This module is supposed to contain all of the functions that are required by the rest -- of the code but could be easily pulled into separate and completely non-related -- codebases or libraries.@@ -9,7 +11,7 @@ insertionSort :: (Ord a) => (a -> a -> Ordering) -> [a] -> [a] -> [a] insertionSort comp xs ys = go xs ys    where-      go (f : fs) (s : ss) = case comp f s of +      go (f : fs) (s : ss) = case comp f s of          LT -> f : go fs (s : ss)          EQ -> f : s : go fs ss          GT -> s : go (f : fs) ss@@ -19,11 +21,14 @@ isBetween :: (Ord a) => a -> (a, a) -> Bool isBetween a (x, y) = (x <= a) && (a <= y) -takeEvenly :: [a] -> [a] -> [a]-takeEvenly (a : as) (b : bs) = a : b : takeEvenly as bs-takeEvenly xs [] = xs-takeEvenly [] xs = xs-   +takeEvenly :: [[a]] -> [a]+takeEvenly [] = []+takeEvenly xss = (catMaybes . map safeHead $ xss) ++ takeEvenly (filter (not . null) . map tail $ xss)++safeHead :: [a] -> Maybe a+safeHead [] = Nothing+safeHead (x : _) = Just x+ pairs :: [a] -> [(a, a)] pairs [] = [] pairs xs = zip xs (tail xs)
range.cabal view
@@ -10,18 +10,23 @@ -- PVP summary:      +-+------- breaking API changes --                   | | +----- non-breaking API additions --                   | | | +--- code changes with no API change-version:             0.2.0.0+version:             0.2.1.0  -- A short (one-line) description of the package.-synopsis:            This has a bunch of code for specifying and managing ranges in your code.+synopsis:            An efficient and versatile range library.  -- A longer description of the package.-description:         range is built to allow you to use ranges in your code quickly and-                     efficiently. There are many occasions where you will want to check if-                     certain values are within a range and this library will make it-                     trivial for you to do so. It also attempts to do so in the most-                     efficient way possible.+description:         The range library alows the use of performant and versatile ranges in your code.+                     It supports bounded and unbounded ranges, ranges in a nested manner (like library+                     versions), an efficient algebra of range computation and even a simplified interface+                     for ranges for the common cases. This library is far more efficient than using the+                     default Data.List functions to approximate range behaviour. Performance is the major+                     value offering of this library. +                     If this is your first time using this library it is highly recommended that you start+                     with "Data.Range.Range"; it contains the basics of this library that meet most use+                     cases.+ homepage:            https://bitbucket.org/robertmassaioli/range  -- The license under which the package is released.@@ -67,7 +72,7 @@     -- Other library packages from which modules are imported.-  build-depends:  base >= 4.5 && < 5+  build-depends:  base >= 4.7 && < 5                   , parsec >= 3                   , free >=4.12