packages feed

explainable-predicates (empty) → 0.1.0.0

raw patch · 10 files changed

+2648/−0 lines, 10 filesdep +arraydep +basedep +doctest-exitcode-stdio

Dependencies added: array, base, doctest-exitcode-stdio, doctest-lib, explainable-predicates, hspec, mono-traversable, regex-tdfa, syb, template-haskell

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for explainable-predicates++## 0.1.0.0 -- 2021-09-18++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2021, Chris Smith++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Chris Smith nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ explainable-predicates.cabal view
@@ -0,0 +1,56 @@+cabal-version:      2.4+name:               explainable-predicates+version:            0.1.0.0+synopsis:           Predicates that can explain themselves.+description:        Explainable predicates are essentially functions from types+                    to 'Bool' which can additionally describe themselves and+                    explain why an argument does or doesn't match.  They are+                    intended to be used during unit tests to provide better+                    error messages when tests fail.  For example, if a+                    collection is missing an element, an explainable predicate+                    can tell you which element is missing.+category:           Testing+homepage:           https://github.com/cdsmith/explainable-predicates+bug-reports:        https://github.com/cdsmith/explainable-predicates/issues+license:            BSD-3-Clause+license-file:       LICENSE++author:             Chris Smith <cdsmith@gmail.com>+maintainer:         Chris Smith <cdsmith@gmail.com>++extra-source-files: CHANGELOG.md++tested-with:        GHC == 8.4.4 || == 8.6.5 || == 8.8.4 || == 8.10.4 || == 9.0.1++source-repository head+    type:     git+    location: git://github.com/cdsmith/explainable-predicates.git++library+    exposed-modules:  Test.Predicates,+                      Test.Predicates.Internal.FlowMatcher+    other-modules:    Test.Predicates.Internal.Util+    build-depends:    array >= 0.5.2 && < 0.6,+                      base >=4.11.0 && < 4.16,+                      mono-traversable >= 1.0.15 && < 1.1,+                      regex-tdfa >= 1.3.1 && < 1.4,+                      syb >= 0.7.2 && < 0.8,+                      template-haskell >= 2.13.0 && < 2.18,+    hs-source-dirs:   src+    default-language: Haskell2010+    ghc-options:      -Wall -Wcompat -Wincomplete-uni-patterns++test-suite tests+    type:             exitcode-stdio-1.0+    main-is:          Main.hs+    other-modules:    DocTests.All,+                      DocTests.Test.Predicates,+                      DocTests.Test.Predicates.Internal.FlowMatcher+    build-depends:    base,+                      doctest-exitcode-stdio,+                      doctest-lib,+                      explainable-predicates,+                      hspec,+    hs-source-dirs:   test+    default-language: Haskell2010+    ghc-options:      -threaded -Wall -Wcompat -Wincomplete-uni-patterns
+ src/Test/Predicates.hs view
@@ -0,0 +1,1312 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE ParallelListComp #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}++-- | Explainable 'Predicate's are essentially functions from types to `Bool`+-- which can additionally describe themselves and explain why an argument does+-- or doesn't match.  They are intended to be used during unit tests to provide+-- better error messages when tests fail.+module Test.Predicates+  ( -- * The Predicate type+    Predicate (..),++    -- * Predicate combinators+    anything,+    eq,+    neq,+    gt,+    geq,+    lt,+    leq,+    just,+    nothing,+    left,+    right,+    zipP,+    zip3P,+    zip4P,+    zip5P,+    andP,+    orP,+    notP,+    startsWith,+    endsWith,+    hasSubstr,+    hasSubsequence,+    caseInsensitive,+    matchesRegex,+    matchesCaseInsensitiveRegex,+    containsRegex,+    containsCaseInsensitiveRegex,+    isEmpty,+    nonEmpty,+    sizeIs,+    elemsAre,+    unorderedElemsAre,+    each,+    contains,+    containsAll,+    containsOnly,+    keys,+    values,+    approxEq,+    positive,+    negative,+    nonPositive,+    nonNegative,+    finite,+    infinite,+    nAn,+    is,+    qIs,+    with,+    qWith,+    qMatch,+    typed,+  )+where++import Data.Char (toUpper)+import Data.List (intercalate)+import Data.Maybe (catMaybes, isJust, isNothing)+import Data.MonoTraversable (Element, MonoFoldable (..), MonoFunctor (..))+import qualified Data.Sequences as Seq+import Data.Typeable (Proxy (..), Typeable, cast, typeRep)+import GHC.Exts (IsList (Item, toList))+import GHC.Stack (HasCallStack, callStack)+import Language.Haskell.TH (ExpQ, PatQ, pprint)+import Language.Haskell.TH.Syntax (lift)+import Test.Predicates.Internal.FlowMatcher (bipartiteMatching)+import Test.Predicates.Internal.Util+  ( isSubsequenceOf,+    locate,+    removeModNames,+    withLoc,+  )+import Text.Regex.TDFA+  ( CompOption (caseSensitive, lastStarGreedy, newSyntax),+    ExecOption (captureGroups),+    Extract (empty),+    Regex,+    RegexLike (matchOnce, matchOnceText),+    RegexMaker (makeRegexOpts),+    RegexOptions (defaultCompOpt, defaultExecOpt),+  )++-- $setup+-- >>> :set -XTemplateHaskell+-- >>> :set -XTypeApplications+-- >>> :set -Wno-type-defaults++-- | A predicate, which tests values and either accepts or rejects them.  This+-- is similar to @a -> 'Bool'@, but also can describe itself and explain why an+-- argument does or doesn't match.+data Predicate a = Predicate+  { showPredicate :: String,+    showNegation :: String,+    accept :: a -> Bool,+    explain :: a -> String+  }++instance Show (Predicate a) where show = showPredicate++withDefaultExplain ::+  (a -> String) -> String -> ((a -> String) -> Predicate a) -> Predicate a+withDefaultExplain format connector mk = p+  where+    p = mk $ \x ->+      if accept p x+        then format x ++ connector ++ showPredicate p+        else format x ++ connector ++ showNegation p++-- | A 'Predicate' that accepts anything at all.+--+-- >>> accept anything "foo"+-- True+-- >>> accept anything undefined+-- True+anything :: Predicate a+anything =+  Predicate+    { showPredicate = "anything",+      showNegation = "nothing",+      accept = const True,+      explain = const "always matches"+    }++-- | A 'Predicate' that accepts only the given value.+--+-- >>> accept (eq "foo") "foo"+-- True+-- >>> accept (eq "foo") "bar"+-- False+eq :: (Show a, Eq a) => a -> Predicate a+eq x =+  Predicate+    { showPredicate = show x,+      showNegation = "≠ " ++ show x,+      accept = (== x),+      explain = \y ->+        if y == x+          then show y ++ " = " ++ show x+          else show y ++ " ≠ " ++ show x+    }++-- | A 'Predicate' that accepts anything but the given value.+--+-- >>> accept (neq "foo") "foo"+-- False+-- >>> accept (neq "foo") "bar"+-- True+neq :: (Show a, Eq a) => a -> Predicate a+neq = notP . eq++-- | A 'Predicate' that accepts anything greater than the given value.+--+-- >>> accept (gt 5) 4+-- False+-- >>> accept (gt 5) 5+-- False+-- >>> accept (gt 5) 6+-- True+gt :: (Show a, Ord a) => a -> Predicate a+gt x = withDefaultExplain show " " $ \explainImpl ->+  Predicate+    { showPredicate = "> " ++ show x,+      showNegation = "≤ " ++ show x,+      accept = (> x),+      explain = explainImpl+    }++-- | A 'Predicate' that accepts anything greater than or equal to the given+-- value.+--+-- >>> accept (geq 5) 4+-- False+-- >>> accept (geq 5) 5+-- True+-- >>> accept (geq 5) 6+-- True+geq :: (Show a, Ord a) => a -> Predicate a+geq x = withDefaultExplain show " " $ \explainImpl ->+  Predicate+    { showPredicate = "≥ " ++ show x,+      showNegation = "< " ++ show x,+      accept = (>= x),+      explain = explainImpl+    }++-- | A 'Predicate' that accepts anything less than the given value.+--+-- >>> accept (lt 5) 4+-- True+-- >>> accept (lt 5) 5+-- False+-- >>> accept (lt 5) 6+-- False+lt :: (Show a, Ord a) => a -> Predicate a+lt = notP . geq++-- | A 'Predicate' that accepts anything less than or equal to the given value.+--+-- >>> accept (leq 5) 4+-- True+-- >>> accept (leq 5) 5+-- True+-- >>> accept (leq 5) 6+-- False+leq :: (Show a, Ord a) => a -> Predicate a+leq = notP . gt++-- | A 'Predicate' that accepts 'Maybe' values of @'Just' x@, where @x@ matches+-- the given child 'Predicate'.+--+-- >>> accept (just (eq "value")) Nothing+-- False+-- >>> accept (just (eq "value")) (Just "value")+-- True+-- >>> accept (just (eq "value")) (Just "wrong value")+-- False+just :: Predicate a -> Predicate (Maybe a)+just p =+  Predicate+    { showPredicate = "Just (" ++ showPredicate p ++ ")",+      showNegation = "not Just (" ++ showPredicate p ++ ")",+      accept = \case Just x -> accept p x; _ -> False,+      explain = \case Just x -> explain p x; _ -> "Nothing ≠ Just _"+    }++-- | A Predicate that accepts 'Maybe' values of @'Nothing'@.  Unlike 'eq', this+-- doesn't require 'Eq' or 'Show' instances.+--+-- >>> accept nothing Nothing+-- True+--+-- >>> accept nothing (Just "something")+-- False+nothing :: Predicate (Maybe a)+nothing =+  Predicate+    { showPredicate = "Nothing",+      showNegation = "Just anything",+      accept = isNothing,+      explain = \case Nothing -> "Nothing = Nothing"; _ -> "Just _ ≠ Nothing"+    }++-- | A 'Predicate' that accepts an 'Either' value of @'Left' x@, where @x@+-- matches the given child 'Predicate'.+--+-- >>> accept (left (eq "value")) (Left "value")+-- True+-- >>> accept (left (eq "value")) (Right "value")+-- False+-- >>> accept (left (eq "value")) (Left "wrong value")+-- False+left :: Predicate a -> Predicate (Either a b)+left p =+  Predicate+    { showPredicate = "Left (" ++ showPredicate p ++ ")",+      showNegation = "not Left (" ++ showPredicate p ++ ")",+      accept = \case Left x -> accept p x; _ -> False,+      explain = \case Left x -> explain p x; _ -> "Right _ ≠ Left _"+    }++-- | A 'Predicate' that accepts an 'Either' value of @'Right' x@, where @x@+-- matches the given child 'Predicate'.+--+-- >>> accept (right (eq "value")) (Right "value")+-- True+-- >>> accept (right (eq "value")) (Right "wrong value")+-- False+-- >>> accept (right (eq "value")) (Left "value")+-- False+right :: Predicate b -> Predicate (Either a b)+right p =+  Predicate+    { showPredicate = "Right (" ++ showPredicate p ++ ")",+      showNegation = "not Right (" ++ showPredicate p ++ ")",+      accept = \case Right x -> accept p x; _ -> False,+      explain = \case Right x -> explain p x; _ -> "Left _ ≠ Right _"+    }++-- | A 'Predicate' that accepts pairs whose elements satisfy the corresponding+-- child 'Predicate's.+--+-- >>> accept (zipP (eq "foo") (eq "bar")) ("foo", "bar")+-- True+-- >>> accept (zipP (eq "foo") (eq "bar")) ("bar", "foo")+-- False+zipP :: Predicate a -> Predicate b -> Predicate (a, b)+zipP p1 p2 =+  Predicate+    { showPredicate = show (p1, p2),+      showNegation = "not " ++ show (p1, p2),+      accept = all fst . acceptAndExplain,+      explain = \xs ->+        let results = acceptAndExplain xs+            significant+              | all fst results = results+              | otherwise = filter (not . fst) results+         in intercalate " and " $ map snd significant+    }+  where+    acceptAndExplain = \(x1, x2) ->+      [ (accept p1 x1, explain p1 x1),+        (accept p2 x2, explain p2 x2)+      ]++-- | A 'Predicate' that accepts 3-tuples whose elements satisfy the+-- corresponding child 'Predicate's.+--+-- >>> accept (zip3P (eq "foo") (eq "bar") (eq "qux")) ("foo", "bar", "qux")+-- True+-- >>> accept (zip3P (eq "foo") (eq "bar") (eq "qux")) ("qux", "bar", "foo")+-- False+zip3P :: Predicate a -> Predicate b -> Predicate c -> Predicate (a, b, c)+zip3P p1 p2 p3 =+  Predicate+    { showPredicate = show (p1, p2, p3),+      showNegation = "not " ++ show (p1, p2, p3),+      accept = all fst . acceptAndExplain,+      explain = \xs ->+        let results = acceptAndExplain xs+            significant+              | all fst results = results+              | otherwise = filter (not . fst) results+         in intercalate " and " $ map snd significant+    }+  where+    acceptAndExplain = \(x1, x2, x3) ->+      [ (accept p1 x1, explain p1 x1),+        (accept p2 x2, explain p2 x2),+        (accept p3 x3, explain p3 x3)+      ]++-- | A 'Predicate' that accepts 3-tuples whose elements satisfy the+-- corresponding child 'Predicate's.+--+-- >>> accept (zip4P (eq 1) (eq 2) (eq 3) (eq 4)) (1, 2, 3, 4)+-- True+-- >>> accept (zip4P (eq 1) (eq 2) (eq 3) (eq 4)) (4, 3, 2, 1)+-- False+zip4P ::+  Predicate a ->+  Predicate b ->+  Predicate c ->+  Predicate d ->+  Predicate (a, b, c, d)+zip4P p1 p2 p3 p4 =+  Predicate+    { showPredicate = show (p1, p2, p3, p4),+      showNegation = "not " ++ show (p1, p2, p3, p4),+      accept = all fst . acceptAndExplain,+      explain = \xs ->+        let results = acceptAndExplain xs+            significant+              | all fst results = results+              | otherwise = filter (not . fst) results+         in intercalate " and " $ map snd significant+    }+  where+    acceptAndExplain = \(x1, x2, x3, x4) ->+      [ (accept p1 x1, explain p1 x1),+        (accept p2 x2, explain p2 x2),+        (accept p3 x3, explain p3 x3),+        (accept p4 x4, explain p4 x4)+      ]++-- | A 'Predicate' that accepts 3-tuples whose elements satisfy the+-- corresponding child 'Predicate's.+--+-- >>> accept (zip5P (eq 1) (eq 2) (eq 3) (eq 4) (eq 5)) (1, 2, 3, 4, 5)+-- True+-- >>> accept (zip5P (eq 1) (eq 2) (eq 3) (eq 4) (eq 5)) (5, 4, 3, 2, 1)+-- False+zip5P ::+  Predicate a ->+  Predicate b ->+  Predicate c ->+  Predicate d ->+  Predicate e ->+  Predicate (a, b, c, d, e)+zip5P p1 p2 p3 p4 p5 =+  Predicate+    { showPredicate = show (p1, p2, p3, p4, p5),+      showNegation = "not " ++ show (p1, p2, p3, p4, p5),+      accept = all fst . acceptAndExplain,+      explain = \xs ->+        let results = acceptAndExplain xs+            significant+              | all fst results = results+              | otherwise = filter (not . fst) results+         in intercalate " and " $ map snd significant+    }+  where+    acceptAndExplain = \(x1, x2, x3, x4, x5) ->+      [ (accept p1 x1, explain p1 x1),+        (accept p2 x2, explain p2 x2),+        (accept p3 x3, explain p3 x3),+        (accept p4 x4, explain p4 x4),+        (accept p5 x5, explain p5 x5)+      ]++-- | A 'Predicate' that accepts anything accepted by both of its children.+--+-- >>> accept (lt "foo" `andP` gt "bar") "eta"+-- True+-- >>> accept (lt "foo" `andP` gt "bar") "quz"+-- False+-- >>> accept (lt "foo" `andP` gt "bar") "alpha"+-- False+andP :: Predicate a -> Predicate a -> Predicate a+p `andP` q =+  Predicate+    { showPredicate = showPredicate p ++ " and " ++ showPredicate q,+      showNegation = showNegation p ++ " or " ++ showNegation q,+      accept = \x -> accept p x && accept q x,+      explain = \x ->+        if+            | not (accept p x) -> explain p x+            | not (accept q x) -> explain q x+            | otherwise -> explain p x ++ " and " ++ explain q x+    }++-- | A 'Predicate' that accepts anything accepted by either of its children.+--+-- >>> accept (lt "bar" `orP` gt "foo") "eta"+-- False+-- >>> accept (lt "bar" `orP` gt "foo") "quz"+-- True+-- >>> accept (lt "bar" `orP` gt "foo") "alpha"+-- True+orP :: Predicate a -> Predicate a -> Predicate a+p `orP` q = notP (notP p `andP` notP q)++-- | A 'Predicate' that inverts another 'Predicate', accepting whatever its+-- child rejects, and rejecting whatever its child accepts.+--+-- >>> accept (notP (eq "negative")) "positive"+-- True+-- >>> accept (notP (eq "negative")) "negative"+-- False+notP :: Predicate a -> Predicate a+notP p =+  Predicate+    { showPredicate = showNegation p,+      showNegation = showPredicate p,+      accept = not . accept p,+      explain = explain p+    }++-- | A 'Predicate' that accepts sequences that start with the given prefix.+--+-- >>> accept (startsWith "fun") "fungible"+-- True+-- >>> accept (startsWith "gib") "fungible"+-- False+startsWith :: (Show t, Seq.IsSequence t, Eq (Element t)) => t -> Predicate t+startsWith pfx = withDefaultExplain show " " $ \explainImpl ->+  Predicate+    { showPredicate = "starts with " ++ show pfx,+      showNegation = "doesn't start with " ++ show pfx,+      accept = (pfx `Seq.isPrefixOf`),+      explain = explainImpl+    }++-- | A 'Predicate' that accepts sequences that end with the given suffix.+--+-- >>> accept (endsWith "ow") "crossbow"+-- True+-- >>> accept (endsWith "ow") "trebuchet"+-- False+endsWith :: (Show t, Seq.IsSequence t, Eq (Element t)) => t -> Predicate t+endsWith sfx = withDefaultExplain show " " $ \explainImpl ->+  Predicate+    { showPredicate = "ends with " ++ show sfx,+      showNegation = "doesn't end with " ++ show sfx,+      accept = (sfx `Seq.isSuffixOf`),+      explain = explainImpl+    }++-- | A 'Predicate' that accepts sequences that contain the given (consecutive)+-- substring.+--+-- >>> accept (hasSubstr "i") "team"+-- False+-- >>> accept (hasSubstr "i") "partnership"+-- True+hasSubstr :: (Show t, Seq.IsSequence t, Eq (Element t)) => t -> Predicate t+hasSubstr s = withDefaultExplain show " " $ \explainImpl ->+  Predicate+    { showPredicate = "has substring " ++ show s,+      showNegation = "doesn't have substring " ++ show s,+      accept = (s `Seq.isInfixOf`),+      explain = explainImpl+    }++-- | A 'Predicate' that accepts sequences that contain the given (not+-- necessarily consecutive) subsequence.+--+-- >>> accept (hasSubsequence [1..5]) [1, 2, 3, 4, 5]+-- True+-- >>> accept (hasSubsequence [1..5]) [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0]+-- True+-- >>> accept (hasSubsequence [1..5]) [2, 3, 5, 7, 11]+-- False+hasSubsequence :: (Show t, Seq.IsSequence t, Eq (Element t)) => t -> Predicate t+hasSubsequence s = withDefaultExplain show " " $ \explainImpl ->+  Predicate+    { showPredicate = "has subsequence " ++ show s,+      showNegation = "doesn't have subsequence " ++ show s,+      accept = (s `isSubsequenceOf`),+      explain = explainImpl+    }++-- | Transforms a 'Predicate' on 'String's or string-like types to match without+-- regard to case.+--+-- >>> accept (caseInsensitive startsWith "foo") "FOOTBALL!"+-- True+-- >>> accept (caseInsensitive endsWith "ball") "soccer"+-- False+-- >>> accept (caseInsensitive eq "time") "TIME"+-- True+-- >>> accept (caseInsensitive gt "NOTHING") "everything"+-- False+caseInsensitive ::+  ( MonoFunctor t,+    MonoFunctor a,+    Element t ~ Char,+    Element a ~ Char+  ) =>+  (t -> Predicate a) ->+  (t -> Predicate a)+caseInsensitive p s =+  Predicate+    { showPredicate = "(case insensitive) " ++ show (p s),+      showNegation = "(case insensitive) " ++ show (notP (p s)),+      accept = accept capP . omap toUpper,+      explain = explain capP . omap toUpper+    }+  where+    capP = p (omap toUpper s)++-- | A 'Predicate' that accepts 'String's or string-like values matching a+-- regular expression.  The expression must match the entire argument.+--+-- You should not use @'caseInsensitive' 'matchesRegex'@, because regular+-- expression syntax itself is still case-sensitive even when the text you are+-- matching is not.  Instead, use 'matchesCaseInsensitiveRegex'.+--+-- >>> accept (matchesRegex "x{2,5}y?") "xxxy"+-- True+-- >>> accept (matchesRegex "x{2,5}y?") "xyy"+-- False+-- >>> accept (matchesRegex "x{2,5}y?") "wxxxyz"+-- False+matchesRegex :: (RegexLike Regex a, Eq a, Show a) => String -> Predicate a+matchesRegex s =+  Predicate+    { showPredicate = pat,+      showNegation = "not " ++ pat,+      accept = accepts,+      explain = \x ->+        if accepts x+          then show x ++ " matches " ++ pat+          else show x ++ " doesn't match " ++ pat+    }+  where+    pat = "/" ++ init (tail $ show s) ++ "/"+    accepts x = case matchOnceText r x of+      Just (a, _, b) -> a == empty && b == empty+      Nothing -> False+    r = makeRegexOpts comp exec s :: Regex+    comp = defaultCompOpt {newSyntax = True, lastStarGreedy = True}+    exec = defaultExecOpt {captureGroups = False}++-- | A 'Predicate' that accepts 'String's or string-like values matching a+-- regular expression in a case-insensitive way.  The expression must match the+-- entire argument.+--+-- You should use this instead of @'caseInsensitive' 'matchesRegex'@, because+-- regular expression syntax itself is still case-sensitive even when the text+-- you are matching is not.+--+-- >>> accept (matchesCaseInsensitiveRegex "x{2,5}y?") "XXXY"+-- True+-- >>> accept (matchesCaseInsensitiveRegex "x{2,5}y?") "XYY"+-- False+-- >>> accept (matchesCaseInsensitiveRegex "x{2,5}y?") "WXXXYZ"+-- False+matchesCaseInsensitiveRegex ::+  (RegexLike Regex a, Eq a, Show a) => String -> Predicate a+matchesCaseInsensitiveRegex s =+  Predicate+    { showPredicate = pat,+      showNegation = "not " ++ pat,+      accept = accepts,+      explain = \x ->+        if accepts x+          then show x ++ " matches " ++ pat+          else show x ++ " doesn't match " ++ pat+    }+  where+    pat = "/" ++ init (tail $ show s) ++ "/i"+    accepts x = case matchOnceText r x of+      Just (a, _, b) -> a == empty && b == empty+      Nothing -> False+    r = makeRegexOpts comp exec s :: Regex+    comp =+      defaultCompOpt+        { newSyntax = True,+          lastStarGreedy = True,+          caseSensitive = False+        }+    exec = defaultExecOpt {captureGroups = False}++-- | A 'Predicate' that accepts 'String's or string-like values containing a+-- match for a regular expression.  The expression need not match the entire+-- argument.+--+-- You should not use @'caseInsensitive' 'containsRegex'@, because regular+-- expression syntax itself is still case-sensitive even when the text you are+-- matching is not.  Instead, use 'containsCaseInsensitiveRegex'.+--+-- >>> accept (containsRegex "x{2,5}y?") "xxxy"+-- True+-- >>> accept (containsRegex "x{2,5}y?") "xyy"+-- False+-- >>> accept (containsRegex "x{2,5}y?") "wxxxyz"+-- True+containsRegex :: (RegexLike Regex a, Eq a, Show a) => String -> Predicate a+containsRegex s = withDefaultExplain show " " $ \explainImpl ->+  Predicate+    { showPredicate = "contains " ++ pat,+      showNegation = "doesn't contain " ++ pat,+      accept = isJust . matchOnce r,+      explain = explainImpl+    }+  where+    pat = "/" ++ init (tail $ show s) ++ "/"+    r = makeRegexOpts comp exec s :: Regex+    comp = defaultCompOpt {newSyntax = True, lastStarGreedy = True}+    exec = defaultExecOpt {captureGroups = False}++-- | A 'Predicate' that accepts 'String's or string-like values containing a+-- match for a regular expression in a case-insensitive way.  The expression+-- need match the entire argument.+--+-- You should use this instead of @'caseInsensitive' 'containsRegex'@, because+-- regular expression syntax itself is still case-sensitive even when the text+-- you are matching is not.+--+-- >>> accept (containsCaseInsensitiveRegex "x{2,5}y?") "XXXY"+-- True+-- >>> accept (containsCaseInsensitiveRegex "x{2,5}y?") "XYY"+-- False+-- >>> accept (containsCaseInsensitiveRegex "x{2,5}y?") "WXXXYZ"+-- True+containsCaseInsensitiveRegex ::+  (RegexLike Regex a, Eq a, Show a) => String -> Predicate a+containsCaseInsensitiveRegex s = withDefaultExplain show " " $ \explainImpl ->+  Predicate+    { showPredicate = "contains " ++ pat,+      showNegation = "doesn't contain " ++ pat,+      accept = isJust . matchOnce r,+      explain = explainImpl+    }+  where+    pat = "/" ++ init (tail $ show s) ++ "/i"+    r = makeRegexOpts comp exec s :: Regex+    comp =+      defaultCompOpt+        { newSyntax = True,+          lastStarGreedy = True,+          caseSensitive = False+        }+    exec = defaultExecOpt {captureGroups = False}++-- | A 'Predicate' that accepts empty data structures.+--+-- >>> accept isEmpty ([] :: [Int])+-- True+-- >>> accept isEmpty [1, 2, 3]+-- False+-- >>> accept isEmpty ""+-- True+-- >>> accept isEmpty "gas tank"+-- False+isEmpty :: (MonoFoldable t, Show t) => Predicate t+isEmpty = withDefaultExplain show " is " $ \explainImpl ->+  Predicate+    { showPredicate = "empty",+      showNegation = "non-empty",+      accept = onull,+      explain = explainImpl+    }++-- | A 'Predicate' that accepts non-empty data structures.+--+-- >>> accept nonEmpty ([] :: [Int])+-- False+-- >>> accept nonEmpty [1, 2, 3]+-- True+-- >>> accept nonEmpty ""+-- False+-- >>> accept nonEmpty "gas tank"+-- True+nonEmpty :: (MonoFoldable t, Show t) => Predicate t+nonEmpty = notP isEmpty++-- | A 'Predicate' that accepts data structures whose number of elements match+-- the child 'Predicate'.+--+-- >>> accept (sizeIs (lt 3)) ['a' .. 'f']+-- False+-- >>> accept (sizeIs (lt 3)) ['a' .. 'b']+-- True+sizeIs :: (MonoFoldable t, Show t) => Predicate Int -> Predicate t+sizeIs p =+  Predicate+    { showPredicate = "size " ++ showPredicate p,+      showNegation = "size " ++ showNegation p,+      accept = accept p . olength,+      explain = \y ->+        let detail+              | accept p (olength y) = showPredicate p+              | otherwise = showNegation p+            detailStr+              | show (olength y) == detail = ""+              | otherwise = ", which is " ++ detail+         in show y ++ " has size " ++ show (olength y) ++ detailStr+    }++-- | A 'Predicate' that accepts data structures whose contents each match the+-- corresponding 'Predicate' in the given list, in the same order.+--+-- >>> accept (elemsAre [lt 3, lt 4, lt 5]) [2, 3, 4]+-- True+-- >>> accept (elemsAre [lt 3, lt 4, lt 5]) [2, 3, 4, 5]+-- False+-- >>> accept (elemsAre [lt 3, lt 4, lt 5]) [2, 10, 4]+-- False+elemsAre :: MonoFoldable t => [Predicate (Element t)] -> Predicate t+elemsAre ps =+  Predicate+    { showPredicate = show ps,+      showNegation = "not " ++ show ps,+      accept = \xs ->+        olength xs == olength ps+          && and (zipWith accept ps (otoList xs)),+      explain = \xs ->+        let results = acceptAndExplain (otoList xs)+         in if+                | olength xs /= length ps ->+                  "wrong size (got "+                    ++ show (olength xs)+                    ++ "; expected "+                    ++ show (length ps)+                    ++ ")"+                | all fst results -> "elements are " ++ show ps+                | otherwise ->+                  intercalate "; and " $+                    snd <$> filter (not . fst) results+    }+  where+    acceptAndExplain xs = zipWith3 matchAndExplain [1 :: Int ..] ps xs+    matchAndExplain i p x =+      (accept p x, "in element #" ++ show i ++ ": " ++ explain p x)++-- | A 'Predicate' that accepts data structures whose contents each match the+-- corresponding 'Predicate' in the given list, in any order.+--+-- >>> accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [1, 2, 3]+-- True+-- >>> accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [2, 3, 1]+-- True+-- >>> accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [1, 2, 3, 4]+-- False+-- >>> accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [1, 3]+-- False+unorderedElemsAre :: MonoFoldable t => [Predicate (Element t)] -> Predicate t+unorderedElemsAre ps =+  Predicate+    { showPredicate =+        "(any order) " ++ show ps,+      showNegation =+        "not (in any order) " ++ show ps,+      accept = \xs ->+        let (_, orphanPs, orphanXs) = matchAll xs+         in null orphanPs && null orphanXs,+      explain = \xs ->+        let (matches, orphanPs, orphanXs) = matchAll xs+         in if null orphanPs && null orphanXs+              then intercalate "; and " (explainMatch <$> matches)+              else+                let missingExplanation =+                      if null orphanPs+                        then Nothing+                        else+                          Just+                            ( "Missing: "+                                ++ intercalate ", " (showPredicate <$> orphanPs)+                            )+                    extraExplanation =+                      if null orphanXs+                        then Nothing+                        else+                          Just+                            ( "Extra elements: "+                                ++ intercalate+                                  ", "+                                  (("#" ++) . show . fst <$> orphanXs)+                            )+                 in intercalate+                      "; "+                      (catMaybes [missingExplanation, extraExplanation])+    }+  where+    matchOne p (_, x) = accept p x+    matchAll xs = bipartiteMatching matchOne ps (zip [1 :: Int ..] (otoList xs))+    explainMatch (p, (j, x)) = "element #" ++ show j ++ ": " ++ explain p x++-- | A 'Predicate' that accepts data structures whose elements each match the+-- child 'Predicate'.+--+-- >>> accept (each (gt 5)) [4, 5, 6]+-- False+-- >>> accept (each (gt 5)) [6, 7, 8]+-- True+-- >>> accept (each (gt 5)) []+-- True+each :: MonoFoldable t => Predicate (Element t) -> Predicate t+each p =+  Predicate+    { showPredicate = "each (" ++ showPredicate p ++ ")",+      showNegation = "contains (" ++ showNegation p ++ ")",+      accept = all fst . acceptAndExplain,+      explain = \xs ->+        let results = acceptAndExplain xs+            format (i, explanation) =+              "element #" ++ show i ++ ": " ++ explanation+         in if all fst results+              then "all elements " ++ showPredicate p+              else+                intercalate "; and " $+                  format . snd <$> filter (not . fst) results+    }+  where+    acceptAndExplain xs =+      [(accept p x, (i, explain p x)) | i <- [1 :: Int ..] | x <- otoList xs]++-- | A 'Predicate' that accepts data structures which contain at least one+-- element matching the child 'Predicate'.+--+-- >>> accept (contains (gt 5)) [3, 4, 5]+-- False+-- >>> accept (contains (gt 5)) [4, 5, 6]+-- True+-- >>> accept (contains (gt 5)) []+-- False+contains :: MonoFoldable t => Predicate (Element t) -> Predicate t+contains = notP . each . notP++-- | A 'Predicate' that accepts data structures whose elements all satisfy the+-- given child 'Predicate's.+--+-- >>> accept (containsAll [eq "foo", eq "bar"]) ["bar", "foo"]+-- True+-- >>> accept (containsAll [eq "foo", eq "bar"]) ["foo"]+-- False+-- >>> accept (containsAll [eq "foo", eq "bar"]) ["foo", "bar", "qux"]+-- True+--+-- Each child 'Predicate' must be satisfied by a different element, so repeating+-- a 'Predicate' requires that two different matching elements exist.  If you+-- want a 'Predicate' to match multiple elements, instead, you can accomplish+-- this with @'contains' p1 `'andP'` 'contains' p2 `'andP'` ...@.+--+-- >>> accept (containsAll [startsWith "f", endsWith "o"]) ["foo"]+-- False+-- >>> accept (contains (startsWith "f") `andP` contains (endsWith "o")) ["foo"]+-- True+containsAll :: MonoFoldable t => [Predicate (Element t)] -> Predicate t+containsAll ps =+  Predicate+    { showPredicate = "contains all of " ++ show ps,+      showNegation = "not all of " ++ show ps,+      accept = \xs -> let (_, orphanPs, _) = matchAll xs in null orphanPs,+      explain = \xs ->+        let (matches, orphanPs, _) = matchAll xs+         in if null orphanPs+              then intercalate "; and " (explainMatch <$> matches)+              else "Missing: " ++ intercalate ", " (showPredicate <$> orphanPs)+    }+  where+    matchOne p (_, x) = accept p x+    matchAll xs = bipartiteMatching matchOne ps (zip [1 :: Int ..] (otoList xs))+    explainMatch (p, (j, x)) = "element #" ++ show j ++ ": " ++ explain p x++-- | A 'Predicate' that accepts data structures whose elements all satisfy one+-- of the child 'Predicate's.+--+-- >>> accept (containsOnly [eq "foo", eq "bar"]) ["foo"]+-- True+-- >>> accept (containsOnly [eq "foo", eq "bar"]) ["foo", "bar"]+-- True+-- >>> accept (containsOnly [eq "foo", eq "bar"]) ["foo", "qux"]+-- False+--+-- Each element must satisfy a different child 'Predicate'.  If you want+-- multiple elements to match the same 'Predicate', instead, you can accomplish+-- this with @'each' (p1 `'orP'` p2 `'orP'` ...)@.+--+-- >>> accept (containsOnly [eq "foo", eq "bar"]) ["foo", "foo"]+-- False+-- >>> accept (each (eq "foo" `orP` eq "bar")) ["foo", "foo"]+-- True+containsOnly :: MonoFoldable t => [Predicate (Element t)] -> Predicate t+containsOnly ps =+  Predicate+    { showPredicate = "contains only " ++ show ps,+      showNegation = "not only " ++ show ps,+      accept = \xs -> let (_, _, orphanXs) = matchAll xs in null orphanXs,+      explain = \xs ->+        let (matches, _, orphanXs) = matchAll xs+         in if null orphanXs+              then intercalate "; and " (explainMatch <$> matches)+              else+                "Extra elements: "+                  ++ intercalate ", " (("#" ++) . show . fst <$> orphanXs)+    }+  where+    matchOne p (_, x) = accept p x+    matchAll xs = bipartiteMatching matchOne ps (zip [1 :: Int ..] (otoList xs))+    explainMatch (p, (j, x)) = "element #" ++ show j ++ ": " ++ explain p x++-- | Transforms a 'Predicate' on a list of keys into a 'Predicate' on map-like+-- data structures.+--+-- This is equivalent to @'with' ('map' 'fst' '.' 'toList')@, but more readable.+--+-- >>> accept (keys (each (eq "foo"))) [("foo", 5)]+-- True+--+-- >>> accept (keys (each (eq "foo"))) [("foo", 5), ("bar", 6)]+-- False+keys :: (IsList t, Item t ~ (k, v)) => Predicate [k] -> Predicate t+keys p =+  Predicate+    { showPredicate = "keys (" ++ showPredicate p ++ ")",+      showNegation = "keys (" ++ showNegation p ++ ")",+      accept = accept p . map fst . toList,+      explain = ("in keys, " ++) . explain p . map fst . toList+    }++-- | Transforms a 'Predicate' on a list of values into a 'Predicate' on map-like+-- data structures.+--+-- This is equivalent to @'with' ('map' 'snd' '.' 'toList')@, but more readable.+--+-- >>> accept (values (each (eq 5))) [("foo", 5), ("bar", 5)]+-- True+--+-- >>> accept (values (each (eq 5))) [("foo", 5), ("bar", 6)]+-- False+values :: (IsList t, Item t ~ (k, v)) => Predicate [v] -> Predicate t+values p =+  Predicate+    { showPredicate = "values (" ++ showPredicate p ++ ")",+      showNegation = "values (" ++ showNegation p ++ ")",+      accept = accept p . map snd . toList,+      explain = ("in values, " ++) . explain p . map snd . toList+    }++-- | A 'Predicate' that accepts values of 'RealFloat' types that are close to+-- the given number.  The expected precision is scaled based on the target+-- value, so that reasonable rounding error is accepted but grossly inaccurate+-- results are not.+--+-- The following naive use of 'eq' fails due to rounding:+--+-- >>> accept (eq 1.0) (sum (replicate 100 0.01))+-- False+--+-- The solution is to use 'approxEq', which accounts for rounding error.+-- However, 'approxEq' doesn't accept results that are far enough off that they+-- likely arise from incorrect calculations instead of rounding error.+--+-- >>> accept (approxEq 1.0) (sum (replicate 100 0.01))+-- True+-- >>> accept (approxEq 1.0) (sum (replicate 100 0.009999))+-- False+approxEq :: (RealFloat a, Show a) => a -> Predicate a+approxEq x = withDefaultExplain show " " $ \explainImpl ->+  Predicate+    { showPredicate = "≈ " ++ show x,+      showNegation = "≇" ++ show x,+      accept = \y -> abs (x - y) < diff,+      explain = explainImpl+    }+  where+    diff = encodeFloat 1 (snd (decodeFloat x) + floatDigits x `div` 2)++-- | A 'Predicate' that accepts positive numbers of any 'Ord'ered 'Num' type.+--+-- >>> accept positive 1+-- True+--+-- >>> accept positive 0+-- False+--+-- >>> accept positive (-1)+-- False+positive :: (Ord a, Num a) => Predicate a+positive =+  Predicate+    { showPredicate = "positive",+      showNegation = "non-positive",+      accept = \x -> signum x > 0,+      explain = \x ->+        if+            | signum x > 0 -> "value is positive"+            | x == 0 -> "value is zero"+            | signum x < 0 -> "value is negative"+            | otherwise -> "value has unknown sign"+    }++-- | A 'Predicate' that accepts negative numbers of any 'Ord'ered 'Num' type.+--+-- >>> accept negative 1+-- False+--+-- >>> accept negative 0+-- False+--+-- >>> accept negative (-1)+-- True+negative :: (Ord a, Num a) => Predicate a+negative =+  Predicate+    { showPredicate = "negative",+      showNegation = "non-negative",+      accept = \x -> signum x < 0,+      explain = \x ->+        if+            | signum x < 0 -> "value is negative"+            | x == 0 -> "value is zero"+            | signum x < 0 -> "value is positive"+            | otherwise -> "value has unknown sign"+    }++-- | A 'Predicate' that accepts non-positive numbers of any 'Ord'ered 'Num'+-- type.+--+-- >>> accept nonPositive 1+-- False+--+-- >>> accept nonPositive 0+-- True+--+-- >>> accept nonPositive (-1)+-- True+nonPositive :: (Ord a, Num a) => Predicate a+nonPositive = notP positive++-- | A 'Predicate' that accepts non-negative numbers of any 'Ord'ered 'Num'+-- type.+--+-- >>> accept nonNegative 1+-- True+--+-- >>> accept nonNegative 0+-- True+--+-- >>> accept nonNegative (-1)+-- False+nonNegative :: (Ord a, Num a) => Predicate a+nonNegative = notP negative++-- | A 'Predicate' that accepts finite numbers of any 'RealFloat' type.+--+-- >>> accept finite 1.0+-- True+-- >>> accept finite (0 / 0)+-- False+-- >>> accept finite (1 / 0)+-- False+finite :: RealFloat a => Predicate a+finite =+  Predicate+    { showPredicate = "finite",+      showNegation = "non-finite",+      accept = isFinite,+      explain = \x ->+        if isFinite x+          then "value is finite"+          else "value is not finite"+    }+  where+    isFinite x = not (isInfinite x) && not (isNaN x)++-- | A 'Predicate' that accepts infinite numbers of any 'RealFloat' type.+--+-- >>> accept infinite 1.0+-- False+-- >>> accept infinite (0 / 0)+-- False+-- >>> accept infinite (1 / 0)+-- True+infinite :: RealFloat a => Predicate a+infinite =+  Predicate+    { showPredicate = "infinite",+      showNegation = "non-infinite",+      accept = isInfinite,+      explain = \x ->+        if isInfinite x+          then "value is infinite"+          else "value is not infinite"+    }++-- | A 'Predicate' that accepts NaN values of any 'RealFloat' type.+--+-- >>> accept nAn 1.0+-- False+-- >>> accept nAn (0 / 0)+-- True+-- >>> accept nAn (1 / 0)+-- False+nAn :: RealFloat a => Predicate a+nAn =+  Predicate+    { showPredicate = "NaN",+      showNegation = "non-NaN",+      accept = isNaN,+      explain = \x ->+        if isNaN x+          then "value is NaN"+          else "value is not NaN"+    }++-- | A conversion from @a -> 'Bool'@ to 'Predicate'.  This is a fallback that+-- can be used to build a 'Predicate' that checks anything at all.  However, its+-- description will be less helpful than standard 'Predicate's.+--+-- >>> accept (is even) 3+-- False+-- >>> accept (is even) 4+-- True+is :: HasCallStack => (a -> Bool) -> Predicate a+is p =+  Predicate+    { showPredicate = withLoc (locate callStack "custom predicate"),+      showNegation = withLoc (locate callStack "negated custom predicate"),+      accept = p,+      explain = \x ->+        if p x+          then "value matched custom predicate"+          else "value did not match custom predicate"+    }++-- | A Template Haskell splice that acts like 'is', but receives a quoted+-- expression at compile time and has a more helpful description for error+-- messages.+--+-- >>> accept $(qIs [| even |]) 3+-- False+-- >>> accept $(qIs [| even |]) 4+-- True+--+-- >>> show $(qIs [| even |])+-- "even"+qIs :: HasCallStack => ExpQ -> ExpQ+qIs p =+  [|+    Predicate+      { showPredicate = $description,+        showNegation = "not " ++ $description,+        accept = $p,+        explain = \x -> if $p x then $description else "not " ++ $description+      }+    |]+  where+    description = lift . pprint . removeModNames =<< p++-- | A combinator to lift a 'Predicate' to work on a property or computed value+-- of the original value.+--+-- >>> accept (with abs (gt 5)) (-6)+-- True+-- >>> accept (with abs (gt 5)) (-5)+-- False+-- >>> accept (with reverse (eq "olleh")) "hello"+-- True+-- >>> accept (with reverse (eq "olleh")) "goodbye"+-- False+with :: HasCallStack => (a -> b) -> Predicate b -> Predicate a+with f p =+  Predicate+    { showPredicate = prop ++ ": " ++ show p,+      showNegation = prop ++ ": " ++ showNegation p,+      accept = accept p . f,+      explain = ((prop ++ ": ") ++) . explain p . f+    }+  where+    prop = withLoc (locate callStack "property")++-- | A Template Haskell splice that acts like 'is', but receives a quoted typed+-- expression at compile time and has a more helpful description for error+-- messages.+--+-- >>> accept ($(qWith [| abs |]) (gt 5)) (-6)+-- True+-- >>> accept ($(qWith [| abs |]) (gt 5)) (-5)+-- False+-- >>> accept ($(qWith [| reverse |]) (eq "olleh")) "hello"+-- True+-- >>> accept ($(qWith [| reverse |]) (eq "olleh")) "goodbye"+-- False+--+-- >>> show ($(qWith [| abs |]) (gt 5))+-- "abs: > 5"+qWith :: ExpQ -> ExpQ+qWith f =+  [|+    \p ->+      Predicate+        { showPredicate = $prop ++ ": " ++ show p,+          showNegation = $prop ++ ": " ++ showNegation p,+          accept = accept p . $f,+          explain = (($prop ++ ": ") ++) . explain p . $f+        }+    |]+  where+    prop = lift . pprint . removeModNames =<< f++-- | A Template Haskell splice that turns a quoted pattern into a predicate that+-- accepts values that match the pattern.+--+-- >>> accept $(qMatch [p| Just (Left _) |]) Nothing+-- False+-- >>> accept $(qMatch [p| Just (Left _) |]) (Just (Left 5))+-- True+-- >>> accept $(qMatch [p| Just (Left _) |]) (Just (Right 5))+-- False+--+-- >>> show $(qMatch [p| Just (Left _) |])+-- "Just (Left _)"+qMatch :: PatQ -> ExpQ+qMatch qpat =+  [|+    Predicate+      { showPredicate = $patString,+        showNegation = "not " ++ $patString,+        accept = \case+          $qpat -> True+          _ -> False,+        explain = \case+          $qpat -> "value matched " ++ $patString+          _ -> "value didn't match " ++ $patString+      }+    |]+  where+    patString = lift . pprint . removeModNames =<< qpat++-- | Converts a 'Predicate' to a new type.  Typically used with visible type+-- application, as in the examples below.+--+-- >>> accept (typed @String anything) "foo"+-- True+-- >>> accept (typed @String (sizeIs (gt 5))) "foo"+-- False+-- >>> accept (typed @String anything) (42 :: Int)+-- False+typed :: forall a b. (Typeable a, Typeable b) => Predicate a -> Predicate b+typed p =+  Predicate+    { showPredicate =+        showPredicate p ++ " :: " ++ show (typeRep (Proxy :: Proxy a)),+      showNegation =+        "not " ++ showPredicate p ++ " :: "+          ++ show (typeRep (Proxy :: Proxy a)),+      accept = \x -> case cast x of+        Nothing -> False+        Just y -> accept p y,+      explain = \x -> case cast x of+        Nothing ->+          "wrong type ("+            ++ show (typeRep (undefined :: Proxy b))+            ++ " vs. "+            ++ show (typeRep (undefined :: Proxy a))+            ++ ")"+        Just y -> explain p y+    }
+ src/Test/Predicates/Internal/FlowMatcher.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | An implementation of bipartite matching using the Ford-Fulkerson algorithm.+module Test.Predicates.Internal.FlowMatcher where++import Control.Monad (forM_, when)+import Control.Monad.ST (ST)+import Data.Array.IArray (Array, assocs, elems)+import Data.Array.ST+  ( MArray (newArray),+    STArray,+    newListArray,+    readArray,+    runSTArray,+    writeArray,+  )+import Data.List ((\\))+import Data.Maybe (catMaybes)++-- $setup+-- >>> :set -Wno-type-defaults++-- | Computes the best bipartite matching of the elements in the two lists,+-- given the compatibility function.+--+-- Returns matched pairs, then unmatched lhs elements, then unmatched rhs+-- elements.+--+-- >>> bipartiteMatching (==) [1 .. 5] [6, 5 .. 2]+-- ([(2,2),(3,3),(4,4),(5,5)],[1],[6])+bipartiteMatching ::+  forall a b. (a -> b -> Bool) -> [a] -> [b] -> ([(a, b)], [a], [b])+bipartiteMatching compatible xs ys = (matchedPairs, unmatchedX, unmatchedY)+  where+    matchedPairs :: [(a, b)]+    matchedPairs = [(xs !! i, ys !! j) | (i, Just j) <- assocs matches]++    unmatchedX :: [a]+    unmatchedX = [xs !! i | (i, Nothing) <- assocs matches]++    unmatchedY :: [b]+    unmatchedY = [ys !! j | j <- [0 .. numYs - 1] \\ catMaybes (elems matches)]++    matches :: Array Int (Maybe Int)+    matches = runSTArray st++    st :: forall s. ST s (STArray s Int (Maybe Int))+    st = do+      compatArray <-+        newListArray+          ((0, 0), (numXs - 1, numYs - 1))+          [compatible x y | x <- xs, y <- ys] ::+          ST s (STArray s (Int, Int) Bool)+      matchArray <-+        newArray (0, numXs - 1) Nothing ::+          ST s (STArray s Int (Maybe Int))+      forM_ [0 .. numYs - 1] $ \j -> do+        seen <-+          newArray (0, numXs - 1) False :: ST s (STArray s Int Bool)+        _ <- go compatArray j matchArray seen+        return ()++      return matchArray++    numXs, numYs :: Int+    numXs = length xs+    numYs = length ys++    go ::+      forall s.+      STArray s (Int, Int) Bool ->+      Int ->+      STArray s Int (Maybe Int) ->+      STArray s Int Bool ->+      ST s Bool+    go compatArray j matchArray seen = loop False 0+      where+        loop True _ = return True+        loop _ i+          | i == numXs = return False+          | otherwise = do+            compat <- readArray compatArray (i, j)+            isSeen <- readArray seen i+            replace <-+              if isSeen || not compat+                then return False+                else do+                  writeArray seen i True+                  matchNum <- readArray matchArray i+                  case matchNum of+                    Nothing -> return True+                    Just n -> go compatArray n matchArray seen+            when replace $ writeArray matchArray i (Just j)+            loop replace (i + 1)
+ src/Test/Predicates/Internal/Util.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleContexts #-}++-- | Internal utilities used for HMock implementation.+module Test.Predicates.Internal.Util where++import Data.Generics (Data, everywhere, mkT)+import Data.MonoTraversable (Element)+import qualified Data.Sequences as Seq+import GHC.Stack (CallStack, getCallStack, prettySrcLoc)+import Language.Haskell.TH.Syntax (NameFlavour (..))++-- | A value together with its source location.+data Located a = Loc (Maybe String) a deriving (Functor)++-- | Annotates a value with its source location from the call stack.+locate :: CallStack -> a -> Located a+locate stack = case map snd (getCallStack stack) of+  (loc : _) -> Loc (Just (prettySrcLoc loc))+  _ -> Loc Nothing++-- | Formats a 'Located' 'String' to include its source location.+withLoc :: Located String -> String+withLoc (Loc Nothing s) = s+withLoc (Loc (Just loc) s) = s ++ " at " ++ loc++-- | Returns all ways to choose one element from a list, and the corresponding+-- remaining list.+choices :: [a] -> [(a, [a])]+choices [] = []+choices (x : xs) = (x, xs) : (fmap (x :) <$> choices xs)++-- | Checks if one sequence is a subsequence of another.+isSubsequenceOf :: (Seq.IsSequence t, Eq (Element t)) => t -> t -> Bool+xs `isSubsequenceOf` ys = case Seq.uncons xs of+  Nothing -> True+  Just (x, xs') -> case Seq.uncons (snd (Seq.break (== x) ys)) of+    Nothing -> False+    Just (_, ys') -> xs' `isSubsequenceOf` ys'++-- | Removes all module names from Template Haskell names in the given value, so+-- that it will pretty-print more cleanly.+removeModNames :: Data a => a -> a+removeModNames = everywhere (mkT unMod)+  where+    unMod NameG {} = NameS+    unMod other = other
+ test/DocTests/All.hs view
@@ -0,0 +1,12 @@+-- Do not edit! Automatically created with doctest-extract.+module DocTests.All where++import qualified DocTests.Test.Predicates+import qualified DocTests.Test.Predicates.Internal.FlowMatcher++import qualified Test.DocTest.Driver as DocTest++main :: DocTest.T ()+main = do+    DocTests.Test.Predicates.test+    DocTests.Test.Predicates.Internal.FlowMatcher.test
+ test/DocTests/Test/Predicates.hs view
@@ -0,0 +1,928 @@+-- Do not edit! Automatically created with doctest-extract from src/Test/Predicates.hs+{-# LINE 90 "src/Test/Predicates.hs" #-}++{-# OPTIONS_GHC -XTemplateHaskell #-}+{-# OPTIONS_GHC -XTypeApplications #-}+{-# OPTIONS_GHC -Wno-type-defaults #-}+module DocTests.Test.Predicates where++import Test.Predicates+import Test.DocTest.Base+import qualified Test.DocTest.Driver as DocTest++{-# LINE 94 "src/Test/Predicates.hs" #-}++test :: DocTest.T ()+test = do+ DocTest.printPrefix "Test.Predicates:121: "+{-# LINE 121 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 121 "src/Test/Predicates.hs" #-}+      (accept anything "foo")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:123: "+{-# LINE 123 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 123 "src/Test/Predicates.hs" #-}+      (accept anything undefined)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:136: "+{-# LINE 136 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 136 "src/Test/Predicates.hs" #-}+      (accept (eq "foo") "foo")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:138: "+{-# LINE 138 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 138 "src/Test/Predicates.hs" #-}+      (accept (eq "foo") "bar")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:154: "+{-# LINE 154 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 154 "src/Test/Predicates.hs" #-}+      (accept (neq "foo") "foo")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:156: "+{-# LINE 156 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 156 "src/Test/Predicates.hs" #-}+      (accept (neq "foo") "bar")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:163: "+{-# LINE 163 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 163 "src/Test/Predicates.hs" #-}+      (accept (gt 5) 4)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:165: "+{-# LINE 165 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 165 "src/Test/Predicates.hs" #-}+      (accept (gt 5) 5)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:167: "+{-# LINE 167 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 167 "src/Test/Predicates.hs" #-}+      (accept (gt 5) 6)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:181: "+{-# LINE 181 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 181 "src/Test/Predicates.hs" #-}+      (accept (geq 5) 4)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:183: "+{-# LINE 183 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 183 "src/Test/Predicates.hs" #-}+      (accept (geq 5) 5)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:185: "+{-# LINE 185 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 185 "src/Test/Predicates.hs" #-}+      (accept (geq 5) 6)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:198: "+{-# LINE 198 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 198 "src/Test/Predicates.hs" #-}+      (accept (lt 5) 4)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:200: "+{-# LINE 200 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 200 "src/Test/Predicates.hs" #-}+      (accept (lt 5) 5)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:202: "+{-# LINE 202 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 202 "src/Test/Predicates.hs" #-}+      (accept (lt 5) 6)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:209: "+{-# LINE 209 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 209 "src/Test/Predicates.hs" #-}+      (accept (leq 5) 4)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:211: "+{-# LINE 211 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 211 "src/Test/Predicates.hs" #-}+      (accept (leq 5) 5)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:213: "+{-# LINE 213 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 213 "src/Test/Predicates.hs" #-}+      (accept (leq 5) 6)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:221: "+{-# LINE 221 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 221 "src/Test/Predicates.hs" #-}+      (accept (just (eq "value")) Nothing)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:223: "+{-# LINE 223 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 223 "src/Test/Predicates.hs" #-}+      (accept (just (eq "value")) (Just "value"))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:225: "+{-# LINE 225 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 225 "src/Test/Predicates.hs" #-}+      (accept (just (eq "value")) (Just "wrong value"))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:239: "+{-# LINE 239 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 239 "src/Test/Predicates.hs" #-}+      (accept nothing Nothing)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:242: "+{-# LINE 242 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 242 "src/Test/Predicates.hs" #-}+      (accept nothing (Just "something"))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:256: "+{-# LINE 256 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 256 "src/Test/Predicates.hs" #-}+      (accept (left (eq "value")) (Left "value"))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:258: "+{-# LINE 258 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 258 "src/Test/Predicates.hs" #-}+      (accept (left (eq "value")) (Right "value"))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:260: "+{-# LINE 260 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 260 "src/Test/Predicates.hs" #-}+      (accept (left (eq "value")) (Left "wrong value"))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:274: "+{-# LINE 274 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 274 "src/Test/Predicates.hs" #-}+      (accept (right (eq "value")) (Right "value"))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:276: "+{-# LINE 276 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 276 "src/Test/Predicates.hs" #-}+      (accept (right (eq "value")) (Right "wrong value"))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:278: "+{-# LINE 278 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 278 "src/Test/Predicates.hs" #-}+      (accept (right (eq "value")) (Left "value"))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:292: "+{-# LINE 292 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 292 "src/Test/Predicates.hs" #-}+      (accept (zipP (eq "foo") (eq "bar")) ("foo", "bar"))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:294: "+{-# LINE 294 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 294 "src/Test/Predicates.hs" #-}+      (accept (zipP (eq "foo") (eq "bar")) ("bar", "foo"))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:318: "+{-# LINE 318 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 318 "src/Test/Predicates.hs" #-}+      (accept (zip3P (eq "foo") (eq "bar") (eq "qux")) ("foo", "bar", "qux"))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:320: "+{-# LINE 320 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 320 "src/Test/Predicates.hs" #-}+      (accept (zip3P (eq "foo") (eq "bar") (eq "qux")) ("qux", "bar", "foo"))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:345: "+{-# LINE 345 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 345 "src/Test/Predicates.hs" #-}+      (accept (zip4P (eq 1) (eq 2) (eq 3) (eq 4)) (1, 2, 3, 4))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:347: "+{-# LINE 347 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 347 "src/Test/Predicates.hs" #-}+      (accept (zip4P (eq 1) (eq 2) (eq 3) (eq 4)) (4, 3, 2, 1))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:378: "+{-# LINE 378 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 378 "src/Test/Predicates.hs" #-}+      (accept (zip5P (eq 1) (eq 2) (eq 3) (eq 4) (eq 5)) (1, 2, 3, 4, 5))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:380: "+{-# LINE 380 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 380 "src/Test/Predicates.hs" #-}+      (accept (zip5P (eq 1) (eq 2) (eq 3) (eq 4) (eq 5)) (5, 4, 3, 2, 1))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:412: "+{-# LINE 412 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 412 "src/Test/Predicates.hs" #-}+      (accept (lt "foo" `andP` gt "bar") "eta")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:414: "+{-# LINE 414 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 414 "src/Test/Predicates.hs" #-}+      (accept (lt "foo" `andP` gt "bar") "quz")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:416: "+{-# LINE 416 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 416 "src/Test/Predicates.hs" #-}+      (accept (lt "foo" `andP` gt "bar") "alpha")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:433: "+{-# LINE 433 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 433 "src/Test/Predicates.hs" #-}+      (accept (lt "bar" `orP` gt "foo") "eta")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:435: "+{-# LINE 435 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 435 "src/Test/Predicates.hs" #-}+      (accept (lt "bar" `orP` gt "foo") "quz")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:437: "+{-# LINE 437 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 437 "src/Test/Predicates.hs" #-}+      (accept (lt "bar" `orP` gt "foo") "alpha")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:445: "+{-# LINE 445 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 445 "src/Test/Predicates.hs" #-}+      (accept (notP (eq "negative")) "positive")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:447: "+{-# LINE 447 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 447 "src/Test/Predicates.hs" #-}+      (accept (notP (eq "negative")) "negative")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:460: "+{-# LINE 460 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 460 "src/Test/Predicates.hs" #-}+      (accept (startsWith "fun") "fungible")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:462: "+{-# LINE 462 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 462 "src/Test/Predicates.hs" #-}+      (accept (startsWith "gib") "fungible")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:475: "+{-# LINE 475 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 475 "src/Test/Predicates.hs" #-}+      (accept (endsWith "ow") "crossbow")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:477: "+{-# LINE 477 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 477 "src/Test/Predicates.hs" #-}+      (accept (endsWith "ow") "trebuchet")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:491: "+{-# LINE 491 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 491 "src/Test/Predicates.hs" #-}+      (accept (hasSubstr "i") "team")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:493: "+{-# LINE 493 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 493 "src/Test/Predicates.hs" #-}+      (accept (hasSubstr "i") "partnership")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:507: "+{-# LINE 507 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 507 "src/Test/Predicates.hs" #-}+      (accept (hasSubsequence [1..5]) [1, 2, 3, 4, 5])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:509: "+{-# LINE 509 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 509 "src/Test/Predicates.hs" #-}+      (accept (hasSubsequence [1..5]) [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:511: "+{-# LINE 511 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 511 "src/Test/Predicates.hs" #-}+      (accept (hasSubsequence [1..5]) [2, 3, 5, 7, 11])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:525: "+{-# LINE 525 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 525 "src/Test/Predicates.hs" #-}+      (accept (caseInsensitive startsWith "foo") "FOOTBALL!")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:527: "+{-# LINE 527 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 527 "src/Test/Predicates.hs" #-}+      (accept (caseInsensitive endsWith "ball") "soccer")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:529: "+{-# LINE 529 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 529 "src/Test/Predicates.hs" #-}+      (accept (caseInsensitive eq "time") "TIME")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:531: "+{-# LINE 531 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 531 "src/Test/Predicates.hs" #-}+      (accept (caseInsensitive gt "NOTHING") "everything")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:558: "+{-# LINE 558 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 558 "src/Test/Predicates.hs" #-}+      (accept (matchesRegex "x{2,5}y?") "xxxy")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:560: "+{-# LINE 560 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 560 "src/Test/Predicates.hs" #-}+      (accept (matchesRegex "x{2,5}y?") "xyy")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:562: "+{-# LINE 562 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 562 "src/Test/Predicates.hs" #-}+      (accept (matchesRegex "x{2,5}y?") "wxxxyz")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:592: "+{-# LINE 592 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 592 "src/Test/Predicates.hs" #-}+      (accept (matchesCaseInsensitiveRegex "x{2,5}y?") "XXXY")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:594: "+{-# LINE 594 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 594 "src/Test/Predicates.hs" #-}+      (accept (matchesCaseInsensitiveRegex "x{2,5}y?") "XYY")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:596: "+{-# LINE 596 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 596 "src/Test/Predicates.hs" #-}+      (accept (matchesCaseInsensitiveRegex "x{2,5}y?") "WXXXYZ")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:632: "+{-# LINE 632 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 632 "src/Test/Predicates.hs" #-}+      (accept (containsRegex "x{2,5}y?") "xxxy")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:634: "+{-# LINE 634 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 634 "src/Test/Predicates.hs" #-}+      (accept (containsRegex "x{2,5}y?") "xyy")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:636: "+{-# LINE 636 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 636 "src/Test/Predicates.hs" #-}+      (accept (containsRegex "x{2,5}y?") "wxxxyz")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:660: "+{-# LINE 660 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 660 "src/Test/Predicates.hs" #-}+      (accept (containsCaseInsensitiveRegex "x{2,5}y?") "XXXY")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:662: "+{-# LINE 662 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 662 "src/Test/Predicates.hs" #-}+      (accept (containsCaseInsensitiveRegex "x{2,5}y?") "XYY")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:664: "+{-# LINE 664 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 664 "src/Test/Predicates.hs" #-}+      (accept (containsCaseInsensitiveRegex "x{2,5}y?") "WXXXYZ")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:688: "+{-# LINE 688 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 688 "src/Test/Predicates.hs" #-}+      (accept isEmpty ([] :: [Int]))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:690: "+{-# LINE 690 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 690 "src/Test/Predicates.hs" #-}+      (accept isEmpty [1, 2, 3])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:692: "+{-# LINE 692 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 692 "src/Test/Predicates.hs" #-}+      (accept isEmpty "")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:694: "+{-# LINE 694 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 694 "src/Test/Predicates.hs" #-}+      (accept isEmpty "gas tank")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:707: "+{-# LINE 707 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 707 "src/Test/Predicates.hs" #-}+      (accept nonEmpty ([] :: [Int]))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:709: "+{-# LINE 709 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 709 "src/Test/Predicates.hs" #-}+      (accept nonEmpty [1, 2, 3])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:711: "+{-# LINE 711 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 711 "src/Test/Predicates.hs" #-}+      (accept nonEmpty "")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:713: "+{-# LINE 713 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 713 "src/Test/Predicates.hs" #-}+      (accept nonEmpty "gas tank")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:721: "+{-# LINE 721 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 721 "src/Test/Predicates.hs" #-}+      (accept (sizeIs (lt 3)) ['a' .. 'f'])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:723: "+{-# LINE 723 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 723 "src/Test/Predicates.hs" #-}+      (accept (sizeIs (lt 3)) ['a' .. 'b'])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:744: "+{-# LINE 744 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 744 "src/Test/Predicates.hs" #-}+      (accept (elemsAre [lt 3, lt 4, lt 5]) [2, 3, 4])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:746: "+{-# LINE 746 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 746 "src/Test/Predicates.hs" #-}+      (accept (elemsAre [lt 3, lt 4, lt 5]) [2, 3, 4, 5])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:748: "+{-# LINE 748 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 748 "src/Test/Predicates.hs" #-}+      (accept (elemsAre [lt 3, lt 4, lt 5]) [2, 10, 4])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:780: "+{-# LINE 780 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 780 "src/Test/Predicates.hs" #-}+      (accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [1, 2, 3])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:782: "+{-# LINE 782 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 782 "src/Test/Predicates.hs" #-}+      (accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [2, 3, 1])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:784: "+{-# LINE 784 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 784 "src/Test/Predicates.hs" #-}+      (accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [1, 2, 3, 4])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:786: "+{-# LINE 786 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 786 "src/Test/Predicates.hs" #-}+      (accept (unorderedElemsAre [eq 1, eq 2, eq 3]) [1, 3])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:833: "+{-# LINE 833 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 833 "src/Test/Predicates.hs" #-}+      (accept (each (gt 5)) [4, 5, 6])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:835: "+{-# LINE 835 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 835 "src/Test/Predicates.hs" #-}+      (accept (each (gt 5)) [6, 7, 8])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:837: "+{-# LINE 837 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 837 "src/Test/Predicates.hs" #-}+      (accept (each (gt 5)) [])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:862: "+{-# LINE 862 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 862 "src/Test/Predicates.hs" #-}+      (accept (contains (gt 5)) [3, 4, 5])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:864: "+{-# LINE 864 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 864 "src/Test/Predicates.hs" #-}+      (accept (contains (gt 5)) [4, 5, 6])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:866: "+{-# LINE 866 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 866 "src/Test/Predicates.hs" #-}+      (accept (contains (gt 5)) [])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:874: "+{-# LINE 874 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 874 "src/Test/Predicates.hs" #-}+      (accept (containsAll [eq "foo", eq "bar"]) ["bar", "foo"])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:876: "+{-# LINE 876 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 876 "src/Test/Predicates.hs" #-}+      (accept (containsAll [eq "foo", eq "bar"]) ["foo"])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:878: "+{-# LINE 878 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 878 "src/Test/Predicates.hs" #-}+      (accept (containsAll [eq "foo", eq "bar"]) ["foo", "bar", "qux"])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:886: "+{-# LINE 886 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 886 "src/Test/Predicates.hs" #-}+      (accept (containsAll [startsWith "f", endsWith "o"]) ["foo"])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:888: "+{-# LINE 888 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 888 "src/Test/Predicates.hs" #-}+      (accept (contains (startsWith "f") `andP` contains (endsWith "o")) ["foo"])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:910: "+{-# LINE 910 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 910 "src/Test/Predicates.hs" #-}+      (accept (containsOnly [eq "foo", eq "bar"]) ["foo"])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:912: "+{-# LINE 912 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 912 "src/Test/Predicates.hs" #-}+      (accept (containsOnly [eq "foo", eq "bar"]) ["foo", "bar"])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:914: "+{-# LINE 914 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 914 "src/Test/Predicates.hs" #-}+      (accept (containsOnly [eq "foo", eq "bar"]) ["foo", "qux"])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:921: "+{-# LINE 921 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 921 "src/Test/Predicates.hs" #-}+      (accept (containsOnly [eq "foo", eq "bar"]) ["foo", "foo"])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:923: "+{-# LINE 923 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 923 "src/Test/Predicates.hs" #-}+      (accept (each (eq "foo" `orP` eq "bar")) ["foo", "foo"])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:949: "+{-# LINE 949 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 949 "src/Test/Predicates.hs" #-}+      (accept (keys (each (eq "foo"))) [("foo", 5)])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:952: "+{-# LINE 952 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 952 "src/Test/Predicates.hs" #-}+      (accept (keys (each (eq "foo"))) [("foo", 5), ("bar", 6)])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:968: "+{-# LINE 968 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 968 "src/Test/Predicates.hs" #-}+      (accept (values (each (eq 5))) [("foo", 5), ("bar", 5)])+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:971: "+{-# LINE 971 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 971 "src/Test/Predicates.hs" #-}+      (accept (values (each (eq 5))) [("foo", 5), ("bar", 6)])+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:989: "+{-# LINE 989 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 989 "src/Test/Predicates.hs" #-}+      (accept (eq 1.0) (sum (replicate 100 0.01)))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:996: "+{-# LINE 996 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 996 "src/Test/Predicates.hs" #-}+      (accept (approxEq 1.0) (sum (replicate 100 0.01)))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:998: "+{-# LINE 998 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 998 "src/Test/Predicates.hs" #-}+      (accept (approxEq 1.0) (sum (replicate 100 0.009999)))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1013: "+{-# LINE 1013 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1013 "src/Test/Predicates.hs" #-}+      (accept positive 1)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1016: "+{-# LINE 1016 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1016 "src/Test/Predicates.hs" #-}+      (accept positive 0)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1019: "+{-# LINE 1019 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1019 "src/Test/Predicates.hs" #-}+      (accept positive (-1))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1037: "+{-# LINE 1037 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1037 "src/Test/Predicates.hs" #-}+      (accept negative 1)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1040: "+{-# LINE 1040 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1040 "src/Test/Predicates.hs" #-}+      (accept negative 0)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1043: "+{-# LINE 1043 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1043 "src/Test/Predicates.hs" #-}+      (accept negative (-1))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1062: "+{-# LINE 1062 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1062 "src/Test/Predicates.hs" #-}+      (accept nonPositive 1)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1065: "+{-# LINE 1065 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1065 "src/Test/Predicates.hs" #-}+      (accept nonPositive 0)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1068: "+{-# LINE 1068 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1068 "src/Test/Predicates.hs" #-}+      (accept nonPositive (-1))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1076: "+{-# LINE 1076 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1076 "src/Test/Predicates.hs" #-}+      (accept nonNegative 1)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1079: "+{-# LINE 1079 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1079 "src/Test/Predicates.hs" #-}+      (accept nonNegative 0)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1082: "+{-# LINE 1082 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1082 "src/Test/Predicates.hs" #-}+      (accept nonNegative (-1))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1089: "+{-# LINE 1089 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1089 "src/Test/Predicates.hs" #-}+      (accept finite 1.0)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1091: "+{-# LINE 1091 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1091 "src/Test/Predicates.hs" #-}+      (accept finite (0 / 0))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1093: "+{-# LINE 1093 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1093 "src/Test/Predicates.hs" #-}+      (accept finite (1 / 0))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1111: "+{-# LINE 1111 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1111 "src/Test/Predicates.hs" #-}+      (accept infinite 1.0)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1113: "+{-# LINE 1113 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1113 "src/Test/Predicates.hs" #-}+      (accept infinite (0 / 0))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1115: "+{-# LINE 1115 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1115 "src/Test/Predicates.hs" #-}+      (accept infinite (1 / 0))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1131: "+{-# LINE 1131 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1131 "src/Test/Predicates.hs" #-}+      (accept nAn 1.0)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1133: "+{-# LINE 1133 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1133 "src/Test/Predicates.hs" #-}+      (accept nAn (0 / 0))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1135: "+{-# LINE 1135 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1135 "src/Test/Predicates.hs" #-}+      (accept nAn (1 / 0))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1153: "+{-# LINE 1153 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1153 "src/Test/Predicates.hs" #-}+      (accept (is even) 3)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1155: "+{-# LINE 1155 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1155 "src/Test/Predicates.hs" #-}+      (accept (is even) 4)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1173: "+{-# LINE 1173 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1173 "src/Test/Predicates.hs" #-}+      (accept $(qIs [| even |]) 3)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1175: "+{-# LINE 1175 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1175 "src/Test/Predicates.hs" #-}+      (accept $(qIs [| even |]) 4)+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1178: "+{-# LINE 1178 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1178 "src/Test/Predicates.hs" #-}+      (show $(qIs [| even |]))+  [ExpectedLine [LineChunk "\"even\""]]+ DocTest.printPrefix "Test.Predicates:1196: "+{-# LINE 1196 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1196 "src/Test/Predicates.hs" #-}+      (accept (with abs (gt 5)) (-6))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1198: "+{-# LINE 1198 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1198 "src/Test/Predicates.hs" #-}+      (accept (with abs (gt 5)) (-5))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1200: "+{-# LINE 1200 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1200 "src/Test/Predicates.hs" #-}+      (accept (with reverse (eq "olleh")) "hello")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1202: "+{-# LINE 1202 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1202 "src/Test/Predicates.hs" #-}+      (accept (with reverse (eq "olleh")) "goodbye")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1219: "+{-# LINE 1219 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1219 "src/Test/Predicates.hs" #-}+      (accept ($(qWith [| abs |]) (gt 5)) (-6))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1221: "+{-# LINE 1221 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1221 "src/Test/Predicates.hs" #-}+      (accept ($(qWith [| abs |]) (gt 5)) (-5))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1223: "+{-# LINE 1223 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1223 "src/Test/Predicates.hs" #-}+      (accept ($(qWith [| reverse |]) (eq "olleh")) "hello")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1225: "+{-# LINE 1225 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1225 "src/Test/Predicates.hs" #-}+      (accept ($(qWith [| reverse |]) (eq "olleh")) "goodbye")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1228: "+{-# LINE 1228 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1228 "src/Test/Predicates.hs" #-}+      (show ($(qWith [| abs |]) (gt 5)))+  [ExpectedLine [LineChunk "\"abs: > 5\""]]+ DocTest.printPrefix "Test.Predicates:1247: "+{-# LINE 1247 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1247 "src/Test/Predicates.hs" #-}+      (accept $(qMatch [p| Just (Left _) |]) Nothing)+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1249: "+{-# LINE 1249 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1249 "src/Test/Predicates.hs" #-}+      (accept $(qMatch [p| Just (Left _) |]) (Just (Left 5)))+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1251: "+{-# LINE 1251 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1251 "src/Test/Predicates.hs" #-}+      (accept $(qMatch [p| Just (Left _) |]) (Just (Right 5)))+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1254: "+{-# LINE 1254 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1254 "src/Test/Predicates.hs" #-}+      (show $(qMatch [p| Just (Left _) |]))+  [ExpectedLine [LineChunk "\"Just (Left _)\""]]+ DocTest.printPrefix "Test.Predicates:1276: "+{-# LINE 1276 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1276 "src/Test/Predicates.hs" #-}+      (accept (typed @String anything) "foo")+  [ExpectedLine [LineChunk "True"]]+ DocTest.printPrefix "Test.Predicates:1278: "+{-# LINE 1278 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1278 "src/Test/Predicates.hs" #-}+      (accept (typed @String (sizeIs (gt 5))) "foo")+  [ExpectedLine [LineChunk "False"]]+ DocTest.printPrefix "Test.Predicates:1280: "+{-# LINE 1280 "src/Test/Predicates.hs" #-}+ DocTest.example+{-# LINE 1280 "src/Test/Predicates.hs" #-}+      (accept (typed @String anything) (42 :: Int))+  [ExpectedLine [LineChunk "False"]]
+ test/DocTests/Test/Predicates/Internal/FlowMatcher.hs view
@@ -0,0 +1,20 @@+-- Do not edit! Automatically created with doctest-extract from src/Test/Predicates/Internal/FlowMatcher.hs+{-# LINE 14 "src/Test/Predicates/Internal/FlowMatcher.hs" #-}++{-# OPTIONS_GHC -Wno-type-defaults #-}+module DocTests.Test.Predicates.Internal.FlowMatcher where++import Test.Predicates.Internal.FlowMatcher+import Test.DocTest.Base+import qualified Test.DocTest.Driver as DocTest++{-# LINE 16 "src/Test/Predicates/Internal/FlowMatcher.hs" #-}++test :: DocTest.T ()+test = do+ DocTest.printPrefix "Test.Predicates.Internal.FlowMatcher:23: "+{-# LINE 23 "src/Test/Predicates/Internal/FlowMatcher.hs" #-}+ DocTest.example+{-# LINE 23 "src/Test/Predicates/Internal/FlowMatcher.hs" #-}+      (bipartiteMatching (==) [1 .. 5] [6, 5 .. 2])+  [ExpectedLine [LineChunk "([(2,2),(3,3),(4,4),(5,5)],[1],[6])"]]
+ test/Main.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}++import Data.List (isPrefixOf)+import Data.Typeable (Typeable)+import qualified DocTests.All+import qualified Test.DocTest.Driver as DocTest+import Test.Hspec+import Test.Predicates++main :: IO ()+main = do+  hspec predicateTests+  DocTest.run DocTests.All.main++predicateTests :: SpecWith ()+predicateTests = do+  describe "Predicate" $ do+    it "describes itself" $+      example $ do+        show anything `shouldBe` "anything"+        show (eq "foo") `shouldBe` "\"foo\""+        show (neq "foo") `shouldBe` "≠ \"foo\""+        show (gt "foo") `shouldBe` "> \"foo\""+        show (geq "foo") `shouldBe` "≥ \"foo\""+        show (lt "foo") `shouldBe` "< \"foo\""+        show (leq "foo") `shouldBe` "≤ \"foo\""+        show (just (gt "foo")) `shouldBe` "Just (> \"foo\")"+        show (left (gt "foo")) `shouldBe` "Left (> \"foo\")"+        show (right (gt "foo")) `shouldBe` "Right (> \"foo\")"+        show (zipP (eq 1) (eq 2) :: Predicate (Int, Int)) `shouldBe` "(1,2)"+        show (zip3P (eq 1) (eq 2) (eq 3) :: Predicate (Int, Int, Int))+          `shouldBe` "(1,2,3)"+        show+          ( zip4P (eq 1) (eq 2) (eq 3) (eq 4) ::+              Predicate (Int, Int, Int, Int)+          )+          `shouldBe` "(1,2,3,4)"+        show+          ( zip5P (eq 1) (eq 2) (eq 3) (eq 4) (eq 5) ::+              Predicate (Int, Int, Int, Int, Int)+          )+          `shouldBe` "(1,2,3,4,5)"+        show (lt "foo" `andP` gt "bar")+          `shouldBe` "< \"foo\" and > \"bar\""+        show (lt "bar" `orP` gt "foo")+          `shouldBe` "< \"bar\" or > \"foo\""+        show (notP (gt "foo")) `shouldBe` "≤ \"foo\""+        show (startsWith "fun") `shouldBe` "starts with \"fun\""+        show (endsWith "ing") `shouldBe` "ends with \"ing\""+        show (hasSubstr "i") `shouldBe` "has substring \"i\""+        show (hasSubsequence "abc") `shouldBe` "has subsequence \"abc\""+        show (caseInsensitive eq "foo") `shouldBe` "(case insensitive) \"foo\""+        show (caseInsensitive startsWith "foo")+          `shouldBe` "(case insensitive) starts with \"foo\""+        show (caseInsensitive endsWith "foo")+          `shouldBe` "(case insensitive) ends with \"foo\""+        show (matchesRegex "foo" :: Predicate String)+          `shouldBe` "/foo/"+        show (matchesCaseInsensitiveRegex "foo" :: Predicate String)+          `shouldBe` "/foo/i"+        show (containsRegex "foo" :: Predicate String)+          `shouldBe` "contains /foo/"+        show (containsCaseInsensitiveRegex "foo" :: Predicate String)+          `shouldBe` "contains /foo/i"+        show (isEmpty :: Predicate [()]) `shouldBe` "empty"+        show (nonEmpty :: Predicate [()]) `shouldBe` "non-empty"+        show (sizeIs (gt 5) :: Predicate [()]) `shouldBe` "size > 5"+        show (elemsAre [gt 5, eq 5] :: Predicate [Int]) `shouldBe` "[> 5,5]"+        show (unorderedElemsAre [gt 5, eq 5] :: Predicate [Int])+          `shouldBe` "(any order) [> 5,5]"+        show (each (gt 5) :: Predicate [Int]) `shouldBe` "each (> 5)"+        show (contains (gt 5) :: Predicate [Int]) `shouldBe` "contains (> 5)"+        show (containsAll [gt 5] :: Predicate [Int])+          `shouldBe` "contains all of [> 5]"+        show (containsOnly [gt 5] :: Predicate [Int])+          `shouldBe` "contains only [> 5]"+        show (keys (contains (eq "foo")) :: Predicate [(String, String)])+          `shouldBe` "keys (contains (\"foo\"))"+        show+          ( contains (eq "foo" `zipP` eq "bar") ::+              Predicate [(String, String)]+          )+          `shouldBe` "contains ((\"foo\",\"bar\"))"+        show+          ( keys (unorderedElemsAre [eq 1, eq 2, eq 3]) ::+              Predicate [(Int, String)]+          )+          `shouldBe` "keys ((any order) [1,2,3])"+        show+          (values (elemsAre [eq "one", eq "two"]) :: Predicate [(Int, String)])+          `shouldBe` "values ([\"one\",\"two\"])"+        show (approxEq 1.0 :: Predicate Double) `shouldBe` "≈ 1.0"+        show (finite :: Predicate Double) `shouldBe` "finite"+        show (infinite :: Predicate Double) `shouldBe` "infinite"+        show (positive :: Predicate Double) `shouldBe` "positive"+        show (negative :: Predicate Double) `shouldBe` "negative"+        show (nonPositive :: Predicate Double) `shouldBe` "non-positive"+        show (nonNegative :: Predicate Double) `shouldBe` "non-negative"+        show (nAn :: Predicate Double) `shouldBe` "NaN"+        show (is even :: Predicate Int)+          `shouldSatisfy` ("custom predicate at " `isPrefixOf`)+        show ($(qIs [|even|]) :: Predicate Int) `shouldBe` "even"+        show (with length (gt 5) :: Predicate String)+          `shouldSatisfy` ("property at " `isPrefixOf`)+        show ($(qWith [|length|]) (gt 5) :: Predicate String)+          `shouldBe` "length: > 5"++    it "matches patterns" $+      example $ do+        let p = $(qMatch [p|Just (Left _)|])+        show p `shouldBe` "Just (Left _)"++        accept p (Just (Left "foo")) `shouldBe` True+        accept p (Just (Right "foo")) `shouldBe` False+        accept p Nothing `shouldBe` False++    it "checks types" $+      example $ do+        let p1 :: Typeable a => Predicate a+            p1 = typed @String anything++            p2 :: Typeable a => Predicate a+            p2 = typed @String (eq "foo")++        show (p1 :: Predicate String)+          `shouldBe` "anything :: [Char]"+        show (p1 :: Predicate Int)+          `shouldBe` "anything :: [Char]"++        accept p1 "foo" `shouldBe` True+        accept p1 "bar" `shouldBe` True+        accept p1 () `shouldBe` False+        accept p1 (5 :: Int) `shouldBe` False++        show (p2 :: Predicate String)+          `shouldBe` "\"foo\" :: [Char]"+        show (p2 :: Predicate Int)+          `shouldBe` "\"foo\" :: [Char]"++        accept p2 "foo" `shouldBe` True+        accept p2 "bar" `shouldBe` False+        accept p2 (5 :: Int) `shouldBe` False