packages feed

split 0.2.1.3 → 0.2.5

raw patch · 7 files changed

Files

CHANGES view
@@ -1,3 +1,73 @@+* 0.2.5 (9 January 2024)++  - Test with GHC 9.8.+  - New function `mapSplitter` witnessing the fact that `Splitter` is+    a contravariant functor+    ([#22](https://github.com/byorgey/split/pull/22), thanks to Ellis+    Kesterton)++* 0.2.4 (9 October 2023)++  - Test with GHC 9.6.+  - Change examples to doctests (+ add a few more), and check them as part of CI.+  - Fix up a bunch of style hints.++* 0.2.3.5 (11 August 2022)++  - Change base upper bound to < 5.+  - Test with GHC 9.2 and 9.4.++* 0.2.3.4-r1 (22 February 2021)++  - Allow base-4.15 for GHC-9.0++* 0.2.3.4 (22 February 2020)++  - Allow base-4.14 for GHC-8.10++* 0.2.3.3-r2 (19 August 2019)++  - Allow base-4.13 for GHC-8.8++* 0.2.3.3-r1 (2 August 2018)++  - Allow base-4.12 for GHC-8.6++* 0.2.3.3 (23 Jan 2018)++  - Update for GHC-8.4, test with GHC 7.0-8.4++* 0.2.3.2 (15 May 2017)++  - Move to github.++* 0.2.3.1 (2 May 2016)++  - Fix a test case which was causing occasional spurious test suite+    failures due to too many discarded tests.  Thanks to Doug+    Beardsley and Peter Simons for reporting the issue.++* 0.2.3 (12 January 2016)++  - New function 'divvy' (with associated tests) from Tim Washington.++* 0.2.2r2 (7 Jan 2016)++  - allow base-4.9++* 0.2.2r1 (12 Dec 2014)++  - allow base-4.8++* 0.2.2 (14 April 2013)++  - Add 'dropInnerBlanks' combinator for dropping blank chunks between+    consecutive delimiters while still keeping the delimiters separate.++* 0.2.1.3 (28 March 2013)++  - bump upper bound to allow base-4.7+ * 0.2.1.2 (28 January 2013)    - Patch from Daniel Wagner to make splitting lazier when using
− README
@@ -1,32 +0,0 @@--Data.List.Split provides a wide range of strategies and a unified-combinator framework for splitting lists with respect to some sort of-delimiter.----------------------------------------------------------------------------Dependencies:--  There are no dependencies other than the base package.-  Data.List.Split has been tested with versions of GHC from 6.8.3 up-  through 7.6.1.  It is completely Haskell2010 (probably also-  Haskell98) compliant, so it probably builds with other compilers as-  well.--  The Properties.hs file depends on QuickCheck >= 2.4, but you don't-  need it in order to build the library itself, only to run the tests.--Build with Cabal:--    cabal install--Building Haddock documentation (recommended):--    cabal haddock--  Once the documentation has been built, you can access it by -  pointing your browser to dist/doc/html/split/index.html.--Running the tests:--  cabal configure --enable-tests && cabal build && cabal test
+ README.md view
@@ -0,0 +1,28 @@+[![Build Status][build-status]][actions]+[![split release on Hackage][hackage-img]][hackage]++[build-status]: https://github.com/byorgey/split/actions/workflows/haskell-ci.yml/badge.svg+[hackage-img]: https://img.shields.io/hackage/v/split.svg?logo=haskell+[hackage]: https://hackage.haskell.org/package/split+[actions]: https://github.com/byorgey/split/actions++List splitting+==============++`Data.List.Split` provides a wide range of strategies and a unified+combinator framework for splitting lists with respect to some sort of+delimiter.  See+<https://hackage.haskell.org/package/split/docs/Data-List-Split.html>+to get started using it.++Dependencies+============++There are no dependencies other than the base package.+`Data.List.Split` is currently tested with versions of GHC from 7.0 up+through 9.6.  It is completely Haskell2010 (probably also Haskell98)+compliant, so it probably builds with other compilers as well.++The tests in `Properties.hs` depend on `QuickCheck` >= 2.4, but you+don't need it in order to build the library itself, only to run the+tests.
split.cabal view
@@ -1,5 +1,5 @@ Name:                split-Version:             0.2.1.3+Version:             0.2.5 Stability:           stable  Description:         A collection of various methods for splitting@@ -29,29 +29,30 @@ License:             BSD3 License-file:        LICENSE Copyright:           (c) Brent Yorgey, Louis Wasserman 2008-2012-Extra-source-files:  README, test/Properties.hs, CHANGES+Extra-source-files:  README.md, test/Properties.hs, CHANGES Author:              Brent Yorgey-Maintainer:          byorgey@cis.upenn.edu+Maintainer:          byorgey@gmail.com Category:            List Build-type:          Simple Cabal-Version:       >= 1.10-Tested-with:         GHC ==7.0.4, GHC ==7.2.1, GHC ==7.4.*, GHC ==7.6.1-Bug-reports:         http://hub.darcs.net/byorgey/split/issues+Tested-with:         GHC ==9.8.1 || ==9.6.3 || ==9.4.8 || ==9.2.8 || ==9.0.2 || ==8.10.7 || ==8.8.4 || ==8.6.5 || ==8.4.4 || ==8.2.2 || ==8.0.2 || ==7.10.3 || ==7.8.4 || ==7.6.3 || ==7.4.2 || ==7.2.2 || ==7.0.4 +Bug-reports:         https://github.com/byorgey/split/issues+ Test-suite split-tests   type:              exitcode-stdio-1.0   main-is:           Properties.hs-  build-depends:     base, QuickCheck >= 2.4, split+  build-depends:     base, QuickCheck >= 2.4 && < 3, split   default-language:  Haskell2010   Hs-source-dirs:    test  Source-repository head-  type:              darcs-  location:          http://hub.darcs.net/byorgey/split+  type:              git+  location:          http://github.com/byorgey/split.git  Library   ghc-options:       -Wall-  build-depends:     base <4.8+  build-depends:     base < 5   exposed-modules:   Data.List.Split, Data.List.Split.Internals   default-language:  Haskell2010   Hs-source-dirs:    src
src/Data/List/Split.hs view
@@ -1,6 +1,5 @@ {-# OPTIONS_HADDOCK prune #-} ------------------------------------------------------------------------------ -- | -- Module      :  Data.List.Split -- Copyright   :  (c) Brent Yorgey, Louis Wasserman 2008-2012@@ -16,82 +15,74 @@ -- detailed documentation of all exported functions.  If you want to -- learn about the implementation, see "Data.List.Split.Internals". ----- A darcs repository containing the source (including a module with+-- A git repository containing the source (including a module with -- over 40 QuickCheck properties) can be found at--- <http://hub.darcs.net/byorgey/split>.---------------------------------------------------------------------------------+-- <https://github.com/byorgey/split>. module Data.List.Split (--                       -- * Getting started-                       -- $started--                       -- * Convenience functions-                       -- $conv--                         splitOn-                       , splitOneOf-                       , splitWhen-                       , endBy-                       , endByOneOf--                       , wordsBy-                       , linesBy--                       -- * Other splitting methods-                       -- $other-                       , chunksOf-                       , splitPlaces-                       , splitPlacesBlanks-                       , chop--                       -- * Splitting combinators-                       -- $comb--                       , Splitter-                       , defaultSplitter-                       , split--                       -- ** Basic strategies-                       -- $basic--                       , oneOf-                       , onSublist-                       , whenElt+  -- * Getting started+  -- $started -                       -- ** Strategy transformers-                       -- $transform+  -- * Convenience functions+  -- $conv+  splitOn,+  splitOneOf,+  splitWhen,+  endBy,+  endByOneOf,+  wordsBy,+  linesBy, -                       , dropDelims-                       , keepDelimsL-                       , keepDelimsR-                       , condense-                       , dropInitBlank-                       , dropFinalBlank+  -- * Other splitting methods+  -- $other+  chunksOf,+  splitPlaces,+  splitPlacesBlanks,+  chop,+  divvy, -                       -- ** Derived combinators-                       -- $derived+  -- * Splitting combinators+  -- $comb+  Splitter,+  defaultSplitter,+  split, -                       , dropBlanks-                       , startsWith-                       , startsWithOneOf-                       , endsWith-                       , endsWithOneOf+  -- ** Basic strategies+  -- $basic+  oneOf,+  onSublist,+  whenElt, -                       -- The following synonyms are deprecated, but-                       -- still exported for now.  No documentation is-                       -- generated for them via the 'OPTIONS_HADDOCK-                       -- prune' pragma.+  -- ** Strategy transformers+  -- $transform+  dropDelims,+  keepDelimsL,+  keepDelimsR,+  condense,+  dropInitBlank,+  dropFinalBlank,+  dropInnerBlanks,+  mapSplitter, -                       , sepBy-                       , sepByOneOf-                       , unintercalate-                       , splitEvery-                       , chunk+  -- ** Derived combinators+  -- $derived+  dropBlanks,+  startsWith,+  startsWithOneOf,+  endsWith,+  endsWithOneOf,+  -- The following synonyms are deprecated, but+  -- still exported for now.  No documentation is+  -- generated for them via the 'OPTIONS_HADDOCK+  -- prune' pragma. -                       ) where+  sepBy,+  sepByOneOf,+  unintercalate,+  splitEvery,+  chunk,+) where -import           Data.List.Split.Internals+import Data.List.Split.Internals  -- $started -- To get started, you should take a look at the functions 'splitOn',@@ -100,24 +91,24 @@ -- functions implement various common splitting operations, and one of -- them will probably do the job 90\% of the time.  For example: ----- > > splitOn "x" "axbxc"--- > ["a","b","c"]--- >--- > > splitOn "x" "axbxcx"--- > ["a","b","c",""]--- >--- > > endBy ";" "foo;bar;baz;"--- > ["foo","bar","baz"]--- >--- > > splitWhen (<0) [1,3,-4,5,7,-9,0,2]--- > [[1,3],[5,7],[0,2]]--- >--- > > splitOneOf ";.," "foo,bar;baz.glurk"--- > ["foo","bar","baz","glurk"]--- >--- > > chunksOf 3 ['a'..'z']--- > ["abc","def","ghi","jkl","mno","pqr","stu","vwx","yz"]+-- >>> splitOn "x" "axbxc"+-- ["a","b","c"] --+-- >>> splitOn "x" "axbxcx"+-- ["a","b","c",""]+--+-- >>> endBy ";" "foo;bar;baz;"+-- ["foo","bar","baz"]+--+-- >>> splitWhen (<0) [1,3,-4,5,7,-9,0,2]+-- [[1,3],[5,7],[0,2]]+--+-- >>> splitOneOf ";.," "foo,bar;baz.glurk"+-- ["foo","bar","baz","glurk"]+--+-- >>> chunksOf 3 ['a'..'z']+-- ["abc","def","ghi","jkl","mno","pqr","stu","vwx","yz"]+-- -- If you want more flexibility, however, you can use the combinator -- library in terms of which these functions are defined.  For more -- information, see the section labeled \"Splitting Combinators\".@@ -146,8 +137,8 @@ -- suitable 'Splitter' has been created, it can be run with the -- 'split' function.  For example: ----- > > split (dropBlanks . condense $ whenElt (<0)) [1,2,4,-5,-6,4,9,-19,-30]--- > [[1,2,4],[-5,-6],[4,9],[-19,-30]]+-- >>> split (dropBlanks . condense $ whenElt (<0)) [1,2,4,-5,-6,4,9,-19,-30]+-- [[1,2,4],[-5,-6],[4,9],[-19,-30]]  -- $basic -- All these basic strategies have the same parameters as the
src/Data/List/Split/Internals.hs view
@@ -1,6 +1,5 @@ {-# OPTIONS_HADDOCK prune #-} ------------------------------------------------------------------------------ -- | -- Module      :  Data.List.Split.Internals -- Copyright   :  (c) Brent Yorgey, Louis Wasserman 2008-2012@@ -12,31 +11,29 @@ -- Implementation module for "Data.List.Split", a combinator library -- for splitting lists.  See the "Data.List.Split" documentation for -- more description and examples.---------------------------------------------------------------------------------- module Data.List.Split.Internals where -import           Data.List (genericSplitAt)+import Data.List (genericSplitAt)  -- * Types and utilities  -- | A splitting strategy.-data Splitter a = Splitter { delimiter        :: Delimiter a-                               -- ^ What delimiter to split on-                           , delimPolicy      :: DelimPolicy-                               -- ^ What to do with delimiters (drop-                               --   from output, keep as separate-                               --   elements in output, or merge with-                               --   previous or following chunks)-                           , condensePolicy   :: CondensePolicy-                               -- ^ What to do with multiple-                               --   consecutive delimiters-                           , initBlankPolicy  :: EndPolicy-                               -- ^ Drop an initial blank?-                           , finalBlankPolicy :: EndPolicy-                               -- ^ Drop a final blank?-                           }+data Splitter a = Splitter+  { delimiter :: Delimiter a+  -- ^ What delimiter to split on+  , delimPolicy :: DelimPolicy+  -- ^ What to do with delimiters (drop+  --   from output, keep as separate+  --   elements in output, or merge with+  --   previous or following chunks)+  , condensePolicy :: CondensePolicy+  -- ^ What to do with multiple+  --   consecutive delimiters+  , initBlankPolicy :: EndPolicy+  -- ^ Drop an initial blank?+  , finalBlankPolicy :: EndPolicy+  -- ^ Drop a final blank?+  }  -- | The default splitting strategy: keep delimiters in the output --   as separate chunks, don't condense multiple consecutive@@ -55,12 +52,14 @@ --   the original input list.  This default strategy can be overridden --   to allow discarding various sorts of information. defaultSplitter :: Splitter a-defaultSplitter = Splitter { delimiter        = Delimiter [const False]-                           , delimPolicy      = Keep-                           , condensePolicy   = KeepBlankFields-                           , initBlankPolicy  = KeepBlank-                           , finalBlankPolicy = KeepBlank-                           }+defaultSplitter =+  Splitter+    { delimiter = Delimiter [const False]+    , delimPolicy = Keep+    , condensePolicy = KeepBlankFields+    , initBlankPolicy = KeepBlank+    , finalBlankPolicy = KeepBlank+    }  -- | A delimiter is a list of predicates on elements, matched by some --   contiguous subsequence of a list.@@ -69,29 +68,42 @@ -- | Try to match a delimiter at the start of a list, either failing --   or decomposing the list into the portion which matched the delimiter --   and the remainder.-matchDelim :: Delimiter a -> [a] -> Maybe ([a],[a])-matchDelim (Delimiter []) xs = Just ([],xs)-matchDelim (Delimiter _)  [] = Nothing-matchDelim (Delimiter (p:ps)) (x:xs)-  | p x       = matchDelim (Delimiter ps) xs >>= \(h,t) -> Just (x:h,t)+matchDelim :: Delimiter a -> [a] -> Maybe ([a], [a])+matchDelim (Delimiter []) xs = Just ([], xs)+matchDelim (Delimiter _) [] = Nothing+matchDelim (Delimiter (p : ps)) (x : xs)+  | p x = matchDelim (Delimiter ps) xs >>= \(h, t) -> Just (x : h, t)   | otherwise = Nothing  -- | What to do with delimiters?-data DelimPolicy = Drop      -- ^ Drop delimiters from the output.-                 | Keep      -- ^ Keep delimiters as separate chunks-                             --   of the output.-                 | KeepLeft  -- ^ Keep delimiters in the output,-                             --   prepending them to the following-                             --   chunk.-                 | KeepRight -- ^ Keep delimiters in the output,-                             --   appending them to the previous chunk.+data DelimPolicy+  = -- | Drop delimiters from the output.+    Drop+  | -- | Keep delimiters as separate chunks+    --   of the output.+    Keep+  | -- | Keep delimiters in the output,+    --   prepending them to the following+    --   chunk.+    KeepLeft+  | -- | Keep delimiters in the output,+    --   appending them to the previous chunk.+    KeepRight   deriving (Eq, Show)  -- | What to do with multiple consecutive delimiters?-data CondensePolicy = Condense         -- ^ Condense into a single delimiter.-                    | KeepBlankFields  -- ^ Insert blank chunks-                                       --   between consecutive-                                       --   delimiters.+data CondensePolicy+  = -- | Condense into a single delimiter.+    Condense+  | -- | Keep consecutive+    --   delimiters separate, but+    --   don't insert blank chunks in+    --   between them.+    DropBlankFields+  | -- | Insert blank chunks+    --   between consecutive+    --   delimiters.+    KeepBlankFields   deriving (Eq, Show)  -- | What to do with a blank chunk at either end of the list@@ -134,64 +146,70 @@ splitInternal :: Delimiter a -> [a] -> SplitList a splitInternal _ [] = [] splitInternal d xxs-  | null xs   = toSplitList match+  | null xs = toSplitList match   | otherwise = Text xs : toSplitList match  where-  (xs,match) = breakDelim d xxs+  (xs, match) = breakDelim d xxs -  toSplitList Nothing             = []-  toSplitList (Just ([],r:rs))    = Delim [] : Text [r] : splitInternal d rs-  toSplitList (Just (delim,rest)) = Delim delim : splitInternal d rest+  toSplitList Nothing = []+  toSplitList (Just ([], r : rs)) = Delim [] : Text [r] : splitInternal d rs+  toSplitList (Just (delim, rest)) = Delim delim : splitInternal d rest -breakDelim :: Delimiter a -> [a] -> ([a],Maybe ([a],[a]))-breakDelim (Delimiter []) xs         = ([],Just ([],xs))-breakDelim _              []         = ([],Nothing)-breakDelim d              xxs@(x:xs) =+breakDelim :: Delimiter a -> [a] -> ([a], Maybe ([a], [a]))+breakDelim (Delimiter []) xs = ([], Just ([], xs))+breakDelim _ [] = ([], Nothing)+breakDelim d xxs@(x : xs) =   case matchDelim d xxs of-      Nothing    -> let (ys,match) = breakDelim d xs in (x:ys,match)-      Just match -> ([], Just match)+    Nothing -> let (ys, match) = breakDelim d xs in (x : ys, match)+    Just match -> ([], Just match)  -- | Given a split list in the internal tagged representation, produce --   a new internal tagged representation corresponding to the final --   output, according to the strategy defined by the given --   'Splitter'. postProcess :: Splitter a -> SplitList a -> SplitList a-postProcess s = dropFinal (finalBlankPolicy s)-              . dropInitial (initBlankPolicy s)-              . doMerge (delimPolicy s)-              . doDrop (delimPolicy s)-              . insertBlanks-              . doCondense (condensePolicy s)+postProcess s =+  dropFinal (finalBlankPolicy s)+    . dropInitial (initBlankPolicy s)+    . doMerge (delimPolicy s)+    . doDrop (delimPolicy s)+    . insertBlanks (condensePolicy s)+    . doCondense (condensePolicy s)  -- | Drop delimiters if the 'DelimPolicy' is 'Drop'. doDrop :: DelimPolicy -> SplitList a -> SplitList a-doDrop Drop l = [ c | c@(Text _) <- l ]+doDrop Drop l = [c | c@(Text _) <- l] doDrop _ l = l  -- | Condense multiple consecutive delimiters into one if the --   'CondensePolicy' is 'Condense'. doCondense :: CondensePolicy -> SplitList a -> SplitList a-doCondense KeepBlankFields ls = ls doCondense Condense ls = condense' ls-  where condense' [] = []-        condense' (c@(Text _) : l) = c : condense' l-        condense' l = (Delim $ concatMap fromElem ds) : condense' rest-          where (ds,rest) = span isDelim l+ where+  condense' [] = []+  condense' (c@(Text _) : l) = c : condense' l+  condense' l = Delim (concatMap fromElem ds) : condense' rest+   where+    (ds, rest) = span isDelim l+doCondense _ ls = ls --- | Insert blank chunks between any remaining consecutive delimiters,---   and at the beginning or end if the first or last element is a---   delimiter.-insertBlanks :: SplitList a -> SplitList a-insertBlanks [] = [Text []]-insertBlanks (d@(Delim _) : l) = Text [] : insertBlanks' (d:l)-insertBlanks l = insertBlanks' l+-- | Insert blank chunks between any remaining consecutive delimiters+--   (unless the condense policy is 'DropBlankFields'), and at the+--   beginning or end if the first or last element is a delimiter.+insertBlanks :: CondensePolicy -> SplitList a -> SplitList a+insertBlanks _ [] = [Text []]+insertBlanks cp (d@(Delim _) : l) = Text [] : insertBlanks' cp (d : l)+insertBlanks cp l = insertBlanks' cp l  -- | Insert blank chunks between consecutive delimiters.-insertBlanks' :: SplitList a -> SplitList a-insertBlanks' [] = []-insertBlanks' (d1@(Delim _) : d2@(Delim _) : l) = d1 : Text [] : insertBlanks' (d2:l)-insertBlanks' [d@(Delim _)] = [d, Text []]-insertBlanks' (c : l) = c : insertBlanks' l+insertBlanks' :: CondensePolicy -> SplitList a -> SplitList a+insertBlanks' _ [] = []+insertBlanks' cp@DropBlankFields (d1@(Delim _) : d2@(Delim _) : l) =+  d1 : insertBlanks' cp (d2 : l)+insertBlanks' cp (d1@(Delim _) : d2@(Delim _) : l) =+  d1 : Text [] : insertBlanks' cp (d2 : l)+insertBlanks' _ [d@(Delim _)] = [d, Text []]+insertBlanks' cp (c : l) = c : insertBlanks' cp l  -- | Merge delimiters into adjacent chunks according to the 'DelimPolicy'. doMerge :: DelimPolicy -> SplitList a -> SplitList a@@ -204,7 +222,7 @@ --   chunks, so they are merged with chunks to their right). mergeLeft :: SplitList a -> SplitList a mergeLeft [] = []-mergeLeft ((Delim d) : (Text c) : l) = Text (d++c) : mergeLeft l+mergeLeft ((Delim d) : (Text c) : l) = Text (d ++ c) : mergeLeft l mergeLeft (c : l) = c : mergeLeft l  -- | Merge delimiters with adjacent chunks to the left.@@ -213,10 +231,11 @@ -- below fanciness is with the goal of laziness: we want to start returning -- stuff before we've necessarily discovered a delimiter, in case we're -- processing some infinite list with no delimiter-mergeRight ((Text c) : l) = Text (c++d) : mergeRight lTail-  where (d, lTail) = case l of-                     Delim d' : l' -> (d', l')-                     _ -> ([], l)+mergeRight ((Text c) : l) = Text (c ++ d) : mergeRight lTail+ where+  (d, lTail) = case l of+    Delim d' : l' -> (d', l')+    _ -> ([], l) mergeRight (c : l) = c : mergeRight l  -- | Drop an initial blank chunk according to the given 'EndPolicy'.@@ -226,12 +245,13 @@  -- | Drop a final blank chunk according to the given 'EndPolicy'. dropFinal :: EndPolicy -> SplitList a -> SplitList a-dropFinal _         [] = []-dropFinal DropBlank l  = dropFinal' l-  where dropFinal' []        = []-        dropFinal' [Text []] = []-        dropFinal' (x:xs)    = x:dropFinal' xs-dropFinal _         l  = l+dropFinal _ [] = []+dropFinal DropBlank l = dropFinal' l+ where+  dropFinal' [] = []+  dropFinal' [Text []] = []+  dropFinal' (x : xs) = x : dropFinal' xs+dropFinal _ l = l  -- * Combinators @@ -242,220 +262,297 @@ split s = map fromElem . postProcess s . splitInternal (delimiter s)  -- ** Basic strategies---+ -- $ All these basic strategies have the same parameters as the -- 'defaultSplitter' except for the delimiters.  -- | A splitting strategy that splits on any one of the given---   elements.  For example:+--   elements. ----- > split (oneOf "xyz") "aazbxyzcxd" == ["aa","z","b","x","","y","","z","c","x","d"]-oneOf :: Eq a => [a] -> Splitter a-oneOf elts = defaultSplitter { delimiter = Delimiter [(`elem` elts)] }+-- >>> split (oneOf ",;") "hi;there,world"+-- ["hi",";","there",",","world"]+--+-- >>> split (oneOf "xyz") "aazbxyzcxd"+-- ["aa","z","b","x","","y","","z","c","x","d"]+oneOf :: (Eq a) => [a] -> Splitter a+oneOf elts = defaultSplitter {delimiter = Delimiter [(`elem` elts)]}  -- | A splitting strategy that splits on the given list, when it is---   encountered as an exact subsequence.  For example:+--   encountered as an exact subsequence. ----- > split (onSublist "xyz") "aazbxyzcxd" == ["aazb","xyz","cxd"]+-- >>> split (onSublist "xyz") "aazbxyzcxd"+-- ["aazb","xyz","cxd"] -- --   Note that splitting on the empty list is a special case, which---   splits just before every element of the list being split.  For example:+--   splits just before every element of the list being split. ----- > split (onSublist "") "abc" == ["","","a","","b","","c"]--- > split (dropDelims . dropBlanks $ onSublist "") "abc" == ["a","b","c"]+-- >>> split (onSublist "") "abc"+-- ["","","a","","b","","c"] --+-- >>> split (dropDelims . dropBlanks $ onSublist "") "abc"+-- ["a","b","c"]+-- --   However, if you want to break a list into singleton elements like --   this, you are better off using @'chunksOf' 1@, or better yet, --   @'map' (:[])@.-onSublist :: Eq a => [a] -> Splitter a-onSublist lst = defaultSplitter { delimiter = Delimiter (map (==) lst) }+onSublist :: (Eq a) => [a] -> Splitter a+onSublist lst = defaultSplitter {delimiter = Delimiter (map (==) lst)}  -- | A splitting strategy that splits on any elements that satisfy the---   given predicate.  For example:+--   given predicate. ----- > split (whenElt (<0)) [2,4,-3,6,-9,1] == [[2,4],[-3],[6],[-9],[1]]+-- >>> split (whenElt (<0)) [2,4,-3,6,-9,1 :: Int]+-- [[2,4],[-3],[6],[-9],[1]] whenElt :: (a -> Bool) -> Splitter a-whenElt p = defaultSplitter { delimiter = Delimiter [p] }+whenElt p = defaultSplitter {delimiter = Delimiter [p]}  -- ** Strategy transformers  -- | Drop delimiters from the output (the default is to keep---   them). For example,+--   them). ----- > split (oneOf ":") "a:b:c" == ["a", ":", "b", ":", "c"]--- > split (dropDelims $ oneOf ":") "a:b:c" == ["a", "b", "c"]+-- >>> split (oneOf ":") "a:b:c"+-- ["a",":","b",":","c"]+--+-- >>> split (dropDelims $ oneOf ":") "a:b:c"+-- ["a","b","c"] dropDelims :: Splitter a -> Splitter a-dropDelims s = s { delimPolicy = Drop }+dropDelims s = s {delimPolicy = Drop}  -- | Keep delimiters in the output by prepending them to adjacent---   chunks.  For example:+--   chunks. ----- > split (keepDelimsL $ oneOf "xyz") "aazbxyzcxd" == ["aa","zb","x","y","zc","xd"]+-- >>> split (keepDelimsL $ oneOf "xyz") "aazbxyzcxd"+-- ["aa","zb","x","y","zc","xd"] keepDelimsL :: Splitter a -> Splitter a-keepDelimsL s = s { delimPolicy = KeepLeft }+keepDelimsL s = s {delimPolicy = KeepLeft}  -- | Keep delimiters in the output by appending them to adjacent---   chunks. For example:+--   chunks. ----- > split (keepDelimsR $ oneOf "xyz") "aazbxyzcxd" == ["aaz","bx","y","z","cx","d"]+-- >>> split (keepDelimsR $ oneOf "xyz") "aazbxyzcxd"+-- ["aaz","bx","y","z","cx","d"] keepDelimsR :: Splitter a -> Splitter a-keepDelimsR s = s { delimPolicy = KeepRight }+keepDelimsR s = s {delimPolicy = KeepRight} --- | Condense multiple consecutive delimiters into one.  For example:+-- | Condense multiple consecutive delimiters into one. ----- > split (condense $ oneOf "xyz") "aazbxyzcxd" == ["aa","z","b","xyz","c","x","d"]--- > split (dropDelims $ oneOf "xyz") "aazbxyzcxd" == ["aa","b","","","c","d"]--- > split (condense . dropDelims $ oneOf "xyz") "aazbxyzcxd" == ["aa","b","c","d"]+-- >>> split (condense $ oneOf "xyz") "aazbxyzcxd"+-- ["aa","z","b","xyz","c","x","d"]+--+-- >>> split (dropDelims $ oneOf "xyz") "aazbxyzcxd"+-- ["aa","b","","","c","d"]+--+-- >>> split (condense . dropDelims $ oneOf "xyz") "aazbxyzcxd"+-- ["aa","b","c","d"] condense :: Splitter a -> Splitter a-condense s = s { condensePolicy = Condense }+condense s = s {condensePolicy = Condense}  -- | Don't generate a blank chunk if there is a delimiter at the---   beginning.  For example:+--   beginning. ----- > split (oneOf ":") ":a:b" == ["",":","a",":","b"]--- > split (dropInitBlank $ oneOf ":") ":a:b" == [":","a",":","b"]+-- >>> split (oneOf ":") ":a:b"+-- ["",":","a",":","b"]+--+-- >>> split (dropInitBlank $ oneOf ":") ":a:b"+-- [":","a",":","b"] dropInitBlank :: Splitter a -> Splitter a-dropInitBlank s = s { initBlankPolicy = DropBlank }+dropInitBlank s = s {initBlankPolicy = DropBlank}  -- | Don't generate a blank chunk if there is a delimiter at the end.---   For example: ----- > split (oneOf ":") "a:b:" == ["a",":","b",":",""]--- > split (dropFinalBlank $ oneOf ":") "a:b:" == ["a",":","b",":"]+-- >>> split (oneOf ":") "a:b:"+-- ["a",":","b",":",""]+--+-- >>> split (dropFinalBlank $ oneOf ":") "a:b:"+-- ["a",":","b",":"] dropFinalBlank :: Splitter a -> Splitter a-dropFinalBlank s = s { finalBlankPolicy = DropBlank }+dropFinalBlank s = s {finalBlankPolicy = DropBlank} +-- | Don't generate blank chunks between consecutive delimiters.+--+-- >>> split (oneOf ":") "::b:::a"+-- ["",":","",":","b",":","",":","",":","a"]+--+-- >>> split (dropInnerBlanks $ oneOf ":") "::b:::a"+-- ["",":",":","b",":",":",":","a"]+dropInnerBlanks :: Splitter a -> Splitter a+dropInnerBlanks s = s {condensePolicy = DropBlankFields}++-- | Split over a different type of element by performing a preprocessing step.+--+-- >>> split (mapSplitter snd $ oneOf "-_") $ zip [0..] "a-bc_d"+-- [[(0,'a')],[(1,'-')],[(2,'b'),(3,'c')],[(4,'_')],[(5,'d')]]+--+-- >>> import Data.Char (toLower)+-- >>> split (mapSplitter toLower $ dropDelims $ whenElt (== 'x')) "abXcxd"+-- ["ab","c","d"]+mapSplitter :: (b -> a) -> Splitter a -> Splitter b+mapSplitter f (Splitter d dp cp ibp fbp) = Splitter (mapDelimiter f d) dp cp ibp fbp+ where+  mapDelimiter :: (b -> a) -> Delimiter a -> Delimiter b+  mapDelimiter g (Delimiter xs) = Delimiter $ map (. g) xs+ -- ** Derived combinators --- | Drop all blank chunks from the output.  Equivalent to---   @'dropInitBlank' . 'dropFinalBlank' . 'condense'@.  For example:+-- | Drop all blank chunks from the output, and condense consecutive+--   delimiters into one.  Equivalent to @'dropInitBlank'+--   . 'dropFinalBlank' . 'condense'@. ----- > split (oneOf ":") "::b:::a" == ["",":","",":","b",":","",":","",":","a"]--- > split (dropBlanks $ oneOf ":") "::b:::a" == ["::","b",":::","a"]+-- >>> split (oneOf ":") "::b:::a"+-- ["",":","",":","b",":","",":","",":","a"]+--+-- >>> split (dropBlanks $ oneOf ":") "::b:::a"+-- ["::","b",":::","a"] dropBlanks :: Splitter a -> Splitter a dropBlanks = dropInitBlank . dropFinalBlank . condense  -- | Make a strategy that splits a list into chunks that all start --   with the given subsequence (except possibly the first). --   Equivalent to @'dropInitBlank' . 'keepDelimsL' . 'onSublist'@.---   For example: ----- > split (startsWith "app") "applyapplicativeapplaudapproachapple" == ["apply","applicative","applaud","approach","apple"]-startsWith :: Eq a => [a] -> Splitter a+-- >>> split (startsWith "app") "applyapplicativeapplaudapproachapple"+-- ["apply","applicative","applaud","approach","apple"]+startsWith :: (Eq a) => [a] -> Splitter a startsWith = dropInitBlank . keepDelimsL . onSublist  -- | Make a strategy that splits a list into chunks that all start --   with one of the given elements (except possibly the first).---   Equivalent to @'dropInitBlank' . 'keepDelimsL' . 'oneOf'@.  For+--   Equivalent to @'dropInitBlank' . 'keepDelimsL' . 'oneOf'@. --   example: ----- > split (startsWithOneOf ['A'..'Z']) "ACamelCaseIdentifier" == ["A","Camel","Case","Identifier"]-startsWithOneOf :: Eq a => [a] -> Splitter a+-- >>> split (startsWithOneOf ['A'..'Z']) "ACamelCaseIdentifier"+-- ["A","Camel","Case","Identifier"]+startsWithOneOf :: (Eq a) => [a] -> Splitter a startsWithOneOf = dropInitBlank . keepDelimsL . oneOf  -- | Make a strategy that splits a list into chunks that all end with --   the given subsequence, except possibly the last.  Equivalent to---   @'dropFinalBlank' . 'keepDelimsR' . 'onSublist'@.  For example:+--   @'dropFinalBlank' . 'keepDelimsR' . 'onSublist'@. ----- > split (endsWith "ly") "happilyslowlygnarlylily" == ["happily","slowly","gnarly","lily"]-endsWith :: Eq a => [a] -> Splitter a+-- >>> split (endsWith "ly") "happilyslowlygnarlylily"+-- ["happily","slowly","gnarly","lily"]+endsWith :: (Eq a) => [a] -> Splitter a endsWith = dropFinalBlank . keepDelimsR . onSublist  -- | Make a strategy that splits a list into chunks that all end with --   one of the given elements, except possibly the last.  Equivalent---   to @'dropFinalBlank' . 'keepDelimsR' . 'oneOf'@.  For example:+--   to @'dropFinalBlank' . 'keepDelimsR' . 'oneOf'@. ----- > split (condense $ endsWithOneOf ".,?! ") "Hi, there!  How are you?" == ["Hi, ","there!  ","How ","are ","you?"]-endsWithOneOf :: Eq a => [a] -> Splitter a+-- >>> split (condense $ endsWithOneOf ".,?! ") "Hi, there!  How are you?"+-- ["Hi, ","there!  ","How ","are ","you?"]+endsWithOneOf :: (Eq a) => [a] -> Splitter a endsWithOneOf = dropFinalBlank . keepDelimsR . oneOf  -- ** Convenience functions------ These functions implement some common splitting strategies.  Note--- that all of the functions in this section drop delimiters from--- the final output, since that is a more common use case even--- though it is not the default.  -- | Split on any of the given elements.  Equivalent to @'split'---   . 'dropDelims' . 'oneOf'@.  For example:+--   . 'dropDelims' . 'oneOf'@. ----- > splitOneOf ";.," "foo,bar;baz.glurk" == ["foo","bar","baz","glurk"]-splitOneOf :: Eq a => [a] -> [a] -> [[a]]+-- >>> splitOneOf ";.," "foo,bar;baz.glurk"+-- ["foo","bar","baz","glurk"]+splitOneOf :: (Eq a) => [a] -> [a] -> [[a]] splitOneOf = split . dropDelims . oneOf  -- | Split on the given sublist.  Equivalent to @'split'---   . 'dropDelims' . 'onSublist'@.  For example:+--   . 'dropDelims' . 'onSublist'@. ----- > splitOn ".." "a..b...c....d.." == ["a","b",".c","","d",""]+-- >>> splitOn ":" "12:35:07"+-- ["12","35","07"] --+-- >>> splitOn "x" "axbxc"+-- ["a","b","c"]+--+-- >>> splitOn "x" "axbxcx"+-- ["a","b","c",""]+--+-- >>> splitOn ".." "a..b...c....d.."+-- ["a","b",".c","","d",""]+-- --   In some parsing combinator frameworks this is also known as --   @sepBy@. -- --   Note that this is the right inverse of the 'Data.List.intercalate' function --   from "Data.List", that is, -----   > intercalate x . splitOn x === id+-- @+-- intercalate x . splitOn x === id+-- @ -- --   @'splitOn' x . 'Data.List.intercalate' x@ is the identity on --   certain lists, but it is tricky to state the precise conditions --   under which this holds.  (For example, it is not enough to say --   that @x@ does not occur in any elements of the input list. --   Working out why is left as an exercise for the reader.)-splitOn :: Eq a => [a] -> [a] -> [[a]]-splitOn   = split . dropDelims . onSublist+splitOn :: (Eq a) => [a] -> [a] -> [[a]]+splitOn = split . dropDelims . onSublist  -- | Split on elements satisfying the given predicate.  Equivalent to---   @'split' . 'dropDelims' . 'whenElt'@.  For example:+--   @'split' . 'dropDelims' . 'whenElt'@. ----- > splitWhen (<0) [1,3,-4,5,7,-9,0,2] == [[1,3],[5,7],[0,2]]+-- >>> splitWhen (<0) [1,3,-4,5,7,-9,0,2]+-- [[1,3],[5,7],[0,2]]+--+-- >>> splitWhen (<0) [1,-2,3,4,-5,-6,7,8,-9]+-- [[1],[3,4],[],[7,8],[]] splitWhen :: (a -> Bool) -> [a] -> [[a]] splitWhen = split . dropDelims . whenElt  {-# DEPRECATED sepBy "Use splitOn." #-}-sepBy :: Eq a => [a] -> [a] -> [[a]]+sepBy :: (Eq a) => [a] -> [a] -> [[a]] sepBy = splitOn  {-# DEPRECATED sepByOneOf "Use splitOneOf." #-}-sepByOneOf :: Eq a => [a] -> [a] -> [[a]]+sepByOneOf :: (Eq a) => [a] -> [a] -> [[a]] sepByOneOf = splitOneOf  -- | Split into chunks terminated by the given subsequence. --   Equivalent to @'split' . 'dropFinalBlank' . 'dropDelims'---   . 'onSublist'@.  For example:+--   . 'onSublist'@. ----- > endBy ";" "foo;bar;baz;" == ["foo","bar","baz"]+-- >>> endBy ".;" "foo.;bar.;baz.;"+-- ["foo","bar","baz"] -- --   Note also that the 'lines' function from "Data.List" is equivalent --   to @'endBy' \"\\n\"@.-endBy :: Eq a => [a] -> [a] -> [[a]]+endBy :: (Eq a) => [a] -> [a] -> [[a]] endBy = split . dropFinalBlank . dropDelims . onSublist  -- | Split into chunks terminated by one of the given elements. --   Equivalent to @'split' . 'dropFinalBlank' . 'dropDelims'---   . 'oneOf'@. For example:+--   . 'oneOf'@. ----- > endByOneOf ";," "foo;bar,baz;" == ["foo","bar","baz"]-endByOneOf :: Eq a => [a] -> [a] -> [[a]]+-- >>> endByOneOf ";," "foo;bar,baz;"+-- ["foo","bar","baz"]+endByOneOf :: (Eq a) => [a] -> [a] -> [[a]] endByOneOf = split . dropFinalBlank . dropDelims . oneOf  {-# DEPRECATED unintercalate "Use splitOn." #-}-unintercalate :: Eq a => [a] -> [a] -> [[a]]+unintercalate :: (Eq a) => [a] -> [a] -> [[a]] unintercalate = splitOn  -- | Split into \"words\", with word boundaries indicated by the given --   predicate.  Satisfies @'Data.List.words' === wordsBy --   'Data.Char.isSpace'@; equivalent to @'split' . 'dropBlanks'---   . 'dropDelims' . 'whenElt'@.  For example:+--   . 'dropDelims' . 'whenElt'@. ----- > wordsBy (=='x') "dogxxxcatxbirdxx" == ["dog","cat","bird"]+-- >>> wordsBy (`elem` ",;.?! ") "Hello there, world! How?"+-- ["Hello","there","world","How"]+--+-- >>> wordsBy (=='x') "dogxxxcatxbirdxx"+-- ["dog","cat","bird"] wordsBy :: (a -> Bool) -> [a] -> [[a]] wordsBy = split . dropBlanks . dropDelims . whenElt  -- | Split into \"lines\", with line boundaries indicated by the given --   predicate. Satisfies @'lines' === linesBy (=='\n')@; equivalent to---   @'split' . 'dropFinalBlank' . 'dropDelims' . 'whenElt'@.  For example:+--   @'split' . 'dropFinalBlank' . 'dropDelims' . 'whenElt'@. ----- > linesBy (=='x') "dogxxxcatxbirdxx" == ["dog","","","cat","bird",""]+-- >>> linesBy (==';') "foo;bar;;baz;"+-- ["foo","bar","","baz"]+--+-- >>> linesBy (=='x') "dogxxxcatxbirdxx"+-- ["dog","","","cat","bird",""] linesBy :: (a -> Bool) -> [a] -> [[a]] linesBy = split . dropFinalBlank . dropDelims . whenElt @@ -485,20 +582,29 @@ -- | @'chunksOf' n@ splits a list into length-n pieces.  The last --   piece will be shorter if @n@ does not evenly divide the length of --   the list.  If @n <= 0@, @'chunksOf' n l@ returns an infinite list---   of empty lists.  For example:+--   of empty lists. --+-- >>> chunksOf 3 [1..12]+-- [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]+--+-- >>> chunksOf 3 "Hello there"+-- ["Hel","lo ","the","re"]+--+-- >>> chunksOf 3 ([] :: [Int])+-- []+-- --   Note that @'chunksOf' n []@ is @[]@, not @[[]]@.  This is---   intentional, and is consistent with a recursive definition of---   'chunksOf'; it satisfies the property that+--   intentional, and satisfies the property that -- --   @chunksOf n xs ++ chunksOf n ys == chunksOf n (xs ++ ys)@ -- --   whenever @n@ evenly divides the length of @xs@. chunksOf :: Int -> [e] -> [[e]]-chunksOf i ls = map (take i) (build (splitter ls)) where+chunksOf i ls = map (take i) (build (splitter ls))+ where   splitter :: [e] -> ([e] -> a -> a) -> a -> a   splitter [] _ n = n-  splitter l c n  = l `c` splitter (drop i l) c n+  splitter l c n = l `c` splitter (drop i l) c n  {-# DEPRECATED chunk "Use chunksOf." #-} chunk :: Int -> [e] -> [[e]]@@ -508,24 +614,31 @@ splitEvery :: Int -> [e] -> [[e]] splitEvery = chunksOf --- | Split a list into chunks of the given lengths. For example:+-- | Split a list into chunks of the given lengths. ----- > splitPlaces [2,3,4] [1..20] == [[1,2],[3,4,5],[6,7,8,9]]--- > splitPlaces [4,9] [1..10] == [[1,2,3,4],[5,6,7,8,9,10]]--- > splitPlaces [4,9,3] [1..10] == [[1,2,3,4],[5,6,7,8,9,10]]+-- >>> splitPlaces [2,3,4] [1..20]+-- [[1,2],[3,4,5],[6,7,8,9]] --+-- >>> splitPlaces [4,9] [1..10]+-- [[1,2,3,4],[5,6,7,8,9,10]]+--+-- >>> splitPlaces [4,9,3] [1..10]+-- [[1,2,3,4],[5,6,7,8,9,10]]+-- --   If the input list is longer than the total of the given lengths, --   then the remaining elements are dropped. If the list is shorter --   than the total of the given lengths, then the result may contain --   fewer chunks than requested, and the last chunk may be shorter --   than requested.-splitPlaces :: Integral a => [a] -> [e] -> [[e]]-splitPlaces is ys = build (splitPlacer is ys) where-  splitPlacer :: Integral i => [i] -> [b] -> ([b] -> t -> t) -> t -> t-  splitPlacer [] _ _ n      = n-  splitPlacer _ [] _ n      = n-  splitPlacer (l:ls) xs c n = let (x1, x2) = genericSplitAt l xs-                              in  x1 `c` splitPlacer ls x2 c n+splitPlaces :: (Integral a) => [a] -> [e] -> [[e]]+splitPlaces is ys = build (splitPlacer is ys)+ where+  splitPlacer :: (Integral i) => [i] -> [b] -> ([b] -> t -> t) -> t -> t+  splitPlacer [] _ _ n = n+  splitPlacer _ [] _ n = n+  splitPlacer (l : ls) xs c n =+    let (x1, x2) = genericSplitAt l xs+     in x1 `c` splitPlacer ls x2 c n  -- | Split a list into chunks of the given lengths. Unlike --   'splitPlaces', the output list will always be the same length as@@ -533,20 +646,27 @@ --   total of the given lengths, then the remaining elements are --   dropped. If the list is shorter than the total of the given --   lengths, then the last several chunks will be shorter than---   requested or empty. For example:+--   requested or empty. ----- > splitPlacesBlanks [2,3,4] [1..20] == [[1,2],[3,4,5],[6,7,8,9]]--- > splitPlacesBlanks [4,9] [1..10] == [[1,2,3,4],[5,6,7,8,9,10]]--- > splitPlacesBlanks [4,9,3] [1..10] == [[1,2,3,4],[5,6,7,8,9,10],[]]+-- >>> splitPlacesBlanks [2,3,4] [1..20]+-- [[1,2],[3,4,5],[6,7,8,9]] --+-- >>> splitPlacesBlanks [4,9] [1..10]+-- [[1,2,3,4],[5,6,7,8,9,10]]+--+-- >>> splitPlacesBlanks [4,9,3] [1..10]+-- [[1,2,3,4],[5,6,7,8,9,10],[]]+-- --   Notice the empty list in the output of the third example, which --   differs from the behavior of 'splitPlaces'.-splitPlacesBlanks :: Integral a => [a] -> [e] -> [[e]]-splitPlacesBlanks is ys = build (splitPlacer is ys) where-  splitPlacer :: Integral i => [i] -> [b] -> ([b] -> t -> t) -> t -> t-  splitPlacer [] _ _ n      = n-  splitPlacer (l:ls) xs c n = let (x1, x2) = genericSplitAt l xs-                              in  x1 `c` splitPlacer ls x2 c n+splitPlacesBlanks :: (Integral a) => [a] -> [e] -> [[e]]+splitPlacesBlanks is ys = build (splitPlacer is ys)+ where+  splitPlacer :: (Integral i) => [i] -> [b] -> ([b] -> t -> t) -> t -> t+  splitPlacer [] _ _ n = n+  splitPlacer (l : ls) xs c n =+    let (x1, x2) = genericSplitAt l xs+     in x1 `c` splitPlacer ls x2 c n  -- | A useful recursion pattern for processing a list to produce a new --   list, often used for \"chopping\" up the input list.  Typically@@ -560,9 +680,41 @@ -- > group = chop (\ xs@(x:_) -> span (==x) xs) -- > -- > words :: String -> [String]--- > words = filter (not . null) . chop (span (not . isSpace) . dropWhile isSpace)-+-- > words = filter (not . null) . chop (break isSpace . dropWhile isSpace) chop :: ([a] -> (b, [a])) -> [a] -> [b] chop _ [] = [] chop f as = b : chop f as'-  where (b, as') = f as+ where+  (b, as') = f as++-- | Divides up an input list into a set of sublists, according to 'n' and 'm'+--   input specifications you provide. Each sublist will have 'n' items, and the+--   start of each sublist will be offset by 'm' items from the previous one.+--+-- >>> divvy 5 5 [1..15]+-- [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]]+--+-- >>> divvy 5 2 [1..15]+-- [[1,2,3,4,5],[3,4,5,6,7],[5,6,7,8,9],[7,8,9,10,11],[9,10,11,12,13],[11,12,13,14,15]]+--+--   In the case where a source list's trailing elements do no fill an entire+--   sublist, those trailing elements will be dropped.+--+-- >>> divvy 5 2 [1..10]+-- [[1,2,3,4,5],[3,4,5,6,7],[5,6,7,8,9]]+--+--   As an example, you can generate a moving average over a list of prices:+--+-- > type Prices = [Float]+-- > type AveragePrices = [Float]+-- >+-- > average :: [Float] -> Float+-- > average xs = sum xs / (fromIntegral $ length xs)+-- >+-- > simpleMovingAverage :: Prices -> AveragePrices+-- > simpleMovingAverage = map average . divvy 20 1+divvy :: Int -> Int -> [a] -> [[a]]+divvy _ _ [] = []+divvy n m lst = filter ((n ==) . length) choppedl+ where+  choppedl = chop (\xs -> (take n xs, drop m xs)) lst
test/Properties.hs view
@@ -1,22 +1,30 @@-{-# LANGUAGE FlexibleInstances  #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE StandaloneDeriving #-}+ module Main where -import           Data.List.Split.Internals-import           Test.QuickCheck-import           Test.QuickCheck.Function+import Data.List.Split.Internals+import Test.QuickCheck+import Test.QuickCheck.Function -import           Control.Monad-import           System.Environment-import           Text.Printf+import Control.Monad+import System.Environment+import Text.Printf -import           Data.Char-import           Data.Functor-import           Data.List                 (genericTake, group, intercalate, isInfixOf, isPrefixOf,- isSuffixOf, tails)-import           Data.Maybe                (isJust)+import Data.Char+import Data.Functor+import Data.List (+  genericTake,+  group,+  intercalate,+  isInfixOf,+  isPrefixOf,+  isSuffixOf,+  tails,+ )+import Data.Maybe (isJust) -newtype Elt = Elt { unElt :: Char }+newtype Elt = Elt {unElt :: Char}   deriving (Eq)  instance Show Elt where@@ -37,12 +45,14 @@   show (Delimiter ps) = show (map function ps)  instance (Arbitrary a, CoArbitrary a, Function a) => Arbitrary (Delimiter a) where-  arbitrary = (Delimiter . map apply) <$> arbitrary+  arbitrary = Delimiter . map apply <$> arbitrary -instance Arbitrary a => Arbitrary (Chunk a) where-  arbitrary = oneof [ liftM Text (listOf arbitrary)-                    , liftM Delim (listOf arbitrary)-                    ]+instance (Arbitrary a) => Arbitrary (Chunk a) where+  arbitrary =+    oneof+      [ Text <$> listOf arbitrary+      , Delim <$> listOf arbitrary+      ]  instance Arbitrary DelimPolicy where   arbitrary = elements [Drop, Keep, KeepLeft, KeepRight]@@ -63,91 +73,99 @@  main :: IO () main = do-    results <- mapM (\(s,t) -> printf "%-40s" s >> t) tests-    when (not . all isSuccess $ results) $ fail "Not all tests passed!"+  results <- mapM (\(s, t) -> printf "%-40s" s >> t) tests+  unless (all isSuccess results) $ fail "Not all tests passed!"  where-    isSuccess (Success{}) = True-    isSuccess _ = False-    qc x = quickCheckWithResult (stdArgs { maxSuccess = 200 }) x-    tests = [ ("default/id",                    qc prop_default_id)-            , ("match/decompose",               qc prop_match_decompose)-            , ("match/yields delim",            qc prop_match_yields_delim)-            , ("splitInternal/lossless",        qc prop_splitInternal_lossless)-            , ("splitInternal/yields delims",   qc prop_splitInternal_yields_delims)-            , ("splitInternal/text",            qc prop_splitInternal_text_not_delims)-            , ("doCondense/no consec delims",   qc prop_doCondense_no_consec_delims)-            , ("insBlanks/no consec delims",    qc prop_insBlanks_no_consec_delims)-            , ("insBlanks/fl not delims",       qc prop_insBlanks_fl_not_delim)-            , ("mergeL/no delims",              qc prop_mergeL_no_delims)-            , ("mergeR/no delims",              qc prop_mergeR_no_delims)-            , ("oneOf",                         qc prop_oneOf)-            , ("oneOf/not text",                qc prop_oneOf_not_text)-            , ("onSublist",                     qc prop_onSublist)-            , ("onSublist/not text",            qc prop_onSublist_not_text)-            , ("whenElt",                       qc prop_whenElt)-            , ("whenElt/not text",              qc prop_whenElt_not_text)-            , ("process/dropDelims",            qc prop_dropDelims)-            , ("process/keepDelimsL no delims", qc prop_keepDelimsL_no_delims)-            , ("process/keepDelimsR no delims", qc prop_keepDelimsR_no_delims)-            , ("process/keepDelimsL match",     qc prop_keepDelimsL_match)-            , ("process/keepDelimsR match",     qc prop_keepDelimsR_match)-            , ("condense/no consec delims",     qc prop_condense_no_consec_delims)-            , ("condense/all delims",           qc prop_condense_all_delims)-            , ("dropInitBlank",                 qc prop_dropInitBlank)-            , ("dropFinalBlank",                qc prop_dropFinalBlank)-            , ("dropBlanks",                    qc prop_dropBlanks)-            , ("startsWith",                    qc prop_startsWith)-            , ("startsWithOneOf",               qc prop_startsWithOneOf)-            , ("endsWith",                      qc prop_endsWith)-            , ("endsWithOneOf",                 qc prop_endsWithOneOf)-            , ("splitOn/right inv",             qc prop_splitOn_right_inv)-            , ("splitOn/idem",                  qc prop_splitOn_intercalate_idem)-            , ("splitOn/empty delim",           qc prop_splitOn_empty_delim)-            , ("split/empty delim",             qc prop_split_empty_delim_drop)-            , ("chunksOf/lengths",              qc prop_chunksOf_all_n)-            , ("chunksOf/last <= n",            qc prop_chunksOf_last_less_n)-            , ("chunksOf/preserve",             qc prop_chunksOf_preserve)-            , ("splitPlaces/lengths",           qc prop_splitPlaces_lengths)-            , ("splitPlaces/last <= n",         qc prop_splitPlaces_last_less_n)-            , ("splitPlaces/preserve",          qc prop_splitPlaces_preserve)-            , ("splitPlaces/chunksOf",          qc prop_splitPlaces_chunksOf)-            , ("splitPlacesB/length",           qc prop_splitPlacesB_length)-            , ("splitPlacesB/last <= n",        qc prop_splitPlacesB_last_less_n)-            , ("splitPlacesB/preserve",         qc prop_splitPlacesB_preserve)-            , ("lines",                         qc prop_lines)-            , ("wordsBy/words",                 qc prop_wordsBy_words)-            , ("linesBy/lines",                 qc prop_linesBy_lines)-            , ("chop/group",                    qc prop_chop_group)-            , ("chop/words",                    qc prop_chop_words)-            ]+  isSuccess (Success {}) = True+  isSuccess _ = False+  qc x = quickCheckWithResult (stdArgs {maxSuccess = 200}) x+  tests =+    [ ("default/id", qc prop_default_id)+    , ("match/decompose", qc prop_match_decompose)+    , ("match/yields delim", qc prop_match_yields_delim)+    , ("splitInternal/lossless", qc prop_splitInternal_lossless)+    , ("splitInternal/yields delims", qc prop_splitInternal_yields_delims)+    , ("splitInternal/text", qc prop_splitInternal_text_not_delims)+    , ("doCondense/no consec delims", qc prop_doCondense_no_consec_delims)+    , ("insBlanks/no consec delims", qc prop_insBlanks_no_consec_delims)+    , ("insBlanks/fl not delims", qc prop_insBlanks_fl_not_delim)+    , ("mergeL/no delims", qc prop_mergeL_no_delims)+    , ("mergeR/no delims", qc prop_mergeR_no_delims)+    , ("oneOf", qc prop_oneOf)+    , ("oneOf/not text", qc prop_oneOf_not_text)+    , ("onSublist", qc prop_onSublist)+    , ("onSublist/not text", qc prop_onSublist_not_text)+    , ("whenElt", qc prop_whenElt)+    , ("whenElt/not text", qc prop_whenElt_not_text)+    , ("process/dropDelims", qc prop_dropDelims)+    , ("process/keepDelimsL no delims", qc prop_keepDelimsL_no_delims)+    , ("process/keepDelimsR no delims", qc prop_keepDelimsR_no_delims)+    , ("process/keepDelimsL match", qc prop_keepDelimsL_match)+    , ("process/keepDelimsR match", qc prop_keepDelimsR_match)+    , ("condense/no consec delims", qc prop_condense_no_consec_delims)+    , ("condense/all delims", qc prop_condense_all_delims)+    , ("dropInitBlank", qc prop_dropInitBlank)+    , ("dropFinalBlank", qc prop_dropFinalBlank)+    , ("dropBlanks", qc prop_dropBlanks)+    , ("startsWith", qc prop_startsWith)+    , ("startsWithOneOf", qc prop_startsWithOneOf)+    , ("endsWith", qc prop_endsWith)+    , ("endsWithOneOf", qc prop_endsWithOneOf)+    , ("splitOn/right inv", qc prop_splitOn_right_inv)+    , ("splitOn/idem", qc prop_splitOn_intercalate_idem)+    , ("splitOn/empty delim", qc prop_splitOn_empty_delim)+    , ("split/empty delim", qc prop_split_empty_delim_drop)+    , ("chunksOf/lengths", qc prop_chunksOf_all_n)+    , ("chunksOf/last <= n", qc prop_chunksOf_last_less_n)+    , ("chunksOf/preserve", qc prop_chunksOf_preserve)+    , ("splitPlaces/lengths", qc prop_splitPlaces_lengths)+    , ("splitPlaces/last <= n", qc prop_splitPlaces_last_less_n)+    , ("splitPlaces/preserve", qc prop_splitPlaces_preserve)+    , ("splitPlaces/chunksOf", qc prop_splitPlaces_chunksOf)+    , ("splitPlacesB/length", qc prop_splitPlacesB_length)+    , ("splitPlacesB/last <= n", qc prop_splitPlacesB_last_less_n)+    , ("splitPlacesB/preserve", qc prop_splitPlacesB_preserve)+    , ("lines", qc prop_lines)+    , ("wordsBy/words", qc prop_wordsBy_words)+    , ("linesBy/lines", qc prop_linesBy_lines)+    , ("chop/group", qc prop_chop_group)+    , ("chop/words", qc prop_chop_words)+    , ("divvy/evenly", qc prop_divvy_evenly)+    , ("divvy/discard_remainder", qc prop_divvy_discard_remainder)+    , ("divvy/outputlists_allsame_length", qc prop_divvy_outputlists_allsame_length)+    , ("divvy/output_are_sublists", qc prop_divvy_output_are_sublists)+    , ("divvy/heads", qc prop_divvy_heads)+    ]  prop_default_id :: [Elt] -> Bool prop_default_id l = split defaultSplitter l == [l]  prop_match_decompose :: Delim Elt -> [Elt] -> Bool-prop_match_decompose d l = maybe True ((==l) . uncurry (++)) $ matchDelim (unDelim d) l+prop_match_decompose d l = maybe True ((== l) . uncurry (++)) $ matchDelim (unDelim d) l  isDelimMatch :: Delim Elt -> [Elt] -> Bool-isDelimMatch d l = matchDelim (unDelim d) l == Just (l,[])+isDelimMatch d l = matchDelim (unDelim d) l == Just (l, [])  prop_match_yields_delim :: Delim Elt -> [Elt] -> Bool prop_match_yields_delim d l =-    case matchDelim (unDelim d) l of-      Nothing -> True-      Just (del,rest) -> isDelimMatch d del+  case matchDelim (unDelim d) l of+    Nothing -> True+    Just (del, rest) -> isDelimMatch d del  prop_splitInternal_lossless :: Delim Elt -> [Elt] -> Bool prop_splitInternal_lossless d l = concatMap fromElem (splitInternal (unDelim d) l) == l  prop_splitInternal_yields_delims :: Delim Elt -> [Elt] -> Bool prop_splitInternal_yields_delims d l =-    all (isDelimMatch d) $ [ del | (Delim del) <- splitInternal d' l ]-  where d' = unDelim d+  all (isDelimMatch d) $ [del | (Delim del) <- splitInternal d' l]+ where+  d' = unDelim d  prop_splitInternal_text_not_delims :: Delim Elt -> [Elt] -> Bool prop_splitInternal_text_not_delims d l =-    all (not . isDelimMatch d) $ [ ch | (Text ch) <- splitInternal d' l ]-  where d' = unDelim d+  not (any (isDelimMatch d) ([ch | (Text ch) <- splitInternal d' l]))+ where+  d' = unDelim d  noConsecDelims :: SplitList Elt -> Bool noConsecDelims [] = True@@ -159,91 +177,101 @@ prop_doCondense_no_consec_delims l = noConsecDelims $ doCondense Condense l  prop_insBlanks_no_consec_delims :: SplitList Elt -> Bool-prop_insBlanks_no_consec_delims l = noConsecDelims $ insertBlanks l+prop_insBlanks_no_consec_delims l = noConsecDelims $ insertBlanks Condense l  prop_insBlanks_fl_not_delim :: SplitList Elt -> Bool prop_insBlanks_fl_not_delim l =-    case insertBlanks l of-      [] -> True-      xs -> (not . isDelim $ head xs) && (not . isDelim $ last xs)+  case insertBlanks Condense l of+    [] -> True+    xs -> (not . isDelim $ head xs) && (not . isDelim $ last xs)  prop_mergeL_no_delims :: SplitList Elt -> Bool-prop_mergeL_no_delims = all (not . isDelim) . mergeLeft . insertBlanks+prop_mergeL_no_delims = not . any isDelim . mergeLeft . insertBlanks Condense  prop_mergeR_no_delims :: SplitList Elt -> Bool-prop_mergeR_no_delims = all (not . isDelim) . mergeRight . insertBlanks+prop_mergeR_no_delims = not . any isDelim . mergeRight . insertBlanks Condense  getDelims :: Splitter Elt -> [Elt] -> [[Elt]]-getDelims s l = [ d | Delim d <- splitInternal (delimiter s) l ]+getDelims s l = [d | Delim d <- splitInternal (delimiter s) l]  getTexts :: Splitter Elt -> [Elt] -> [[Elt]]-getTexts s l = [ c | Text c <- splitInternal (delimiter s) l ]+getTexts s l = [c | Text c <- splitInternal (delimiter s) l]  prop_oneOf :: [Elt] -> [Elt] -> Bool-prop_oneOf elts l = all ((==1) . length) ds && all ((`elem` elts) . head) ds-  where ds = getDelims (oneOf elts) l+prop_oneOf elts l = all ((== 1) . length) ds && all ((`elem` elts) . head) ds+ where+  ds = getDelims (oneOf elts) l  prop_oneOf_not_text :: [Elt] -> [Elt] -> Bool-prop_oneOf_not_text elts l = all (not . (`elem` elts)) (concat cs)-  where cs = getTexts (oneOf elts) l+prop_oneOf_not_text elts l = not (any (`elem` elts) (concat cs))+ where+  cs = getTexts (oneOf elts) l  prop_onSublist :: [Elt] -> [Elt] -> Bool-prop_onSublist sub l = all (==sub) $ getDelims (onSublist sub) l+prop_onSublist sub l = all (== sub) $ getDelims (onSublist sub) l  prop_onSublist_not_text :: [Elt] -> [Elt] -> Property prop_onSublist_not_text sub l =-    (not . null $ sub) ==>-      all (not . isInfixOf sub) $ getTexts (onSublist sub) l+  (not . null $ sub)+    ==> not (any (isInfixOf sub) $ getTexts (onSublist sub) l) -prop_whenElt :: (Fun Elt Bool) -> [Elt] -> Bool-prop_whenElt (Fun _ p) l = all ((==1) . length) ds && all (p . head) ds-  where ds = getDelims (whenElt p) l+prop_whenElt :: Fun Elt Bool -> [Elt] -> Bool+prop_whenElt (Fun _ p) l = all ((== 1) . length) ds && all (p . head) ds+ where+  ds = getDelims (whenElt p) l -prop_whenElt_not_text :: (Fun Elt Bool) -> [Elt] -> Bool-prop_whenElt_not_text (Fun _ p) l = all (not . p) (concat cs)-  where cs = getTexts (whenElt p) l+prop_whenElt_not_text :: Fun Elt Bool -> [Elt] -> Bool+prop_whenElt_not_text (Fun _ p) l = not (any p (concat cs))+ where+  cs = getTexts (whenElt p) l  process :: Splitter Elt -> [Elt] -> SplitList Elt process s = postProcess s . splitInternal (delimiter s)  prop_dropDelims :: Splitter Elt -> [Elt] -> Bool-prop_dropDelims s l = all (not . isDelim) (process (dropDelims s) l)+prop_dropDelims s l = not (any isDelim (process (dropDelims s) l))  prop_keepDelimsL_no_delims :: Splitter Elt -> [Elt] -> Bool-prop_keepDelimsL_no_delims s l = all (not . isDelim) (process (keepDelimsL s) l)+prop_keepDelimsL_no_delims s l = not (any isDelim (process (keepDelimsL s) l))  prop_keepDelimsL_match :: Splitter Elt -> NonEmptyList Elt -> Bool prop_keepDelimsL_match s (NonEmpty l) =-  all (isJust . matchDelim (delimiter s)) [ c | Text c <- tail p ]-    where p = process (keepDelimsL s) l+  all (isJust . matchDelim (delimiter s)) [c | Text c <- tail p]+ where+  p = process (keepDelimsL s) l  prop_keepDelimsR_no_delims :: Splitter Elt -> [Elt] -> Bool-prop_keepDelimsR_no_delims s l = all (not . isDelim) (process (keepDelimsR s) l)+prop_keepDelimsR_no_delims s l = not (any isDelim (process (keepDelimsR s) l))  prop_keepDelimsR_match :: Splitter Elt -> NonEmptyList Elt -> Bool prop_keepDelimsR_match s (NonEmpty l) =-  all (any (isJust . matchDelim (delimiter s)) . tails)-    [ c | Text c <- init p ]-      where p = process (keepDelimsR s) l+  all+    (any (isJust . matchDelim (delimiter s)) . tails)+    [c | Text c <- init p]+ where+  p = process (keepDelimsR s) l  prop_condense_no_consec_delims :: Splitter Elt -> [Elt] -> Bool prop_condense_no_consec_delims s l = noConsecDelims $ process (condense s) l  prop_condense_all_delims :: Splitter Elt -> [Elt] -> Bool prop_condense_all_delims s l = all allDelims p-  where p = [ d | Delim d <- process (condense s) l ]-        allDelims t = all isDelim (splitInternal (delimiter s) t)+ where+  p = [d | Delim d <- process (condense s) l]+  allDelims t = all isDelim (splitInternal (delimiter s) t)  prop_dropInitBlank :: Splitter Elt -> NonEmptyList Elt -> Bool prop_dropInitBlank s (NonEmpty l) = head p /= Text []-  where p = process (dropInitBlank $ s { delimPolicy = Keep } ) l+ where+  p = process (dropInitBlank $ s {delimPolicy = Keep}) l  prop_dropFinalBlank :: Splitter Elt -> NonEmptyList Elt -> Bool prop_dropFinalBlank s (NonEmpty l) = last p /= Text []-  where p = process (dropFinalBlank $ s { delimPolicy = Keep } ) l+ where+  p = process (dropFinalBlank $ s {delimPolicy = Keep}) l  prop_dropBlanks :: Splitter Elt -> [Elt] -> Bool-prop_dropBlanks s = null . filter (== (Text [])) . process (dropBlanks s)+prop_dropBlanks s = notElem (Text []) . process (dropBlanks s)  prop_startsWith :: [Elt] -> NonEmptyList Elt -> Bool prop_startsWith s (NonEmpty l) = all (s `isPrefixOf`) (tail $ split (startsWith s) l)@@ -267,7 +295,6 @@    so it's not enough just to say that the delimiter is not an infix of    any elements of l! - prop_splitOn_left_inv :: [Elt] -> NonEmptyList [Elt] -> Property prop_splitOn_left_inv x (NonEmpty ls) = not (any (x `isInfixOf`) ls) ==>                                         splitOn x (intercalate x ls) == ls@@ -277,52 +304,59 @@ -- prop_splitOn_right_inv, but we keep it here just for kicks. prop_splitOn_intercalate_idem :: [Elt] -> [[Elt]] -> Bool prop_splitOn_intercalate_idem x ls = f (f ls) == f ls-  where f = splitOn x . intercalate x+ where+  f = splitOn x . intercalate x  prop_splitOn_empty_delim :: [Elt] -> Bool-prop_splitOn_empty_delim ls = splitOn [] ls == [] : map (:[]) ls+prop_splitOn_empty_delim ls = splitOn [] ls == [] : map (: []) ls  prop_split_empty_delim_drop :: [Elt] -> Bool-prop_split_empty_delim_drop ls-  = split (dropDelims . dropBlanks $ onSublist []) ls == map (:[]) ls+prop_split_empty_delim_drop ls =+  split (dropDelims . dropBlanks $ onSublist []) ls == map (: []) ls  prop_chunksOf_all_n :: Positive Int -> NonEmptyList Elt -> Bool-prop_chunksOf_all_n (Positive n) (NonEmpty l) = all ((==n) . length) (init $ chunksOf n l)+prop_chunksOf_all_n (Positive n) (NonEmpty l) = all ((== n) . length) (init $ chunksOf n l)  prop_chunksOf_last_less_n :: Positive Int -> NonEmptyList Elt -> Bool-prop_chunksOf_last_less_n (Positive n) (NonEmpty l) = (<=n) . length . last $ chunksOf n l+prop_chunksOf_last_less_n (Positive n) (NonEmpty l) = (<= n) . length . last $ chunksOf n l  prop_chunksOf_preserve :: Positive Int -> [Elt] -> Bool prop_chunksOf_preserve (Positive n) l = concat (chunksOf n l) == l  prop_splitPlaces_lengths :: [NonNegative Int] -> [Elt] -> Bool prop_splitPlaces_lengths ps = and . mInit . zipWith (==) ps' . map length . splitPlaces ps'-  where ps' = map unNN ps+ where+  ps' = map unNN ps  prop_splitPlaces_last_less_n :: NonEmptyList (NonNegative Int) -> NonEmptyList Elt -> Bool-prop_splitPlaces_last_less_n (NonEmpty ps) (NonEmpty l) = (head $ drop (length l' - 1) ps') >= length (last l')-  where l' = splitPlaces ps' l-        ps' = map unNN ps+prop_splitPlaces_last_less_n (NonEmpty ps) (NonEmpty l) = (ps' !! max 0 (length l' - 1)) >= length (last l')+ where+  l' = splitPlaces ps' l+  ps' = map unNN ps  prop_splitPlaces_preserve :: [NonNegative Integer] -> [Elt] -> Bool prop_splitPlaces_preserve ps l = concat (splitPlaces ps' l) == genericTake (sum ps') l-  where ps' = map unNN ps+ where+  ps' = map unNN ps  prop_splitPlaces_chunksOf :: Positive Int -> [Elt] -> Bool prop_splitPlaces_chunksOf (Positive n) l = splitPlaces (repeat n) l == chunksOf n l  prop_splitPlacesB_length :: [NonNegative Int] -> [Elt] -> Bool prop_splitPlacesB_length ps xs = length ps' == length (splitPlacesBlanks ps' xs)-  where ps' = map unNN ps+ where+  ps' = map unNN ps  prop_splitPlacesB_last_less_n :: NonEmptyList (NonNegative Int) -> NonEmptyList Elt -> Bool-prop_splitPlacesB_last_less_n (NonEmpty ps) (NonEmpty l) = (head $ drop (length l' - 1) ps') >= length (last l')-  where l' = splitPlacesBlanks ps' l-        ps' = map unNN ps+prop_splitPlacesB_last_less_n (NonEmpty ps) (NonEmpty l) = (ps' !! max 0 (length l' - 1)) >= length (last l')+ where+  l' = splitPlacesBlanks ps' l+  ps' = map unNN ps  prop_splitPlacesB_preserve :: [NonNegative Integer] -> [Elt] -> Bool prop_splitPlacesB_preserve ps l = concat (splitPlacesBlanks ps' l) == genericTake (sum ps') l-  where ps' = map unNN ps+ where+  ps' = map unNN ps  unNN :: NonNegative a -> a unNN (NonNegative x) = x@@ -330,9 +364,9 @@ mInit :: [a] -> [a] mInit [] = [] mInit [x] = []-mInit (x:xs) = x : init xs+mInit (x : xs) = x : init xs -newtype EltWS = EltWS { unEltWS :: Char }+newtype EltWS = EltWS {unEltWS :: Char}   deriving (Eq, Show)  instance Arbitrary EltWS where@@ -340,19 +374,64 @@  prop_lines :: [EltWS] -> Bool prop_lines s = lines s' == endBy "\n" s'-  where s' = map unEltWS s+ where+  s' = map unEltWS s  prop_wordsBy_words :: [EltWS] -> Bool prop_wordsBy_words s = words s' == wordsBy isSpace s'-  where s' = map unEltWS s+ where+  s' = map unEltWS s  prop_linesBy_lines :: [EltWS] -> Bool-prop_linesBy_lines s = lines s' == linesBy (=='\n') s'-  where s' = map unEltWS s+prop_linesBy_lines s = lines s' == linesBy (== '\n') s'+ where+  s' = map unEltWS s  prop_chop_group :: [Elt] -> Bool-prop_chop_group s = chop (\xs@(x:_) -> span (==x) xs) s == group s+prop_chop_group s = chop (\xs@(x : _) -> span (== x) xs) s == group s  prop_chop_words :: [EltWS] -> Bool-prop_chop_words s = words s' == (filter (not . null) . chop (span (not . isSpace) . dropWhile isSpace) $ s')-  where s' = map unEltWS s+prop_chop_words s = words s' == (filter (not . null) . chop (break isSpace . dropWhile isSpace) $ s')+ where+  s' = map unEltWS s++prop_divvy_evenly :: [Elt] -> Positive Int -> Bool+prop_divvy_evenly elems (Positive n) = concat (divvy n n elems') == elems'+ where+  -- Chop off the smallest possible tail of elems to make the length+  -- evenly divisible by n.  This property used to have a+  -- precondition (length elements `mod` n == 0), but that led to too+  -- many discarded test cases and occasional test suite failures.+  elems' = take ((length elems `div` n) * n) elems++prop_divvy_discard_remainder :: [Elt] -> Positive Int -> Bool+prop_divvy_discard_remainder elems (Positive n) =+  concat (divvy n n elems) == (reverse . drop (length elems `mod` n) . reverse $ elems)++prop_divvy_outputlists_allsame_length :: [Elt] -> Positive Int -> Positive Int -> Bool+prop_divvy_outputlists_allsame_length elems (Positive n) (Positive m) = allSame xs+ where+  allSame :: [Int] -> Bool+  allSame [] = True+  allSame zs = all (== head zs) (tail zs)+  xs = map length (divvy n m elems)++prop_divvy_output_are_sublists :: [Elt] -> Positive Int -> Positive Int -> Bool+prop_divvy_output_are_sublists elems (Positive n) (Positive m) = all (`isInfixOf` elems) xs+ where+  xs = divvy n m elems++takeEvery :: Int -> [a] -> [a]+takeEvery _ [] = []+takeEvery n lst = (map head . chunksOf n) lst++initNth :: Int -> [a] -> [a]+initNth _ [] = []+initNth n lst = (reverse . drop n . reverse) lst++prop_divvy_heads :: [Elt] -> Positive Int -> Positive Int -> Bool+prop_divvy_heads [] _ _ = True+prop_divvy_heads elems (Positive n) (Positive m) = hds1 == hds2+ where+  hds1 = takeEvery m (initNth (n - 1) elems)+  hds2 = map head $ divvy n m elems