packages feed

leancheck (empty) → 0.3.0

raw patch · 29 files changed

+3195/−0 lines, 29 filesdep +basedep +template-haskellsetup-changed

Dependencies added: base, template-haskell

Files

+ CREDITS.md view
@@ -0,0 +1,10 @@+Credits+=======++Rudy Matela:+  original implementation of LeanCheck previously known as "llcheck".++Colin Runciman:+  pointing out "why not a list of lists?";+  improvements in library interface and code;+  the name "LeanCheck".
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015-2016, Rudy Matela++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 Rudy Matela 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.
+ README.md view
@@ -0,0 +1,150 @@+LeanCheck+=========++**The API is likely to change in the near future**++LeanCheck is a simple enumerative property-based testing library.  It works by+producing *tiers* of test values, which are essentially (possibly infinite)+lists of finite lists of same-and-increasingly-sized values.  It is similar to+[Feat] in that regard.++In this README, lines ending with `-- >` indicate expected return values.+++Checking if properties are True+-------------------------------++To check if properties are True,+just use the function `holds :: Testable a => Int -> a -> Bool`.+It takes _two arguments_:+the _number of values_ to test+and a _property_ (function returning Bool),+then, it returns a boolean indicating whether the property holds.+See (ghci):++	import Test.Check+	import Data.List+	holds 100 $ \xs -> sort (sort xs) == sort (xs::[Int])  -- > True+	holds 100 $ \xs -> [] `union` xs == (xs::[Int])        -- > False+++Finding counter examples+------------------------++To find counter examples to properties,+you can use the function `counterExample :: Testable a => Int -> a -> Maybe [String]`.+It takes _two arguments_:+the _number of values_ to test+and a _property_ (function returning Bool).+Then, it returns Nothing if no results are found or Just a list of Strings+representing the offending arguments to the property.+See (ghci):++	import Test.Check+	import Data.List++	counterExample 100 $ \xs -> sort (sort xs) == sort (xs::[Int])+	-- > Nothing++	counterExample 100 $ \xs -> [] `union` xs == (xs::[Int])+	-- > Just ["[0,0]"]++	counterExample 100 $ \xs ys -> xs `union` ys == ys `union` (xs::[Int])+	-- > Just ["[]","[0,0]"]+++Checking properties like in SmallCheck/QuickCheck+-------------------------------------------------++To "check" properties like in [SmallCheck] and [QuickCheck]+automatically printing results on standard output,+you can use the function `check :: Testable a => a -> IO ()`.++	import Test.Check+	import Data.List++	check $ \xs -> sort (sort xs) == sort (xs::[Int])+	-- > OK, passed 200 tests.++	check $ \xs ys -> xs `union` ys == ys `union` (xs::[Int])+	-- > Failed! Falsifiable (after 4 tests):+	-- > [] [0,0]++The function `check` tests for a maximum of 200 tests.+To check for a maximum of `n` tests, use `checkFor n`.+To get a boolean result wrapped in `IO`, use `checkResult` or `checkResultFor`.+There is no "quiet" option, just use `holds` or `counterExample` in that case.+++Testing for custom types+------------------------++LeanCheck works on properties with `Listable` argument types.+Custom `Listable` instances are created similarly to SmallCheck:++	data MyType = MyConsA+	            | MyConsB Int+	            | MyConsC Int Char+	            | MyConsD String++	instance Listable MyType where+	  tiers = cons0 MyConsA+	       \/ cons1 MyConsB+	       \/ cons2 MyConsC+	       \/ cons1 MyConsD++The tiers function return a potentially infinite list of finite sub-lists (tiers).+Each tier has values of increasing size.++	tiers :: Listable a => [[a]]++For convenience, there is also the function `list`,+which returns an infinite list of values of the bound type:++	list :: Listable a => [a]++So, for example:++	take 5 (list :: [(Int,Int)]) -- > [(0,0),(0,1),(1,0),(0,-1),(1,1)]++The `list` function can be used to debug your custom instances.+++More information / extra functions+----------------------------------++`Listable` class instances are more customizable than what is described here:+check source comments or haddock documentation for details.+++Building / Installing+---------------------++To build:++	$ cabal build++To install:++	$ cabal install++To reference in a cabal sandbox:++	$ cabal sandbox add-source ../path/to/leancheck++To use the files directly in your project:++	$ cp -r Test ../path/to/your-project+++LeanCheck was tested on GHC 7.10, GHC 7.8, GHC 7.6 and GHC 7.4.+This library does not use any fancy extensions:+if it does not work on previous GHC versions,+probably only *minor* changes are needed.+It optionally depends on Template Haskell+(for automatic derivation of Listable instances).+++[Feat]: https://hackage.haskell.org/package/testing-feat+[SmallCheck]: https://hackage.haskell.org/package/smallcheck+[QuickCheck]: https://hackage.haskell.org/package/QuickCheck
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Test/Check.hs view
@@ -0,0 +1,112 @@+-- | A simple property-based testing library based on+--   enumeration of values via lists of lists.+--+-- In the context of this library,+-- a __property__ is a function returning a 'Bool'+-- that should return 'True' for all input values.+--+-- To check if a property holds by testing a thousand values, you simply do:+--+-- > holds 1000 property  -- yield True when Ok, False otherwise+--+-- For example:+--+-- > holds $ \xs -> length (sort xs) == length (xs::[Int])+--+-- Arguments of properties should be instances of the 'Listable' typeclass.+-- 'Listable' instances are provided for the most common Haskell types.+-- New instances are easily defined+-- (see the 'Listable's documentation for more info).+module Test.Check+  (+  -- * Checking and testing+    holds+  , fails+  , exists++  -- ** Boolean (property) operators+  , (==>)++  -- ** Counterexamples and witnesses+  , counterExample+  , counterExamples+  , witness+  , witnesses++  -- * Listing test values+  , Listable(..)++  -- ** Listing constructors+  , cons0+  , cons1+  , cons2+  , cons3+  , cons4+  , cons5+  , cons6+  , cons7+  , cons8+  , cons9+  , cons10+  , cons11+  , cons12++  , ofWeight+  , addWeight+  , suchThat++  -- ** Combining tiers+  , (\/)+  , (\\//)+  , (><)+  , productWith++  -- ** Manipulating tiers+  , mapT+  , filterT+  , concatT+  , concatMapT+  , deleteT+  , normalizeT+  , toTiers++  -- ** Automatically deriving Listable instances+  , deriveListable++  -- ** Extra constructors+  , consFromList+  , consFromAscendingList+  , consFromStrictlyAscendingList+  , consFromSet+  , consFromNoDupList++  -- ** Products of tiers+  , product3With+  , productMaybeWith++  -- * Listing lists+  , listsOf+  , setsOf+  , ascendingListsOf+  , strictlyAscendingListsOf+  , noDupListsOf+  , products+  , listsOfLength++  -- ** Listing values+  , tiersFractional+  , listIntegral+  , (+|)++  -- * Test results+  , Testable+  , results++  , module Test.Check.IO+  )+where++import Test.Check.Basic+import Test.Check.Utils+import Test.Check.Derive+import Test.Check.IO
+ Test/Check/Basic.hs view
@@ -0,0 +1,123 @@+-- | Simple property-based testing library based on+--   enumeration of values via lists of lists.+--+-- This module exports "Test.Check.Core" functionality along with instances and+-- functions for further tuple and constructor arities.+--+-- For the complete list of functions, see "Test.Check".+module Test.Check.Basic+  ( module Test.Check.Core++  , cons6+  , cons7+  , cons8+  , cons9+  , cons10+  , cons11+  , cons12+  )+where++import Test.Check.Core++instance (Listable a, Listable b, Listable c,+          Listable d, Listable e, Listable f) =>+         Listable (a,b,c,d,e,f) where+  tiers = productWith (\x (y,z,w,v,u) -> (x,y,z,w,v,u)) tiers tiers++instance (Listable a, Listable b, Listable c, Listable d,+          Listable e, Listable f, Listable g) =>+         Listable (a,b,c,d,e,f,g) where+  tiers = productWith (\x (y,z,w,v,u,r) -> (x,y,z,w,v,u,r)) tiers tiers++instance (Listable a, Listable b, Listable c, Listable d,+          Listable e, Listable f, Listable g, Listable h) =>+         Listable (a,b,c,d,e,f,g,h) where+  tiers = productWith (\x (y,z,w,v,u,r,s) -> (x,y,z,w,v,u,r,s))+                      tiers tiers++instance (Listable a, Listable b, Listable c, Listable d, Listable e,+          Listable f, Listable g, Listable h, Listable i) =>+         Listable (a,b,c,d,e,f,g,h,i) where+  tiers = productWith (\x (y,z,w,v,u,r,s,t) -> (x,y,z,w,v,u,r,s,t))+                      tiers tiers++instance (Listable a, Listable b, Listable c, Listable d, Listable e,+          Listable f, Listable g, Listable h, Listable i, Listable j) =>+         Listable (a,b,c,d,e,f,g,h,i,j) where+  tiers = productWith (\x (y,z,w,v,u,r,s,t,o) -> (x,y,z,w,v,u,r,s,t,o))+                      tiers tiers++instance (Listable a, Listable b, Listable c, Listable d,+          Listable e, Listable f, Listable g, Listable h,+          Listable i, Listable j, Listable k) =>+         Listable (a,b,c,d,e,f,g,h,i,j,k) where+  tiers = productWith (\x (y,z,w,v,u,r,s,t,o,p) -> (x,y,z,w,v,u,r,s,t,o,p))+                      tiers tiers++instance (Listable a, Listable b, Listable c, Listable d,+          Listable e, Listable f, Listable g, Listable h,+          Listable i, Listable j, Listable k, Listable l) =>+         Listable (a,b,c,d,e,f,g,h,i,j,k,l) where+  tiers = productWith (\x (y,z,w,v,u,r,s,t,o,p,q) ->+                        (x,y,z,w,v,u,r,s,t,o,p,q))+                      tiers tiers++cons6 :: (Listable a, Listable b, Listable c, Listable d, Listable e, Listable f)+      => (a -> b -> c -> d -> e -> f -> g) -> [[g]]+cons6 f = mapT (uncurry6 f) tiers `addWeight` 1++cons7 :: (Listable a, Listable b, Listable c, Listable d,+          Listable e, Listable f, Listable g)+      => (a -> b -> c -> d -> e -> f -> g -> h) -> [[h]]+cons7 f = mapT (uncurry7 f) tiers `addWeight` 1++cons8 :: (Listable a, Listable b, Listable c, Listable d,+          Listable e, Listable f, Listable g, Listable h)+      => (a -> b -> c -> d -> e -> f -> g -> h -> i) -> [[i]]+cons8 f = mapT (uncurry8 f) tiers `addWeight` 1++cons9 :: (Listable a, Listable b, Listable c, Listable d, Listable e,+          Listable f, Listable g, Listable h, Listable i)+      => (a -> b -> c -> d -> e -> f -> g -> h -> i -> j) -> [[j]]+cons9 f = mapT (uncurry9 f) tiers `addWeight` 1++cons10 :: (Listable a, Listable b, Listable c, Listable d, Listable e,+           Listable f, Listable g, Listable h, Listable i, Listable j)+       => (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k) -> [[k]]+cons10 f = mapT (uncurry10 f) tiers `addWeight` 1++cons11 :: (Listable a, Listable b, Listable c, Listable d,+           Listable e, Listable f, Listable g, Listable h,+           Listable i, Listable j, Listable k)+       => (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> l) -> [[l]]+cons11 f = mapT (uncurry11 f) tiers `addWeight` 1++cons12 :: (Listable a, Listable b, Listable c, Listable d,+           Listable e, Listable f, Listable g, Listable h,+           Listable i, Listable j, Listable k, Listable l)+       => (a->b->c->d->e->f->g->h->i->j->k->l->m) -> [[m]]+cons12 f = mapT (uncurry12 f) tiers `addWeight` 1++uncurry6 :: (a->b->c->d->e->f->g) -> (a,b,c,d,e,f) -> g+uncurry6 f (x,y,z,w,v,u) = f x y z w v u++uncurry7 :: (a->b->c->d->e->f->g->h) -> (a,b,c,d,e,f,g) -> h+uncurry7 f (x,y,z,w,v,u,r) = f x y z w v u r++uncurry8 :: (a->b->c->d->e->f->g->h->i) -> (a,b,c,d,e,f,g,h) -> i+uncurry8 f (x,y,z,w,v,u,r,s) = f x y z w v u r s++uncurry9 :: (a->b->c->d->e->f->g->h->i->j) -> (a,b,c,d,e,f,g,h,i) -> j+uncurry9 f (x,y,z,w,v,u,r,s,t) = f x y z w v u r s t++uncurry10 :: (a->b->c->d->e->f->g->h->i->j->k) -> (a,b,c,d,e,f,g,h,i,j) -> k+uncurry10 f (x,y,z,w,v,u,r,s,t,o) = f x y z w v u r s t o++uncurry11 :: (a->b->c->d->e->f->g->h->i->j->k->l)+          -> (a,b,c,d,e,f,g,h,i,j,k) -> l+uncurry11 f (x,y,z,w,v,u,r,s,t,o,p) = f x y z w v u r s t o p++uncurry12 :: (a->b->c->d->e->f->g->h->i->j->k->l->m)+          -> (a,b,c,d,e,f,g,h,i,j,k,l) -> m+uncurry12 f (x,y,z,w,v,u,r,s,t,o,p,q) = f x y z w v u r s t o p q
+ Test/Check/Core.hs view
@@ -0,0 +1,384 @@+-- | Simple property-based testing library based on+--   enumeration of values via lists of lists.+--+-- This is the core module of the library, with the most basic definitions.  If+-- you are looking just to use the library, import and see "Test.Check".+--+-- If you want to understand how the code works, this is the place to start.+--+--+-- Other important modules:+--+-- "Test.Check.Basic" re-exports (almost) everything from this module+--         along with constructors and instances for further arities.+--+-- "Test.Check.Utils" re-exports "Test.Check.Basic"+--         along with functions for advanced Listable instance definitions.+--+-- "Test.Check" re-exports "Test.Check.Utils"+--   along with a TH function to automatically derive Listable instances.+module Test.Check.Core+  (+  -- * Checking and testing+    holds+  , fails+  , exists+  , counterExample+  , counterExamples+  , witness+  , witnesses+  , Testable++  , results++  -- * Listing test values+  , Listable(..)++  -- ** Constructing lists of tiers+  , cons0+  , cons1+  , cons2+  , cons3+  , cons4+  , cons5++  , ofWeight+  , addWeight+  , suchThat++  -- ** Combining lists of tiers+  , (\/), (\\//)+  , (><)+  , productWith++  -- ** Manipulating lists of tiers+  , mapT+  , filterT+  , concatT+  , concatMapT+  , toTiers++  -- ** Boolean (property) operators+  , (==>)++  -- ** Misc utilities+  , (+|)+  , listIntegral+  , tiersFractional+  )+where++import Data.Maybe (listToMaybe)+++-- | A type is 'Listable' when there exists a function that+--   is able to list (ideally all of) its values.+--+-- Ideally, this type should be defined by a 'tiers' function that+-- returns a (possibly infinite) list of finite sub-lists (tiers):+--   the first sub-list contains elements of size 0,+--   the second sub-list contains elements of size 1+--   and so on.+-- Size here is defined by the implementor of the type-class instance.+--+-- For algebraic data types, the general form for 'tiers' is:+--+-- > tiers = consN ConstructorA+-- >      \/ consN ConstructorB+-- >      \/ consN ConstructorC+-- >      \/ ...+--+-- When defined by 'list', each sub-list in 'tiers' is a singleton list+-- (each element of 'list' has +1 size).+--+-- The function 'Test.Check.Derive.deriveListable' from "Test.Check.Derive"+-- can automatically derive instances of this typeclass.+--+-- A 'Listable' instance for functions is also available but is not exported by+-- default.  Import "Test.Check.Function" for that.+-- ("Test.Check.Function.Show" for a Show instance for functions)+class Listable a where+  tiers :: [[a]]+  list :: [a]+  tiers = toTiers list+  list = concat tiers+  {-# MINIMAL list | tiers #-}++-- | Takes a list of values @xs@ and transform it into tiers on which each+--   tier is occupied by a single element from @xs@.+--+-- To convert back to a list, just 'concat'.+toTiers :: [a] -> [[a]]+toTiers = map (:[])++instance Listable () where+  list = [()]++listIntegral :: (Enum a, Num a) => [a]+listIntegral = [0,-1..] +| [1..]++instance Listable Int where+  list = listIntegral++instance Listable Integer where+  list = listIntegral++instance Listable Char where+  list = ['a'..'z']+      +| [' ','\n']+      +| ['A'..'Z']+      +| ['0'..'9']+      +| ['!'..'/']+      +| ['\t']+      +| [':'..'@']+      +| ['['..'`']+      +| ['{'..'~']++instance Listable Bool where+  tiers = cons0 False \/ cons0 True++instance Listable a => Listable (Maybe a) where+  tiers = cons0 Nothing \/ cons1 Just++instance (Listable a, Listable b) => Listable (Either a b) where+  tiers = cons1 Left  `ofWeight` 0+     \\// cons1 Right `ofWeight` 0++instance (Listable a, Listable b) => Listable (a,b) where+  tiers = tiers >< tiers++instance (Listable a, Listable b, Listable c) => Listable (a,b,c) where+  tiers = productWith (\x (y,z) -> (x,y,z)) tiers tiers++instance (Listable a, Listable b, Listable c, Listable d) =>+         Listable (a,b,c,d) where+  tiers = productWith (\x (y,z,w) -> (x,y,z,w)) tiers tiers++instance (Listable a, Listable b, Listable c, Listable d, Listable e) =>+         Listable (a,b,c,d,e) where+  tiers = productWith (\x (y,z,w,v) -> (x,y,z,w,v)) tiers tiers++instance (Listable a) => Listable [a] where+  tiers = cons0 []+       \/ cons2 (:)++-- | Tiers of 'Fractional' values.+--   This can be used as the implementation of 'tiers' for 'Fractional' types.+tiersFractional :: Fractional a => [[a]]+tiersFractional = productWith (+) tiersFractionalParts+                                  (mapT fromIntegral (tiers::[[Integer]]))+               \/ [ [], [], [1/0], [-1/0] {- , [-0], [0/0] -} ]+  where tiersFractionalParts :: Fractional a => [[a]]+        tiersFractionalParts = [0]+                             : [ [fromIntegral a / fromIntegral b]+                               | b <- iterate (*2) 2, a <- [1::Integer,3..b] ]+-- The position of Infinity in the above enumeration is arbitrary.++-- Note that this instance ignores NaN's.+instance Listable Float where+  tiers = tiersFractional++instance Listable Double where+  tiers = tiersFractional+++-- | 'map' over tiers+mapT :: (a -> b) -> [[a]] -> [[b]]+mapT = map . map++-- | 'filter' tiers+filterT :: (a -> Bool) -> [[a]] -> [[a]]+filterT f = map (filter f)++-- | 'concat' tiers of tiers+concatT :: [[ [[a]] ]] -> [[a]]+concatT = foldr (\+:/) [] . map (foldr (\/) [])+  where xss \+:/ yss = xss \/ ([]:yss)++-- | 'concatMap' over tiers+concatMapT :: (a -> [[b]]) -> [[a]] -> [[b]]+concatMapT f = concatT . mapT f+++-- | Takes a constructor with no arguments and return tiers (with a single value).+--   This value, by default, has size/weight 0.+cons0 :: a -> [[a]]+cons0 x = [[x]]++-- | Takes a constructor with one argument and return tiers of that value.+--   This value, by default, has size/weight 1.+cons1 :: Listable a => (a -> b) -> [[b]]+cons1 f = mapT f tiers `addWeight` 1++-- | Takes a constructor with two arguments and return tiers of that value.+--   This value, by default, has size/weight 1.+cons2 :: (Listable a, Listable b) => (a -> b -> c) -> [[c]]+cons2 f = mapT (uncurry f) tiers `addWeight` 1++cons3 :: (Listable a, Listable b, Listable c) => (a -> b -> c -> d) -> [[d]]+cons3 f = mapT (uncurry3 f) tiers `addWeight` 1++cons4 :: (Listable a, Listable b, Listable c, Listable d)+      => (a -> b -> c -> d -> e) -> [[e]]+cons4 f = mapT (uncurry4 f) tiers `addWeight` 1++cons5 :: (Listable a, Listable b, Listable c, Listable d, Listable e)+      => (a -> b -> c -> d -> e -> f) -> [[f]]+cons5 f = mapT (uncurry5 f) tiers `addWeight` 1++-- | Resets the weight of a constructor (or tiers)+-- Typically used as an infix constructor when defining Listable instances:+--+-- > cons<N> `ofWeight` W+--+-- Be careful: do not apply @`ofWeight` 0@ to recursive data structure+-- constructors.  In general this will make the list of size 0 infinite,+-- breaking the tier invariant (each tier must be finite).+ofWeight :: [[a]] -> Int -> [[a]]+ofWeight xss w = dropWhile null xss `addWeight` w++-- | Adds to the weight of tiers of a constructor+addWeight :: [[a]] -> Int -> [[a]]+addWeight xss w = replicate w [] ++ xss++-- | Tiers of values that follow a property+--+-- > cons<N> `suchThat` condition+suchThat :: [[a]] -> (a->Bool) -> [[a]]+suchThat = flip filterT++-- | Lazily interleaves two lists, switching between elements of the two.+--   Union/sum of the elements in the lists.+--+-- > [x,y,z] +| [a,b,c] == [x,a,y,b,z,c]+(+|) :: [a] -> [a] -> [a]+[]     +| ys = ys+(x:xs) +| ys = x:(ys +| xs)+infixr 5 +|++-- | Append tiers.+--+-- > [xs,ys,zs,...] \/ [as,bs,cs,...] = [xs++as,ys++bs,zs++cs,...]+(\/) :: [[a]] -> [[a]] -> [[a]]+xss \/ []  = xss+[]  \/ yss = yss+(xs:xss) \/ (ys:yss) = (xs ++ ys) : xss \/ yss+infixr 7 \/++-- | Interleave tiers.  When in doubt, use @\/@ instead.+--+-- > [xs,ys,zs,...] \/ [as,bs,cs,...] = [xs+|as,ys+|bs,zs+|cs,...]+(\\//) :: [[a]] -> [[a]] -> [[a]]+xss \\// []  = xss+[]  \\// yss = yss+(xs:xss) \\// (ys:yss) = (xs +| ys) : xss \\// yss+infixr 7 \\//++-- | Take a tiered product of lists of tiers.+--+-- > [t0,t1,t2,...] >< [u0,u1,u2,...] =+-- > [ t0**u0+-- > , t0**u1 ++ t1**u0+-- > , t0**u2 ++ t1**u1 ++ t2**u0+-- > , ...       ...       ...       ...+-- > where xs ** ys = [(x,y) | x <- xs, y <- ys]+--+-- Example:+--+-- > [[0],[1],[2],...] >< [[0],[1],[2],...]+-- > == [  [(0,0)]+-- >    ,  [(1,0),(0,1)]+-- >    ,  [(2,0),(1,1),(0,2)]+-- >    ,  [(3,0),(2,1),(1,2),(0,3)]+-- >    ...+-- >    ]+(><) :: [[a]] -> [[b]] -> [[(a,b)]]+(><) = productWith (,)+infixr 8 ><++-- | Take the product of two lists of tiers.+--+-- > productWith f xss yss = map (uncurry f) $ xss >< yss+productWith :: (a->b->c) -> [[a]] -> [[b]] -> [[c]]+productWith _ _ [] = []+productWith _ [] _ = []+productWith f (xs:xss) yss = map (xs **) yss+                          \/ productWith f xss yss `addWeight` 1+  where xs ** ys = [x `f` y | x <- xs, y <- ys]++-- | 'Testable' values are functions+--   of 'Listable' arguments that return boolean values,+--   e.g.:+--+-- * @ Bool @+-- * @ Int -> Bool @+-- * @ Listable a => a -> a -> Bool @+class Testable a where+  resultiers :: a -> [[([String],Bool)]]++instance Testable Bool where+  resultiers p = [[([],p)]]++instance (Testable b, Show a, Listable a) => Testable (a->b) where+  resultiers p = concatMapT resultiersFor tiers+    where resultiersFor x = mapFst (showsPrec 11 x "":) `mapT` resultiers (p x)+          mapFst f (x,y) = (f x, y)++-- | List all results of a 'Testable' property.+-- Each results is composed by a list of strings and a boolean.+-- The list of strings represents the arguments applied to the function.+-- The boolean tells whether the property holds for that selection of argument.+-- This list is usually infinite.+results :: Testable a => a -> [([String],Bool)]+results = concat . resultiers++-- | Lists all counter-examples for a number of tests to a property,+counterExamples :: Testable a => Int -> a -> [[String]]+counterExamples n = map fst . filter (not . snd) . take n . results++-- | For a number of tests to a property,+--   returns Just the first counter-example or Nothing.+counterExample :: Testable a => Int -> a -> Maybe [String]+counterExample n = listToMaybe . counterExamples n++-- | Lists all witnesses for a number of tests to a property,+witnesses :: Testable a => Int -> a -> [[String]]+witnesses n = map fst . filter snd . take n . results++-- | For a number of tests to a property,+--   returns Just the first witness or Nothing.+witness :: Testable a => Int -> a -> Maybe [String]+witness n = listToMaybe . witnesses n++-- | Does a property __hold__ for a number of test values?+--+-- > holds 1000 $ \xs -> length (sort xs) == length xs+holds :: Testable a => Int -> a -> Bool+holds n = and . take n . map snd . results++-- | Does a property __fail__ for a number of test values?+--+-- > fails 1000 $ \xs -> xs ++ ys == ys ++ xs+fails :: Testable a => Int -> a -> Bool+fails n = not . holds n++-- | There __exists__ and assignment of values that satisfy a property?+exists :: Testable a => Int -> a -> Bool+exists n = or . take n . map snd . results++uncurry3 :: (a->b->c->d) -> (a,b,c) -> d+uncurry3 f (x,y,z) = f x y z++uncurry4 :: (a->b->c->d->e) -> (a,b,c,d) -> e+uncurry4 f (x,y,z,w) = f x y z w++uncurry5 :: (a->b->c->d->e->f) -> (a,b,c,d,e) -> f+uncurry5 f (x,y,z,w,v) = f x y z w v++-- | Boolean implication.  Use this for defining conditional properties:+--+-- > prop_something x y = condition x y ==> something x y+(==>) :: Bool -> Bool -> Bool+False ==> _ = True+True  ==> p = p+infixr 0 ==>
+ Test/Check/Derive.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE TemplateHaskell, CPP #-}+-- Experimental module for deriving Listable instances+--+-- Needs GHC and Template Haskell (tested on GHC 7.4, 7.6, 7.8 and 7.10)+module Test.Check.Derive+  ( deriveListable+  )+where++import Language.Haskell.TH+import Test.Check.Basic+import Control.Monad (unless, liftM, liftM2)++#if __GLASGOW_HASKELL__ < 706+-- reportWarning was only introduced in GHC 7.6 / TH 2.8+reportWarning :: String -> Q ()+reportWarning = report False+#endif++-- | Derives a Listable instance for a given type ('Name').+deriveListable :: Name -> DecsQ+deriveListable t = do+  is <- t `isInstanceOf` ''Listable+  if is+    then do reportWarning $ "Instance Listable "+                         ++ show t+                         ++ " already exists, skipping derivation"+            return []+    else do cd <- canDeriveListable t+            unless cd (fail $ "Unable to derive Listable "+                           ++ show t)+            reallyDeriveListable t++-- | Checks whether it is possible to derive a Listable instance.+--+-- For example, it is not possible if there are is no Listable instance for a+-- type in one of the constructors.+canDeriveListable :: Name -> Q Bool+canDeriveListable t = return True -- TODO: Check instances for type-cons args++-- TODO: Somehow check if the enumeration has repetitions, then warn the user.+reallyDeriveListable :: Name -> DecsQ+reallyDeriveListable t = do+  (nt,vs) <- normalizeType t+#if __GLASGOW_HASKELL__ >= 710+  cxt <- sequence [[t| Listable $(return v) |] | v <- vs]+#else+  cxt <- sequence [classP ''Listable [return v] | v <- vs]+#endif+#if __GLASGOW_HASKELL__ >= 708+  cxt |=>| [d| instance Listable $(return nt)+                 where tiers = $(conse =<< typeCons t) |]+#else+  tiersE <- conse =<< typeCons t+  return [ InstanceD+             cxt+             (AppT (ConT ''Listable) nt)+             [ValD (VarP 'tiers) (NormalB tiersE) []]+         ]+#endif+  where cone n arity = do+          (Just consN) <- lookupValueName $ "cons" ++ show arity+          [| $(varE consN) $(conE n) |]+        conse = foldr1 (\e1 e2 -> [| $e1 \/ $e2 |]) . map (uncurry cone)+++-- * Template haskell utilities++-- Normalizes a type by applying it to necessary type variables, making it+-- accept "zero" parameters.  The normalized type is tupled with a list of+-- necessary type variables.+--+-- Suppose:+--+-- > data DT a b c ... = ...+--+-- Then, in pseudo-TH:+--+-- > normalizeType [t|DT|] == Q (DT a b c ..., [a, b, c, ...])+normalizeType :: Name -> Q (Type, [Type])+normalizeType t = do+  ar <- typeArity t+  vs <- newVarTs ar+  return (foldl AppT (ConT t) vs, vs)+  where+    newNames :: [String] -> Q [Name]+    newNames = mapM newName+    newVarTs :: Int -> Q [Type]+    newVarTs n = liftM (map VarT)+               $ newNames (take n . map (:[]) $ cycle ['a'..'z'])++-- Normalizes a type by applying it to units (`()`) while possible.+--+-- > normalizeTypeUnits ''Int    === [t| Int |]+-- > normalizeTypeUnits ''Maybe  === [t| Maybe () |]+-- > normalizeTypeUnits ''Either === [t| Either () () |]+normalizeTypeUnits :: Name -> Q Type+normalizeTypeUnits t = do+  ar <- typeArity t+  return (foldl AppT (ConT t) (replicate ar (TupleT 0)))++-- Given a type name and a class name,+-- returns whether the type is an instance of that class.+isInstanceOf :: Name -> Name -> Q Bool+isInstanceOf tn cl = do+  ty <- normalizeTypeUnits tn+  isInstance cl [ty]++-- | Given a type name, return the number of arguments taken by that type.+-- Examples in partially broken TH:+--+-- > arity ''Int        === Q 0+-- > arity ''Int->Int   === Q 0+-- > arity ''Maybe      === Q 1+-- > arity ''Either     === Q 2+-- > arity ''Int->      === Q 1+--+-- This works for Data's and Newtype's and it is useful when generating+-- typeclass instances.+typeArity :: Name -> Q Int+typeArity t = do+  ti <- reify t+  return . length $ case ti of+    TyConI (DataD    _ _ ks _ _) -> ks+    TyConI (NewtypeD _ _ ks _ _) -> ks+    _                            -> error $ "error (arity): symbol "+                                         ++ show t+                                         ++ " is not a newtype or data"++-- Given a type name, returns a list of its type constructor names tupled with+-- the number of arguments they take.+typeCons :: Name -> Q [(Name,Int)]+typeCons t = do+  ti <- reify t+  return . map simplify $ case ti of+    TyConI (DataD    _ _ _ cs _) -> cs+    TyConI (NewtypeD _ _ _ c  _) -> [c]+    _ -> error $ "error (typeConstructors): symbol "+              ++ show t+              ++ " is neither newtype nor data"+  where simplify (NormalC n ts)  = (n,length ts)+        simplify (RecC    n ts)  = (n,length ts)+        simplify (InfixC  _ n _) = (n,2)++-- Append to instance contexts in a declaration.+--+-- > sequence [[|Eq b|],[|Eq c|]] |=>| [t|instance Eq a => Cl (Ty a) where f=g|]+-- > == [t| instance (Eq a, Eq b, Eq c) => Cl (Ty a) where f = g |]+(|=>|) :: Cxt -> DecsQ -> DecsQ+c |=>| qds = do ds <- qds+                return $ map (`ac` c) ds+  where ac (InstanceD c ts ds) c' = InstanceD (c++c') ts ds+        ac d                   _  = d
+ Test/Check/Error.hs view
@@ -0,0 +1,118 @@+-- | A simple property-based testing library based on+--   enumeration of values via lists of lists.+--+-- This module re-exports Test.Check but some test functions have been+-- specialized to catch errors (see the explicit export list below).+--+-- This module is unsafe, it uses `unsafePerformIO` to catch errors.+{-# LANGUAGE CPP #-}+module Test.Check.Error+  ( holds+  , fails+  , exists+  , counterExample+  , counterExamples+  , witness+  , witnesses+  , results++  , errorToNothing+  , errorToFalse+  , errorToTrue+  , anyErrorToNothing++  , module Test.Check+  )+where++#if __GLASGOW_HASKELL__ <= 704+import Prelude hiding (catch)+#endif++import Test.Check hiding+  ( holds+  , fails+  , exists+  , counterExample+  , counterExamples+  , witness+  , witnesses+  , results+  )++import qualified Test.Check as C+  ( holds+  , fails+  , results+  )++import Control.Monad (liftM)+import System.IO.Unsafe (unsafePerformIO)+import Data.Maybe (listToMaybe)+import Control.Exception ( Exception+                         , SomeException+                         , ArithException+                         , ArrayException+                         , ErrorCall+                         , PatternMatchFail+                         , catch+                         , catches+                         , Handler (Handler)+                         , evaluate+                         )++-- | Takes a value and a function.  Ignores the value.  Binds the argument of+--   the function to the type of the value.+bindArgumentType :: a -> (a -> b) -> a -> b+bindArgumentType _ f = f++-- | Transforms a value into 'Just' that value or 'Nothing' on some errors:+--+--   * ArithException+--   * ArrayException+--   * ErrorCall+--   * PatternMatchFail+errorToNothing :: a -> Maybe a+errorToNothing x = unsafePerformIO $+  (Just `liftM` evaluate x) `catches` map ($ return Nothing)+                                      [ hf (undefined :: ArithException)+                                      , hf (undefined :: ArrayException)+                                      , hf (undefined :: ErrorCall)+                                      , hf (undefined :: PatternMatchFail)+                                      ]+  where hf :: Exception e => e -> IO a -> Handler a -- handlerFor+        hf e h = Handler $ bindArgumentType e (\_ -> h)++-- | Transforms a value into 'Just' that value or 'Nothing' on error.+anyErrorToNothing :: a -> Maybe a+anyErrorToNothing x = unsafePerformIO $+  (Just `liftM` evaluate x) `catch` \e -> do let _ = e :: SomeException+                                             return Nothing++errorToFalse :: Bool -> Bool+errorToFalse p = case errorToNothing p of+                   Just p' -> p+                   Nothing -> False++errorToTrue :: Bool -> Bool+errorToTrue p = case errorToNothing p of+                  Just p' -> p+                  Nothing -> True+++holds,fails,exists :: Testable a => Int -> a -> Bool+holds n = errorToFalse . C.holds n+fails n = errorToTrue  . C.fails n+exists n = or . take n . map snd . results++counterExample,witness :: Testable a => Int -> a -> Maybe [String]+counterExample n = listToMaybe . counterExamples n+witness        n = listToMaybe . witnesses n++counterExamples,witnesses :: Testable a => Int -> a -> [[String]]+counterExamples n = map fst . filter (not . snd) . take n . results+witnesses       n = map fst . filter snd         . take n . results++results :: Testable a => a -> [([String],Bool)]+results = map (mapSnd errorToFalse) . C.results+  where mapSnd f (x,y) = (x,f y)
+ Test/Check/Function.hs view
@@ -0,0 +1,2 @@+module Test.Check.Function () where+import Test.Check.Function.ListsOfPairs ()
+ Test/Check/Function/CoListable.hs view
@@ -0,0 +1,65 @@+-- | Function enumeration via CoListable typeclass+--   This currently just a sketch.+module Test.Check.Function.CoListable+where+++import Test.Check.Core+import Test.Check.Utils+import Data.Maybe (fromMaybe)+++(\+:/) :: [[a]] -> [[a]] -> [[a]]+xss \+:/ yss = xss \/ ([]:yss)+infixr 9 \+:/+++class CoListable a where+  coListing :: [[b]] -> [[a -> b]]+++instance CoListable () where+  coListing rs = mapT (\r  () -> r) rs+++instance CoListable Bool where+  coListing rs = productWith (\r1 r2  b -> if b then r1 else r2) rs rs+++instance CoListable a => CoListable (Maybe a) where+  coListing rs = productWith (\z f  m -> case m of Nothing -> z+                                                   Just x  -> f x) rs (coListing rs)+++instance (CoListable a, CoListable b) => CoListable (Either a b) where+  coListing rs = productWith (\f g  e -> case e of Left x  -> f x+                                                   Right x -> g x) (coListing rs) (coListing rs)+++instance (CoListable a) => CoListable [a] where+  coListing rss = mapT const rss+             \+:/ productWith (\y f  xs -> case xs of []      -> y+                                                      (x:xs') -> f x xs') rss (coListing (coListing rss))+++instance CoListable Int where+  coListing rss = mapT const rss+             \+:/ product3With (\f g z  i -> if i > 0 then f (i-1)+                                        else if i < 0 then g (i+1)+                                             else z) (coListing rss) (coListing rss) rss+++alts0 :: [[a]] -> [[a]]+alts0 = id++alts1 :: CoListable a => [[b]] -> [[a->b]]+alts1 bs = coListing bs++alts2 :: (CoListable a, CoListable b) => [[c]] -> [[a->b->c]]+alts2 cs = coListing (coListing cs)++alts3 :: (CoListable a, CoListable b, CoListable c) => [[d]] -> [[a->b->c->d]]+alts3 ds = coListing (coListing (coListing ds))++fListing :: (CoListable a, Listable b) => [[a->b]]+fListing = coListing tiers
+ Test/Check/Function/ListsOfPairs.hs view
@@ -0,0 +1,62 @@+-- | Function enumeration via lists of pairs.+module Test.Check.Function.ListsOfPairs+  ( functionPairs+  , associations+  , pairsToFunction+  , defaultFunPairsToFunction+  )+where++import Test.Check.Core+import Test.Check.Utils+import Data.Maybe (fromMaybe)++instance (Eq a, Listable a, Listable b) => Listable (a -> b) where+  tiers = mapT (uncurry $ flip defaultPairsToFunction)+        $ functions list tiers+++functions :: [[a]] -> [[b]] -> [[([(a,b)],b)]]+functions xss yss =+  concatMapT+    (\(r,yss) -> mapT (\ps -> (ps,r)) $ functionPairs xss yss)+    (choices yss)+++-- | Given a list of domain values, and tiers of codomain values,+-- return tiers of lists of ordered pairs of domain and codomain values.+--+-- Technically: tiers of left-total functional relations.+associations :: [a] -> [[b]] -> [[ [(a,b)] ]]+associations xs sbs = zip xs `mapT` products (const sbs `map` xs)++-- | Given tiers of input values and tiers of output values,+-- return tiers with all possible lists of input-output pairs.+-- Those represent functional relations.+functionPairs :: [[a]] -> [[b]] -> [[[(a,b)]]]+functionPairs xss yss = concatMapT (`associations` yss)+                                   (strictlyAscendingListsOf xss)++-- | Returns a function given by a list of input-output pairs.+-- The result is wrapped in a maybe value.+-- The output for bound inputs is 'Just' a value.+-- The output for unbound inputs is 'Nothing'.+pairsToMaybeFunction :: Eq a => [(a,b)] -> a -> Maybe b+pairsToMaybeFunction []          _ = Nothing+pairsToMaybeFunction ((a',r):bs) a | a == a'   = Just r+                                   | otherwise = pairsToMaybeFunction bs a++-- | Returns a partial function given by a list of input-output pairs.+--+-- NOTE: This function *will* return undefined values for unbound inputs.+pairsToFunction :: Eq a => [(a,b)] -> a -> b+pairsToFunction bs a = fromMaybe undefined (pairsToMaybeFunction bs a)+++-- | Returns a function given by a list of input-output pairs and a default value.+defaultPairsToFunction :: Eq a => b -> [(a,b)] -> a -> b+defaultPairsToFunction r bs a = fromMaybe r (pairsToMaybeFunction bs a)+++defaultFunPairsToFunction :: Eq a => (a -> b) -> [(a,b)] -> a -> b+defaultFunPairsToFunction f bs a = fromMaybe (f a) (pairsToMaybeFunction bs a)
+ Test/Check/Function/Periodic.hs view
@@ -0,0 +1,47 @@+-- | Periodic function enumeration.+--   This is just a sketch.+module Test.Check.Function.Periodic+where+++import Test.Check.Basic+import Test.Check.Utils (listsOf)+import Data.List (inits)+++instance (Eq a, Eq b, Listable a, Listable b) => Listable (a -> b) where+  tiers = mapT pairsToFunction $ functions list tiers++functions :: Eq b => [a] -> [[b]] -> [[[(a,b)]]]+functions xs yss = mapT (zip xs . cycle) $ lsPeriodsOfLimit xs yss++functionsz :: Eq b => [[a]] -> [[b]] -> [[[(a,b)]]]+functionsz xss = functions (concat xss)+++lsPeriodsOf :: Eq a => [[a]] -> [[[a]]]+lsPeriodsOf xss = map (filter isPeriod) (listsOf xss)++lsPeriodsOfLimit :: Eq a => [b] -> [[a]] -> [[[a]]]+lsPeriodsOfLimit ys xss = map (filter isPeriod) (tiersOfLimit ys xss)+++isPeriod :: Eq a => [a] -> Bool+isPeriod [] = False+isPeriod [x] = True+isPeriod xs = not $ any (`isPeriodOf` xs) $ (tail . init . inits) xs++isPeriodOf :: Eq a => [a] -> [a] -> Bool+xs `isPeriodOf` ys = length ys `mod` length xs == 0+                  && and (zipWith (==) (cycle xs) ys)+++tiersOfLimit :: [b] -> [[a]] -> [[[a]]]+tiersOfLimit     [] xss = [[[]]]+tiersOfLimit (_:ys) xss = [[[]]] ++ productWith (:) xss (tiersOfLimit ys xss)+++pairsToFunction :: Eq a => [(a,b)] -> (a -> b)+pairsToFunction ((x,y):ps) x' =  if x' == x+                                   then y+                                   else pairsToFunction ps x'
+ Test/Check/Function/Show.hs view
@@ -0,0 +1,10 @@+-- | A 'Show' instance for functions.+module Test.Check.Function.Show () where++import Test.Check.ShowFunction++instance (Show a, Listable a, ShowFunction b) => Show (a->b) where+  showsPrec 0 = (++) . showFunction 8+  showsPrec _ = (++) . paren . showFunctionLine 4+    where paren s = "(" ++ s ++ ")"+
+ Test/Check/IO.hs view
@@ -0,0 +1,80 @@+-- | QuickCheck-like interface to LeanCheck+{-# LANGUAGE CPP #-}+module Test.Check.IO+  ( check+  , checkFor+  , checkResult+  , checkResultFor+  )+where++#if __GLASGOW_HASKELL__ <= 704+import Prelude hiding (catch)+#endif++import Test.Check.Core+import Data.Maybe (listToMaybe)+import Data.List (find)+import Control.Exception (SomeException, catch, evaluate)++-- | Check a property+--   printing results on 'stdout'+check :: Testable a => a -> IO ()+check p = checkResult p >> return ()++-- | Check a property for @N@ tests+--   printing results on 'stdout'+checkFor :: Testable a => Int -> a -> IO ()+checkFor n p = checkResultFor n p >> return ()++-- | Check a property+--   printing results on 'stdout' and+--   returning 'True' on success.+--+-- There is no option to silence this function:+-- in that case, you should use 'Test.Check.holds'.+checkResult :: Testable a => a -> IO Bool+checkResult p = checkResultFor 200 p++-- | Check a property for @N@ tests+--   printing results on 'stdout' and+--   returning 'True' on success.+--+-- There is no option to silence this function:+-- in that case, you should use 'Test.Check.holds'.+checkResultFor :: Testable a => Int -> a -> IO Bool+checkResultFor n p = do+  r <- resultIO n p+  putStrLn . showResult $ r+  return (isOK r)+  where isOK (OK _) = True+        isOK _      = False++data Result = OK        Int+            | Falsified Int [String]+            | Exception Int [String] String+  deriving (Eq, Show)++resultsIO :: Testable a => Int -> a -> IO [Result]+resultsIO n = sequence . zipWith torio [1..] . take n . results+  where+    tor i (_,True) = OK i+    tor i (as,False) = Falsified i as+    torio i r@(as,_) = evaluate (tor i r)+       `catch` \e -> let _ = e :: SomeException+                     in return (Exception i as (show e))++resultIO :: Testable a => Int -> a -> IO Result+resultIO n p = do+  rs <- resultsIO n p+  return . maybe (last rs) id+         $ find isFailure rs+  where isFailure (OK _) = False+        isFailure _      = True++showResult :: Result -> String+showResult (OK n)             = "+++ OK, passed " ++ show n ++ " tests."+showResult (Falsified i ce)   = "*** Failed! Falsifiable (after "+                             ++ show i ++ " tests):\n" ++ unwords ce+showResult (Exception i ce e) = "*** Failed! Exception '" ++ e ++ "' (after "+                             ++ show i ++ " tests):\n" ++ unwords ce
+ Test/Check/Invariants.hs view
@@ -0,0 +1,130 @@+-- | Some invariants over Test.Check functions+--   You should be importing this ONLY to test 'Check.hs' itself.+module Test.Check.Invariants+  ( tNatPairOrd+  , tNatTripleOrd+  , tNatQuadrupleOrd+  , tNatQuintupleOrd+  , tNatSixtupleOrd+  , tNatListOrd+  , tListsOfNatOrd+  , tPairEqParams+  , tTripleEqParams+  , tProductsIsFilterByLength++  , ordered+  , orderedBy+  , strictlyOrdered+  , strictlyOrderedBy+  )+where++import Test.Check+import Data.List+import Data.Ord+import Test.Types (Nat(..))++-- | check if a list is ordered+ordered :: Ord a => [a] -> Bool+ordered = orderedBy compare+-- ordered [] = True+-- ordered [_] = True+-- ordered (x:y:xs) = x <= y && ordered (y:xs)++strictlyOrdered :: Ord a => [a] -> Bool+strictlyOrdered = strictlyOrderedBy compare++-- | check if a list is ordered by a given ordering function+orderedBy :: (a -> a -> Ordering) -> [a] -> Bool+orderedBy _ [] = True+orderedBy _ [_] = True+orderedBy cmp (x:y:xs) = case x `cmp` y of+                           GT -> False+                           _  -> orderedBy cmp (y:xs)++-- | check if a list is strictly ordered by a given ordering function+strictlyOrderedBy :: (a -> a -> Ordering) -> [a] -> Bool+strictlyOrderedBy _ [] = True+strictlyOrderedBy _ [_] = True+strictlyOrderedBy cmp (x:y:xs) = case x `cmp` y of+                                   LT -> strictlyOrderedBy cmp (y:xs)+                                   _  -> False++ifNotEq :: Ordering -> Ordering -> Ordering+-- Could be implemented as:  ifNotEq = mappend+ifNotEq EQ p = p+ifNotEq  o _ = o++thn :: (a->a->Ordering) -> (a->a->Ordering) -> a -> a -> Ordering+thn cmp1 cmp2 x y = (x `cmp1` y) `ifNotEq` (x `cmp2` y)+infixr 9 `thn`+++-- | checks if the first 'n' elements on tiers are ordered by 'cmp'.+--+-- > (n `seriesOrderedBy`) comparing (id :: Type)+tOrderedBy :: Listable a => Int -> (a -> a -> Ordering) -> Bool+tOrderedBy n cmp = orderedBy cmp $ take n list+infixr 9 `tOrderedBy`++tStrictlyOrderedBy :: Listable a => Int -> (a -> a -> Ordering) -> Bool+tStrictlyOrderedBy n cmp = strictlyOrderedBy cmp $ take n list+infixr 9 `tStrictlyOrderedBy`++tNatPairOrd :: Int -> Bool+tNatPairOrd n = n `tStrictlyOrderedBy`  comparing sum' `thn` compare+  where sum' (x,y) = x+y :: Nat++tNatTripleOrd :: Int -> Bool+tNatTripleOrd n = n `tStrictlyOrderedBy`  comparing sum' `thn` compare+  where sum' (x,y,z) = x+y+z :: Nat++tNatQuadrupleOrd :: Int -> Bool+tNatQuadrupleOrd n = n `tStrictlyOrderedBy`  comparing sum' `thn` compare+  where sum' (x,y,z,w) = x+y+z+w :: Nat++tNatQuintupleOrd :: Int -> Bool+tNatQuintupleOrd n = n `tStrictlyOrderedBy`  comparing sum' `thn` compare+  where sum' (x,y,z,w,v) = x+y+z+w+v :: Nat++tNatSixtupleOrd :: Int -> Bool+tNatSixtupleOrd n = n `tStrictlyOrderedBy`  comparing sum' `thn` compare+  where sum' (x,y,z,w,v,u) = x+y+z+w+v+u :: Nat++tNatListOrd :: Int -> Bool+tNatListOrd n = n `tStrictlyOrderedBy`  comparing sum' `thn` compare+  where sum' = sum . map (+1) :: [Nat] -> Nat++tListsOfStrictlyOrderedBy :: Int+                           -> (a -> a -> Ordering)+                           -> [[a]]+                           -> Bool+tListsOfStrictlyOrderedBy n cmp = strictlyOrderedBy cmp . take n . concat+infixr 9 `tListsOfStrictlyOrderedBy`++tListsOfNatOrd :: Int -> Bool+tListsOfNatOrd n = tListsOfStrictlyOrderedBy n (comparing sum' `thn` compare) tiers+  where sum' = sum . map (+1) :: [Nat] -> Nat++tPairEqParams :: Int -> Bool+tPairEqParams n = ces == srs+  where+    ces = map (map read) $ counterExamples n fail+    srs = map pairToList $ take n list+    pairToList (x,y) = [x,y :: Nat]+    fail :: Nat -> Nat -> Bool+    fail x y = False++tTripleEqParams :: Int -> Bool+tTripleEqParams n = ces == srs+  where+    ces = map (map read) $ counterExamples n fail+    srs = map tripleToList $ take n list+    tripleToList (x,y,z) = [x,y,z :: Nat]+    fail :: Nat -> Nat -> Nat -> Bool+    fail x y z = False++tProductsIsFilterByLength :: Eq a => [[a]] -> Int -> Int -> Bool+tProductsIsFilterByLength values m n = concat (take m byProduct) `isPrefixOf` concat byFilter+  where byProduct = products $ replicate n values+        byFilter  = ((==n) . length) `filterT` listsOf values
+ Test/Check/ShowFunction.hs view
@@ -0,0 +1,173 @@+-- | This module exports the 'ShowFunction' typeclass,+--   its instances and related functions.+--+-- Using this module, it is possible to implement+-- a Show instance for functions:+--+-- > import Test.Check.ShowFunction+-- > instance (Show a, Listable a, ShowFunction b) => Show (a->b) where+-- >   show = showFunction 8+--+-- This shows functions as a case pattern with up to 8 cases.+--+-- The module+-- @Test.Check.Function.Show@ ('Test.Check.Function.Show')+-- exports an instance like the one above.+module Test.Check.ShowFunction+  ( showFunction+  , showFunctionLine+  , Binding+  , bindings+  , ShowFunction (..)+  , tBindingsShow+  -- * Re-exports+  , Listable+  )+where++import Test.Check.Core+import Test.Check.Error (errorToNothing)+import Data.List+import Data.Maybe++-- | A functional binding in a showable format.+type Binding = ([String], Maybe String)++-- | 'ShowFunction' values are those for which+--   we can return a list of functional bindings.+--+-- As a user, you probably want 'showFunction' and 'showFunctionLine'.+--+-- Non functional instances should be defined by:+--+-- > instance ShowFunction Ty where tBindings = tBindingsShow+class ShowFunction a where+  tBindings :: a -> [[Binding]]++-- | Given a 'ShowFunction' value, return a list of bindings+--   for printing.  Examples:+--+-- > bindings True == [([],True)]+-- > bindings (id::Int) == [(["0"],"0"), (["1"],"1"), (["-1"],"-1"), ...+-- > bindings (&&) == [ (["False","False"], "False")+-- >                  , (["False","True"], "False")+-- >                  , (["True","False"], "False")+-- >                  , (["True","True"], "True")+-- >                  ]+bindings :: ShowFunction a => a -> [Binding]+bindings = concat . tBindings+++-- instances for (algebraic/numeric) data types --+-- | A default implementation of tBindings for already 'Show'-able types.+tBindingsShow :: Show a => a -> [[Binding]]+tBindingsShow x = [[([],errorToNothing $ show x)]]++instance ShowFunction ()   where tBindings = tBindingsShow+instance ShowFunction Bool where tBindings = tBindingsShow+instance ShowFunction Int  where tBindings = tBindingsShow+instance ShowFunction Char where tBindings = tBindingsShow+instance Show a => ShowFunction [a]       where tBindings = tBindingsShow+instance Show a => ShowFunction (Maybe a) where tBindings = tBindingsShow+instance (Show a, Show b) => ShowFunction (a,b) where tBindings = tBindingsShow+++-- instance for functional value type --+instance (Show a, Listable a, ShowFunction b) => ShowFunction (a->b) where+  tBindings f = concatMapT tBindingsFor tiers+    where tBindingsFor x = mapFst (show x:) `mapT` tBindings (f x)+          mapFst f (x,y) = (f x, y)++paren :: String -> String+paren s = "(" ++ s ++ ")"++varnamesFor :: ShowFunction a => a -> [String]+varnamesFor = zipWith const varnames . fst . head . bindings+  where varnames = ["x","y","z","w"] ++ map (++"'") varnames++showTuple :: [String] -> String+showTuple [x] = x+showTuple xs  = paren $ intercalate "," xs++showNBindingsOf :: ShowFunction a => Int -> Int -> a -> [String]+showNBindingsOf m n f = take n bs+                     ++ ["..." | length bs' >= m || length bs > n]+  where bs' = take m $ bindings f+        bs = [ showTuple as ++ " -> " ++ r+             | (as, Just r) <- bs' ]++isValue :: ShowFunction a => a -> Bool+isValue f = case bindings f of+              [([],_)] -> True+              _        -> False++showValueOf :: ShowFunction a => a -> String+showValueOf x = case snd . head . bindings $ x of+                  Nothing -> "undefined"+                  Just x' -> x'++-- | Given a number of patterns to show, shows a 'ShowFunction' value.+--+-- > showFunction undefined True == "True"+-- > showFunction 3 (id::Int) == "\\x -> case x of\n\+-- >                              \        0 -> 0\n\+-- >                              \        1 -> 1\n\+-- >                              \        -1 -> -1\n\+-- >                              \        ...\n"+-- > showFunction 4 (&&) == "\\x y -> case (x,y) of\n\+-- >                         \          (False,False) -> False\n\+-- >                         \          (False,True) -> False\n\+-- >                         \          (True,False) -> False\n\+-- >                         \          (True,True) -> True\n"+--+-- This can be used as an implementation of show for functions:+--+-- > instance (Show a, Listable a, ShowFunction b) => Show (a->b) where+-- >   show = showFunction 8+showFunction :: ShowFunction a => Int -> a -> String+showFunction n = showFunctionL False (n*n+1) n++-- | Same as showFunction, but has no line breaks.+--+-- > showFunction 2 (id::Int) == "\\x -> case x of 0 -> 0; 1 -> 1; ..."+showFunctionLine :: ShowFunction a => Int -> a -> String+showFunctionLine n = showFunctionL True (n*n+1) n++-- | isUndefined checks if a function is totally undefined.+-- When it is not possible to check all values, it returns false+isUndefined :: ShowFunction a => Int -> a -> Bool+isUndefined m f = length bs < m && all (isNothing . snd) bs+  where bs = take m $ bindings f++-- The first boolean parameter tells if we are showing+-- the function on a single line+showFunctionL :: ShowFunction a => Bool -> Int -> Int -> a -> String+showFunctionL singleLine m n f | isValue f = showValueOf f+showFunctionL singleLine m n f | otherwise = lambdaPat ++ caseExp+  where+    vs = varnamesFor f+    lambdaPat = "\\" ++ unwords vs ++ " -> "+    casePat = "case " ++ showTuple vs ++ " of"+    bs = showNBindingsOf m n f+    sep | singleLine = " "+        | otherwise = "\n"+    cases | singleLine = intercalate "; " bs+          | otherwise  = unlines+                       $ (replicate (length lambdaPat + 2) ' ' ++) `map` bs+    caseExp = if isUndefined m f+                then "undefined"+                else casePat ++ sep ++ cases++-- instances for further tuples --+instance (Show a, Show b, Show c)+      => ShowFunction (a,b,c) where tBindings = tBindingsShow+instance (Show a, Show b, Show c, Show d)+      => ShowFunction (a,b,c,d) where tBindings = tBindingsShow+instance (Show a, Show b, Show c, Show d, Show e)+      => ShowFunction (a,b,c,d,e) where tBindings = tBindingsShow+instance (Show a, Show b, Show c, Show d, Show e, Show f)+      => ShowFunction (a,b,c,d,e,f) where tBindings = tBindingsShow+instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g)+      => ShowFunction (a,b,c,d,e,f,g) where tBindings = tBindingsShow+instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h)+      => ShowFunction (a,b,c,d,e,f,g,h) where tBindings = tBindingsShow
+ Test/Check/Utils.hs view
@@ -0,0 +1,232 @@+-- | Utilities functions for manipulating tiers (sized lists of lists)+module Test.Check.Utils+  (+  -- * Additional tiers constructors+    consFromList+  , consFromAscendingList+  , consFromStrictlyAscendingList+  , consFromSet+  , consFromNoDupList++  -- * Products of tiers+  , product3With+  , productMaybeWith++  -- * Tiers of lists+  , listsOf+  , ascendingListsOf+  , strictlyAscendingListsOf+  , setsOf+  , noDupListsOf+  , products+  , listsOfLength++  , deleteT+  , normalizeT++  -- * Tiers of choices+  , choices+  , ascendingChoices+  , strictlyAscendingChoices+  )+where++import Test.Check.Basic+import Data.Maybe (catMaybes)++-- | Given a constructor for a type that takes a list,+--   return tiers for that type.+consFromList :: Listable a => ([a] -> b) -> [[b]]+consFromList = (`mapT` listsOf tiers)++consFromAscendingList :: Listable a => ([a] -> b) -> [[b]]+consFromAscendingList = (`mapT` ascendingListsOf tiers)++-- | Given a constructor for a type that takes a list with strictly ascending+--   elements, return tiers of that type (e.g.: a Set type).+consFromStrictlyAscendingList :: Listable a => ([a] -> b) -> [[b]]+consFromStrictlyAscendingList = (`mapT` strictlyAscendingListsOf tiers)++-- | Given a constructor for a type that takes a set of elements (as a list)+--   return tiers of that type (e.g.: a Set type).+consFromSet :: Listable a => ([a] -> b) -> [[b]]+consFromSet = (`mapT` setsOf tiers)++-- | Given a constructor for a type that takes a list with no duplicate+--   elements, return tiers of that type.+consFromNoDupList :: Listable a => ([a] -> b) -> [[b]]+consFromNoDupList f = mapT f (noDupListsOf tiers)+++-- | Like 'product', but over 3 lists of tiers.+product3With :: (a->b->c->d) -> [[a]] -> [[b]] -> [[c]] -> [[d]]+product3With f xss yss zss = productWith ($) (productWith f xss yss) zss++-- | Take the product of lists of tiers by a function returning a maybe value.+productMaybeWith :: (a->b->Maybe c) -> [[a]] -> [[b]] -> [[c]]+productMaybeWith _ _ [] = []+productMaybeWith _ [] _ = []+productMaybeWith f (xs:xss) yss = map (xs **) yss+                               \/ productMaybeWith f xss yss `addWeight` 1+  where xs ** ys = catMaybes [ f x y | x <- xs, y <- ys ]+++-- | Given tiers of values, returns tiers of lists of those values+--+-- > listsOf [[]] == [[[]]]+--+-- > listsOf [[x]] == [ [[]]+-- >                  , [[x]]+-- >                  , [[x,x]]+-- >                  , [[x,x,x]]+-- >                  , ...+-- >                  ]+--+-- > listsOf [[x],[y]] == [ [[]]+-- >                      , [[x]]+-- >                      , [[x,x],[y]]+-- >                      , [[x,x,x],[x,y],[y,x]]+-- >                      , ...+-- >                      ]+listsOf :: [[a]] -> [[[a]]]+listsOf xss = cons0 []+           \/ productWith (:) xss (listsOf xss) `addWeight` 1++-- | Generates several lists of the same size.+--+-- > products [ xss, yss, zss ] ==+--+-- Tiers of all lists combining elements of tiers: xss, yss and zss+products :: [ [[a]] ] -> [[ [a] ]]+products = foldr (productWith (:)) [[[]]]++-- | Delete the first occurence of an element in a tier,+--   for tiers without repetitions:+--+-- > deleteT x === normalizeT . (`suchThat` (/= x))+deleteT :: Eq a => a -> [[a]] -> [[a]]+deleteT _ [] = []+deleteT y ([]:xss) = [] : deleteT y xss+deleteT y [[x]]        | x == y    = []+deleteT y ((x:xs):xss) | x == y    = xs:xss+                       | otherwise = [[x]] \/ deleteT y (xs:xss)++normalizeT :: [[a]] -> [[a]]+normalizeT [] = []+normalizeT [[]] = []+normalizeT (xs:xss) = xs:normalizeT xss++-- | Given tiers of values, returns tiers of lists with no repeated elements.+--+-- > noDupListsOf [[0],[1],[2],...] ==+-- >   [ [[]]+-- >   , [[0]]+-- >   , [[1]]+-- >   , [[0,1],[1,0],[2]]+-- >   , [[0,2],[2,0],[3]]+-- >   , ...+-- >   ]+noDupListsOf :: [[a]] -> [[[a]]]+noDupListsOf =+  ([[]]:) . concatT . choicesWith (\x xss -> mapT (x:) (noDupListsOf xss))++-- | Lists tiers of all choices of values from tiers.+-- Choices are pairs of values and tiers excluding that value.+--+-- > choices [[False,True]] == [[(False,[[True]]),(True,[[False]])]]+-- > choices [[1],[2],[3]]+-- >   == [ [(1,[[],[2],[3]])]+-- >      , [(2,[[1],[],[3]])]+-- >      , [(3,[[1],[2],[]])] ]+--+-- Each choice is sized by the extracted element.+choices :: [[a]] -> [[(a,[[a]])]]+choices = choicesWith (,)++-- | Like 'choices', but allows a custom function.+choicesWith :: (a -> [[a]] -> b) -> [[a]] -> [[b]]+choicesWith f []           = []+choicesWith f [[]]         = []+choicesWith f ([]:xss)     = [] : choicesWith (\y yss -> f y ([]:yss)) xss+choicesWith f ((x:xs):xss) = [[f x (xs:xss)]]+                          \/ choicesWith (\y (ys:yss) -> f y ((x:ys):yss)) (xs:xss)++-- | Given tiers of values,+--   returns tiers of lists of elements in ascending order+--                               (from tiered enumeration).+--+ascendingListsOf :: [[a]] -> [[[a]]]+ascendingListsOf =+  ([[]]:) . concatT . ascendingChoicesWith (\x xss -> mapT (x:) (ascendingListsOf xss))++-- > ascendingChoices [[False,True]] =+-- >   [ [(False,[[False,True]]), (True,[[True]])]+-- >   ]+--+-- > ascendingChoices [[1],[2],[3],...] =+-- >   [ [(1,[[1],[2],[3],...])]+-- >   , [(2,[[ ],[2],[3],...])]+-- >   , [(3,[[ ],[ ],[3],...])]+-- >   , ...+-- >   ]+ascendingChoices :: [[a]] -> [[(a,[[a]])]]+ascendingChoices = ascendingChoicesWith (,)++ascendingChoicesWith :: (a -> [[a]] -> b) -> [[a]] -> [[b]]+ascendingChoicesWith f []           = []+ascendingChoicesWith f [[]]         = []+ascendingChoicesWith f ([]:xss)     = [] : ascendingChoicesWith (\y yss -> f y ([]:yss)) xss+ascendingChoicesWith f ((x:xs):xss) = [[f x ((x:xs):xss)]]+                                   \/ ascendingChoicesWith f (xs:xss)++-- | Given tiers of values,+--   returns tiers of lists of elements in strictly ascending order+--                              (from tiered enumeration).+--   If you only care about whether elements are in returned lists,+--   this returns the tiers of all sets of values.+--+-- > strictlyAscendingListsOf [[0],[1],[2],...] ==+-- >   [ [[]]+-- >   , [[0]]+-- >   , [[1]]+-- >   , [[0,1],[2]]+-- >   , [[0,2],[3]]+-- >   , [[0,3],[1,2],[4]]+-- >   , [[0,1,2],[0,4],[1,3],[5]]+-- >   , ...+-- >   ]+strictlyAscendingListsOf :: [[a]] -> [[[a]]]+strictlyAscendingListsOf =+  ([[]]:) . concatT .+  strictlyAscendingChoicesWith+    (\x xss -> mapT (x:) (strictlyAscendingListsOf xss))++-- | Returns tiers of sets represented as lists of values (no repeated sets).+--   Shorthand for 'strictlyAscendingListsOf'.+setsOf :: [[a]] -> [[[a]]]+setsOf = strictlyAscendingListsOf++-- | Like 'choices', but paired tiers are always strictly ascending (in terms+--   of enumeration).+--+-- > strictlyAscendingChoices [[False,True]] == [[(False,[[True]]),(True,[[]])]]+-- > strictlyAscendingChoices [[1],[2],[3]]+-- >   == [ [(1,[[],[2],[3]])]+-- >      , [(2,[[],[],[3]])]+-- >      , [(3,[[],[],[]])]+-- >      ]+strictlyAscendingChoices :: [[a]] -> [[(a,[[a]])]]+strictlyAscendingChoices = strictlyAscendingChoicesWith (,)++-- | Like 'strictlyAscendingChoices' but customized by a function.+strictlyAscendingChoicesWith :: (a -> [[a]] -> b) -> [[a]] -> [[b]]+strictlyAscendingChoicesWith f []           = []+strictlyAscendingChoicesWith f [[]]         = []+strictlyAscendingChoicesWith f ([]:xss)     = [] : strictlyAscendingChoicesWith (\y yss -> f y ([]:yss)) xss+strictlyAscendingChoicesWith f ((x:xs):xss) = [[f x (xs:xss)]]+                                           \/ strictlyAscendingChoicesWith f (xs:xss)+++-- | Given tiers, returns tiers of lists of a given length.+listsOfLength :: Int -> [[a]] -> [[[a]]]+listsOfLength n xss = products (replicate n xss)
+ Test/Most.hs view
@@ -0,0 +1,21 @@+-- | Simple property-based testing library+--   based on enumeration of values via lists of lists.+--+-- This module exports Most modules that accompain Test.Check+-- and is to be used as a shorthand:+--+-- > import Test.Most+--+-- To get most of the needed stuff+module Test.Most+  ( module Test.Check+  , module Test.Operators+  , module Test.TypeBinding+  , module Test.Types+  )+where++import Test.Check+import Test.Operators+import Test.TypeBinding+import Test.Types
+ Test/Operators.hs view
@@ -0,0 +1,113 @@+module Test.Operators+  (+--  (==>) -- already provided by Test.Check++  -- * Combining properties+    (===), (====)+  , (&&&), (&&&&)+  , (|||), (||||)++  -- * Properties over functions+  , commutative+  , associative+  , distributive+  , transitive+  , idempotent+  , identity+  , notIdentity++  -- * Ternary comparison operators+  , (=$), ($=)+  , (=|), (|=)+  )+where++import Test.Check ((==>))++combine :: (b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d)+combine op f g = \x -> f x `op` g x++-- Uneeded, just food for thought+--combine2 :: (c -> d -> e) -> (a -> b -> c) -> (a -> b -> d) -> (a -> b -> e)+-- Two possible implementations:+--combine2 op f g = \x y -> f x y `op` g x y+--combine2 = combine . combine++(===) :: Eq b => (a -> b) -> (a -> b) -> a -> Bool+(===) = combine (==)+infix 4 ===++(====) :: Eq c => (a -> b -> c) -> (a -> b -> c) -> a -> b -> Bool+(====) = combine (===)+infix 4 ====++(&&&) :: (a -> Bool) -> (a -> Bool) -> a -> Bool+(&&&) = combine (&&)+infix 3 &&&++(&&&&) :: (a -> b -> Bool) -> (a -> b -> Bool) -> a -> b -> Bool+(&&&&) = combine (&&&)+infix 3 &&&&++(|||) :: (a -> Bool) -> (a -> Bool) -> a -> Bool+(|||) = combine (||)+infix 2 |||++(||||) :: (a -> b -> Bool) -> (a -> b -> Bool) -> a -> b -> Bool+(||||) = combine (|||)+infix 2 ||||++commutative :: Eq b => (a -> a -> b) -> a -> a -> Bool+commutative o = \x y -> x `o` y == y `o` x++associative :: Eq a => (a -> a -> a) -> a -> a -> a -> Bool+associative o = \x y z -> x `o` (y `o` z) == (x `o` y) `o` z++-- type could be more general: (b -> a -> a) for both operators+distributive :: Eq a => (a -> a -> a) -> (a -> a -> a) -> a -> a -> a -> Bool+distributive o o' = \x y z -> x `o` (y `o'` z) == (x `o` y) `o'` (x `o` z)++transitive :: (a -> a -> Bool) -> a -> a -> a -> Bool+transitive o = \x y z -> x `o` y && y `o` z ==> x `o` z++idempotent :: Eq a => (a -> a) -> a -> Bool+idempotent f = f . f === f++identity :: Eq a => (a -> a) -> a -> Bool+identity f = f === id++notIdentity :: Eq a => (a -> a) -> a -> Bool+notIdentity = (not .) . identity++-- | Equal under.  A ternary operator.+--+-- > x =$ f $= y  =  f x = f y+--+-- > [1,2,3,4,5] =$  take 2    $= [1,2,4,8,16] -- > True+-- > [1,2,3,4,5] =$  take 3    $= [1,2,4,8,16] -- > False+-- >     [1,2,3] =$    sort    $= [3,2,1]      -- > True+-- >          42 =$ (`mod` 10) $= 16842        -- > True+-- >          42 =$ (`mod`  9) $= 16842        -- > False+-- >         'a' =$  isLetter  $= 'b'          -- > True+-- >         'a' =$  isLetter  $= '1'          -- > False+(=$) :: Eq b => a -> (a -> b) -> a -> Bool+(x =$ f) y = f x == f y+infixl 4 =$++($=) :: (a -> Bool) -> a -> Bool+($=) = ($)+infixl 4 $=++-- | Check if two lists are equal for @n@ values.+--+-- > xs =| n |= ys  =  take n xs == take n ys+--+-- > [1,2,3,4,5] =| 2 |= [1,2,4,8,16] -- > True+-- > [1,2,3,4,5] =| 3 |= [1,2,4,8,16] -- > False+(=|) :: Eq a => [a] -> Int -> [a] -> Bool+xs =| n = xs =$ take n+infixl 4 =|++(|=) :: (a -> Bool) -> a -> Bool+(|=) = ($)+infixl 4 |=
+ Test/TypeBinding.hs view
@@ -0,0 +1,229 @@+-- | Infix operators for type binding using dummy first-class values.+--+-- Those are useful when property based testing to avoid repetition.+-- Suppose:+--+-- > prop_sortAppend :: Ord a => [a] -> Bool+-- > prop_sortAppend xs =  sort (xs++ys) == sort (ys++xs)+--+-- Then this:+--+-- > testResults n =+-- >   [ holds n (prop_sortAppend :: [Int] -> [Int] -> Bool)+-- >   , holds n (prop_sortAppend :: [UInt2] -> [UInt2] -> Bool)+-- >   , holds n (prop_sortAppend :: [Bool] -> [Bool] -> Bool)+-- >   , holds n (prop_sortAppend :: [Char] -> [Char] -> Bool)+-- >   , holds n (prop_sortAppend :: [String] -> [String] -> Bool)+-- >   , holds n (prop_sortAppend :: [()] -> [()] -> Bool)+-- >   ]+--+-- Becomes this:+--+-- > testResults n =+-- >   [ holds n $ prop_sortAppend -:> [int]+-- >   , holds n $ prop_sortAppend -:> [uint2]+-- >   , holds n $ prop_sortAppend -:> [bool]+-- >   , holds n $ prop_sortAppend -:> [char]+-- >   , holds n $ prop_sortAppend -:> [string]+-- >   , holds n $ prop_sortAppend -:> [()]+-- >   ]+--+-- Or even:+--+-- > testResults n = concat+-- >   [ for int, for uint2, for bool, for (), for char, for string ]+-- >   where for a = [ holds n $ prop_sortAppend -:> a ]+--+-- This last form is useful when testing multiple properties for multiple+-- types.+module Test.TypeBinding+  (+  -- * Type binding operators+  --+  -- | Summary:+  --+  -- *                 as type of: '-:'+  -- *        argument as type of: '-:>'+  -- *          result as type of: '->:'+  -- * second argument as type of: '->:>'+  -- * second  result  as type of: '->>:'+  -- * third  argument as type of: '->>:>'+  -- * third   result  as type of: '->>>:'+    (-:)+  , (-:>)+  , (->:)+  , (->:>)+  , (->>:)+  , (->>:>)+  , (->>>:)++  -- * Dummy (undefined) values+  -- ** Standard Haskell types+  , und+  , (>-)+  , bool+  , int, integer+  , float, double+  , char, string+  , mayb, eith+  -- ** Testing types+  , nat+  , int1, uint1+  , int2, uint2+  , int3, uint3+  , int4, uint4+  )+where++import Test.Types++undefinedOf :: String -> a+undefinedOf fn = error $ "Test.TypeBinding." ++ fn++-- | Type restricted version of const+-- that forces its first argument+-- to have the same type as the second.+-- A symnonym to 'asTypeOf':+--+-- >  value -: ty  =  value :: Ty+--+-- Examples:+--+-- >  10 -: int   =  10 :: Int+-- >  undefined -: 'a' >- 'b'  =  undefined :: Char -> Char+(-:) :: a -> a -> a+(-:) = asTypeOf -- const+infixl 1 -:++-- | Type restricted version of const+-- that forces the argument of its first argument+-- to have the same type as the second:+--+-- >  f -:> ty  =  f -: ty >- und  =  f :: Ty -> a+--+-- Example:+--+-- >  abs -:> int   =  abs -: int >- und  =  abs :: Int -> Int+(-:>) :: (a -> b) -> a -> (a -> b)+(-:>) = const+infixl 1 -:>++-- | Type restricted version of const+-- that forces the result of its first argument+-- to have the same type as the second.+--+-- >  f ->: ty  =  f -: und >- ty  =  f :: a -> Ty+(->:) :: (a -> b) -> b -> (a -> b)+(->:) = const+infixl 1 ->:++-- | Type restricted version of const+-- that forces the second argument of its first argument+-- to have the same type as the second.+--+-- > f ->:> ty   =  f -: und -> ty -> und  =  f :: a -> Ty -> b+(->:>) :: (a -> b -> c) -> b -> (a -> b -> c)+(->:>) = const+infixl 1 ->:>++-- | Type restricted version of const+-- that forces the result of the result of its first argument+-- to have the same type as the second.+--+-- > f ->>: ty   =  f -: und -> und -> ty  =  f :: a -> b -> Ty+(->>:) :: (a -> b -> c) -> c -> (a -> b -> c)+(->>:) = const+infixl 1 ->>:++-- | Type restricted version of const+-- that forces the third argument of its first argument+-- to have the same type as the second.+(->>:>) :: (a -> b -> c -> d) -> c -> (a -> b -> c -> d)+(->>:>) = const+infixl 1 ->>:>++-- | Type restricted version of const+-- that forces the result of the result of the result of its first argument+-- to have the same type as the second.+(->>>:) :: (a -> b -> c -> d) -> d -> (a -> b -> c -> d)+(->>>:) = const+infixl 1 ->>>:++-- | Returns an undefined functional value+-- that takes an argument of the type of its first argument+-- and return a value of the type of its second argument.+--+-- > ty >- ty  =  (undefined :: Ty -> Ty)+--+-- Examples:+--+-- > 'a' >- 'b'  =  char >- char  =  (undefined :: Char -> Char)+-- > int >- bool >- int  =  undefined :: Int -> Bool -> Int+(>-) :: a -> b -> (a -> b)+(>-) = undefinedOf "(>-): undefined function -- using dummy value?"+infixr 9 >-+++-- Dummy values of standard Haskell types++-- | Shorthand for undefined+und :: a+und = undefinedOf "und"++int :: Int+int = undefinedOf "int"++integer :: Integer+integer = undefinedOf "integer"++float :: Float+float = undefinedOf "float"++double :: Double+double = undefinedOf "double"++bool :: Bool+bool = undefinedOf "bool"++char :: Char+char = undefinedOf "char"++string :: String+string = undefinedOf "string"++-- | It might be better to just use 'Just'+mayb :: a -> Maybe a+mayb = undefinedOf "mayb"++eith :: a -> b -> Either a b+eith = undefinedOf "eith"+++-- Dummy values of Test.Types's types:++nat :: Nat+nat = undefinedOf "nat"++int1 :: Int1+int1 = undefinedOf "int1"++int2 :: Int2+int2 = undefinedOf "int2"++int3 :: Int3+int3 = undefinedOf "int3"++int4 :: Int4+int4 = undefinedOf "int4"++uint1 :: UInt1+uint1 = undefinedOf "uint1"++uint2 :: UInt2+uint2 = undefinedOf "uint2"++uint3 :: UInt3+uint3 = undefinedOf "uint3"++uint4 :: UInt4+uint4 = undefinedOf "uint4"
+ Test/Types.hs view
@@ -0,0 +1,438 @@+-- | Types to aid in property-based testing.+module Test.Types+  (+  -- * Integer types+  --+  -- | Small-width integer types to aid in property-based testing.+  -- Sometimes it is useful to limit the possibilities of enumerated values+  -- when testing polymorphic functions, these types allow that.+  --+  -- The signed integer types @IntN@ are of limited bit width @N@+  -- bounded by @-2^(N-1)@ to @2^(N-1)-1@.+  -- The unsigned integer types @WordN@ are of limited bit width @N@+  -- bounded by @0@ to @2^N-1@.+  --+  -- Operations are closed and modulo @2^N@.  e.g.:+  --+  -- > maxBound + 1      = minBound+  -- > read "2"          = -2 :: Int2+  -- > abs minBound      = minBound+  -- > negate n          = 2^N - n :: WordN+    Int1+  , Int2+  , Int3+  , Int4+  , Word1+  , Word2+  , Word3+  , Word4+  , Nat+  , Nat1+  , Nat2+  , Nat3+  , Nat4+  , Nat5+  , Nat6+  , Nat7++  -- * Aliases to word types (deprecated)+  , UInt1+  , UInt2+  , UInt3+  , UInt4+  )+where+-- TODO: Add Ix and Bits instances++import Test.Check (Listable(..), listIntegral)+import Data.Ratio ((%))++narrowU :: Int -> Int -> Int+narrowU w i = i `mod` 2^w++narrowS :: Int -> Int -> Int+narrowS w i = let l  = 2^w+                  i' = i `mod` l+              in if i' < 2^(w-1)+                   then i'+                   else i' - l++mapTuple :: (a -> b) -> (a,a) -> (b,b)+mapTuple f (x,y) = (f x, f y)++mapFst :: (a -> b) -> (a,c) -> (b,c)+mapFst f (x,y) = (f x,y)++oNewtype :: (a -> b) -> (b -> a) -> (a -> a -> a) -> (b -> b -> b)+oNewtype con des o = \x y -> con $ des x `o` des y++fNewtype :: (a -> b) -> (b -> a) -> (a -> a) -> (b -> b)+fNewtype con des f = con . f . des++otNewtype :: (a -> b) -> (b -> a) -> (a -> a -> (a,a)) -> (b -> b -> (b,b))+otNewtype con des o = \x y -> mapTuple con $ des x `o` des y++readsPrecNewtype :: Read a => (a -> b) -> Int -> String -> [(b,String)]+readsPrecNewtype con n = map (mapFst con) . readsPrec n++boundedEnumFrom :: (Ord a,Bounded a,Enum a) => a -> [a]+boundedEnumFrom x = [x..maxBound]++boundedEnumFromThen :: (Ord a,Bounded a,Enum a) => a -> a -> [a]+boundedEnumFromThen x y | x > y     = [x,y..minBound]+                        | otherwise = [x,y..maxBound]++-- | Single-bit signed integers: -1, 0+newtype Int1 = Int1 { unInt1 :: Int } deriving (Eq, Ord)++-- | Two-bit signed integers: -2, -1, 0, 1+newtype Int2 = Int2 { unInt2 :: Int } deriving (Eq, Ord)++-- | Three-bit signed integers: -4, -3, -2, -1, 0, 1, 2, 3+newtype Int3 = Int3 { unInt3 :: Int } deriving (Eq, Ord)++-- | Four-bit signed integers:+-- -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7+newtype Int4 = Int4 { unInt4 :: Int } deriving (Eq, Ord)++-- | Single-bit unsigned integer: 0, 1+newtype Word1 = Word1 { unWord1 :: Int } deriving (Eq, Ord)++-- | Two-bit unsigned integers: 0, 1, 2, 3+newtype Word2 = Word2 { unWord2 :: Int } deriving (Eq, Ord)++-- | Three-bit unsigned integers: 0, 1, 2, 3, 4, 5, 6, 7+newtype Word3 = Word3 { unWord3 :: Int } deriving (Eq, Ord)++-- | Four-bit unsigned integers:+-- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15+newtype Word4 = Word4 { unWord4 :: Int } deriving (Eq, Ord)++-- | Natural numbers (including 0): 0, 1, 2, 3, 4, 5, 6, 7, ...+--+-- Internally, this type is represented as an 'Int'.+-- So, it is limited by the 'maxBound' of 'Int'.+newtype Nat = Nat { unNat :: Int } deriving (Eq, Ord)++-- | Natural numbers modulo 1: 0+newtype Nat1 = Nat1 { unNat1 :: Int } deriving (Eq, Ord)++-- | Natural numbers modulo 2: 0, 1+newtype Nat2 = Nat2 { unNat2 :: Int } deriving (Eq, Ord)++-- | Natural numbers modulo 3: 0, 1, 2+newtype Nat3 = Nat3 { unNat3 :: Int } deriving (Eq, Ord)++-- | Natural numbers modulo 4: 0, 1, 2, 3+newtype Nat4 = Nat4 { unNat4 :: Int } deriving (Eq, Ord)++-- | Natural numbers modulo 5: 0, 1, 2, 3, 4+newtype Nat5 = Nat5 { unNat5 :: Int } deriving (Eq, Ord)++-- | Natural numbers modulo 6: 0, 1, 2, 3, 4, 5+newtype Nat6 = Nat6 { unNat6 :: Int } deriving (Eq, Ord)++-- | Natural numbers modulo 7: 0, 1, 2, 3, 4, 5, 6+newtype Nat7 = Nat7 { unNat7 :: Int } deriving (Eq, Ord)++int1  :: Int -> Int1;   int1  = Int1  . narrowS 1+int2  :: Int -> Int2;   int2  = Int2  . narrowS 2+int3  :: Int -> Int3;   int3  = Int3  . narrowS 3+int4  :: Int -> Int4;   int4  = Int4  . narrowS 4+word1 :: Int -> Word1;  word1 = Word1 . narrowU 1+word2 :: Int -> Word2;  word2 = Word2 . narrowU 2+word3 :: Int -> Word3;  word3 = Word3 . narrowU 3+word4 :: Int -> Word4;  word4 = Word4 . narrowU 4+nat1 :: Int -> Nat1;  nat1 = Nat1 . (`mod` 1)+nat2 :: Int -> Nat2;  nat2 = Nat2 . (`mod` 2)+nat3 :: Int -> Nat3;  nat3 = Nat3 . (`mod` 3)+nat4 :: Int -> Nat4;  nat4 = Nat4 . (`mod` 4)+nat5 :: Int -> Nat5;  nat5 = Nat5 . (`mod` 5)+nat6 :: Int -> Nat6;  nat6 = Nat6 . (`mod` 6)+nat7 :: Int -> Nat7;  nat7 = Nat7 . (`mod` 7)++oInt1  ::(Int->Int->Int)->(Int1->Int1->Int1)   ; oInt1  = oNewtype int1  unInt1+oInt2  ::(Int->Int->Int)->(Int2->Int2->Int2)   ; oInt2  = oNewtype int2  unInt2+oInt3  ::(Int->Int->Int)->(Int3->Int3->Int3)   ; oInt3  = oNewtype int3  unInt3+oInt4  ::(Int->Int->Int)->(Int4->Int4->Int4)   ; oInt4  = oNewtype int4  unInt4+oWord1 ::(Int->Int->Int)->(Word1->Word1->Word1); oWord1 = oNewtype word1 unWord1+oWord2 ::(Int->Int->Int)->(Word2->Word2->Word2); oWord2 = oNewtype word2 unWord2+oWord3 ::(Int->Int->Int)->(Word3->Word3->Word3); oWord3 = oNewtype word3 unWord3+oWord4 ::(Int->Int->Int)->(Word4->Word4->Word4); oWord4 = oNewtype word4 unWord4+oNat   ::(Int->Int->Int)->(Nat->Nat->Nat)      ; oNat   = oNewtype Nat   unNat+oNat1  ::(Int->Int->Int)->(Nat1->Nat1->Nat1)   ; oNat1  = oNewtype nat1  unNat1+oNat2  ::(Int->Int->Int)->(Nat2->Nat2->Nat2)   ; oNat2  = oNewtype nat2  unNat2+oNat3  ::(Int->Int->Int)->(Nat3->Nat3->Nat3)   ; oNat3  = oNewtype nat3  unNat3+oNat4  ::(Int->Int->Int)->(Nat4->Nat4->Nat4)   ; oNat4  = oNewtype nat4  unNat4+oNat5  ::(Int->Int->Int)->(Nat5->Nat5->Nat5)   ; oNat5  = oNewtype nat5  unNat5+oNat6  ::(Int->Int->Int)->(Nat6->Nat6->Nat6)   ; oNat6  = oNewtype nat6  unNat6+oNat7  ::(Int->Int->Int)->(Nat7->Nat7->Nat7)   ; oNat7  = oNewtype nat7  unNat7++fInt1  :: (Int->Int) -> (Int1->Int1)   ; fInt1  = fNewtype int1  unInt1+fInt2  :: (Int->Int) -> (Int2->Int2)   ; fInt2  = fNewtype int2  unInt2+fInt3  :: (Int->Int) -> (Int3->Int3)   ; fInt3  = fNewtype int3  unInt3+fInt4  :: (Int->Int) -> (Int4->Int4)   ; fInt4  = fNewtype int4  unInt4+fWord1 :: (Int->Int) -> (Word1->Word1) ; fWord1 = fNewtype word1 unWord1+fWord2 :: (Int->Int) -> (Word2->Word2) ; fWord2 = fNewtype word2 unWord2+fWord3 :: (Int->Int) -> (Word3->Word3) ; fWord3 = fNewtype word3 unWord3+fWord4 :: (Int->Int) -> (Word4->Word4) ; fWord4 = fNewtype word4 unWord4+fNat   :: (Int->Int) -> (Nat->Nat)     ; fNat   = fNewtype Nat   unNat+fNat1  :: (Int->Int) -> (Nat1->Nat1)   ; fNat1  = fNewtype nat1  unNat1+fNat2  :: (Int->Int) -> (Nat2->Nat2)   ; fNat2  = fNewtype nat2  unNat2+fNat3  :: (Int->Int) -> (Nat3->Nat3)   ; fNat3  = fNewtype nat3  unNat3+fNat4  :: (Int->Int) -> (Nat4->Nat4)   ; fNat4  = fNewtype nat4  unNat4+fNat5  :: (Int->Int) -> (Nat5->Nat5)   ; fNat5  = fNewtype nat5  unNat5+fNat6  :: (Int->Int) -> (Nat6->Nat6)   ; fNat6  = fNewtype nat6  unNat6+fNat7  :: (Int->Int) -> (Nat7->Nat7)   ; fNat7  = fNewtype nat7  unNat7++instance Show Int1 where show = show . unInt1+instance Show Int2 where show = show . unInt2+instance Show Int3 where show = show . unInt3+instance Show Int4 where show = show . unInt4+instance Show Word1 where show = show . unWord1+instance Show Word2 where show = show . unWord2+instance Show Word3 where show = show . unWord3+instance Show Word4 where show = show . unWord4+instance Show Nat where show (Nat x) = show x+instance Show Nat1 where show = show . unNat1+instance Show Nat2 where show = show . unNat2+instance Show Nat3 where show = show . unNat3+instance Show Nat4 where show = show . unNat4+instance Show Nat5 where show = show . unNat5+instance Show Nat6 where show = show . unNat6+instance Show Nat7 where show = show . unNat7++instance Read Int1 where readsPrec = readsPrecNewtype int1+instance Read Int2 where readsPrec = readsPrecNewtype int2+instance Read Int3 where readsPrec = readsPrecNewtype int3+instance Read Int4 where readsPrec = readsPrecNewtype int4+instance Read Word1 where readsPrec = readsPrecNewtype word1+instance Read Word2 where readsPrec = readsPrecNewtype word2+instance Read Word3 where readsPrec = readsPrecNewtype word3+instance Read Word4 where readsPrec = readsPrecNewtype word4+instance Read Nat where readsPrec = readsPrecNewtype Nat+instance Read Nat1 where readsPrec = readsPrecNewtype nat1+instance Read Nat2 where readsPrec = readsPrecNewtype nat2+instance Read Nat3 where readsPrec = readsPrecNewtype nat3+instance Read Nat4 where readsPrec = readsPrecNewtype nat4+instance Read Nat5 where readsPrec = readsPrecNewtype nat5+instance Read Nat6 where readsPrec = readsPrecNewtype nat6+instance Read Nat7 where readsPrec = readsPrecNewtype nat7+++instance Num Int1 where (+) = oInt1 (+);  abs    = fInt1 abs+                        (-) = oInt1 (-);  signum = fInt1 signum+                        (*) = oInt1 (*);  fromInteger = int1 . fromInteger++instance Num Int2 where (+) = oInt2 (+);  abs    = fInt2 abs+                        (-) = oInt2 (-);  signum = fInt2 signum+                        (*) = oInt2 (*);  fromInteger = int2 . fromInteger++instance Num Int3 where (+) = oInt3 (+);  abs    = fInt3 abs+                        (-) = oInt3 (-);  signum = fInt3 signum+                        (*) = oInt3 (*);  fromInteger = int3 . fromInteger++instance Num Int4 where (+) = oInt4 (+);  abs    = fInt4 abs+                        (-) = oInt4 (-);  signum = fInt4 signum+                        (*) = oInt4 (*);  fromInteger = int4 . fromInteger++instance Num Word1 where (+) = oWord1 (+);  abs    = fWord1 abs+                         (-) = oWord1 (-);  signum = fWord1 signum+                         (*) = oWord1 (*);  fromInteger = word1 . fromInteger++instance Num Word2 where (+) = oWord2 (+);  abs    = fWord2 abs+                         (-) = oWord2 (-);  signum = fWord2 signum+                         (*) = oWord2 (*);  fromInteger = word2 . fromInteger++instance Num Word3 where (+) = oWord3 (+);  abs    = fWord3 abs+                         (-) = oWord3 (-);  signum = fWord3 signum+                         (*) = oWord3 (*);  fromInteger = word3 . fromInteger++instance Num Word4 where (+) = oWord4 (+);  abs    = fWord4 abs+                         (-) = oWord4 (-);  signum = fWord4 signum+                         (*) = oWord4 (*);  fromInteger = word4 . fromInteger++instance Num Nat where (+) = oNat (+);  abs    = fNat abs+                       (-) = oNat (-);  signum = fNat signum+                       (*) = oNat (*);  fromInteger = Nat . fromInteger++instance Num Nat1 where (+) = oNat1 (+);  abs    = fNat1 abs+                        (-) = oNat1 (-);  signum = fNat1 signum+                        (*) = oNat1 (*);  fromInteger = nat1 . fromInteger++instance Num Nat2 where (+) = oNat2 (+);  abs    = fNat2 abs+                        (-) = oNat2 (-);  signum = fNat2 signum+                        (*) = oNat2 (*);  fromInteger = nat2 . fromInteger++instance Num Nat3 where (+) = oNat3 (+);  abs    = fNat3 abs+                        (-) = oNat3 (-);  signum = fNat3 signum+                        (*) = oNat3 (*);  fromInteger = nat3 . fromInteger++instance Num Nat4 where (+) = oNat4 (+);  abs    = fNat4 abs+                        (-) = oNat4 (-);  signum = fNat4 signum+                        (*) = oNat4 (*);  fromInteger = nat4 . fromInteger++instance Num Nat5 where (+) = oNat5 (+);  abs    = fNat5 abs+                        (-) = oNat5 (-);  signum = fNat5 signum+                        (*) = oNat5 (*);  fromInteger = nat5 . fromInteger++instance Num Nat6 where (+) = oNat6 (+);  abs    = fNat6 abs+                        (-) = oNat6 (-);  signum = fNat6 signum+                        (*) = oNat6 (*);  fromInteger = nat6 . fromInteger++instance Num Nat7 where (+) = oNat7 (+);  abs    = fNat7 abs+                        (-) = oNat7 (-);  signum = fNat7 signum+                        (*) = oNat7 (*);  fromInteger = nat7 . fromInteger+++instance Real Int1 where toRational (Int1 x) = fromIntegral x % 1+instance Real Int2 where toRational (Int2 x) = fromIntegral x % 1+instance Real Int3 where toRational (Int3 x) = fromIntegral x % 1+instance Real Int4 where toRational (Int4 x) = fromIntegral x % 1+instance Real Word1 where toRational (Word1 x) = fromIntegral x % 1+instance Real Word2 where toRational (Word2 x) = fromIntegral x % 1+instance Real Word3 where toRational (Word3 x) = fromIntegral x % 1+instance Real Word4 where toRational (Word4 x) = fromIntegral x % 1+instance Real Nat where toRational (Nat x) = fromIntegral x % 1+instance Real Nat1 where toRational (Nat1 x) = fromIntegral x % 1+instance Real Nat2 where toRational (Nat2 x) = fromIntegral x % 1+instance Real Nat3 where toRational (Nat3 x) = fromIntegral x % 1+instance Real Nat4 where toRational (Nat4 x) = fromIntegral x % 1+instance Real Nat5 where toRational (Nat5 x) = fromIntegral x % 1+instance Real Nat6 where toRational (Nat6 x) = fromIntegral x % 1+instance Real Nat7 where toRational (Nat7 x) = fromIntegral x % 1++instance Integral Int1 where quotRem = otNewtype int1 unInt1 quotRem+                             toInteger = toInteger . unInt1++instance Integral Int2 where quotRem = otNewtype int2 unInt2 quotRem+                             toInteger = toInteger . unInt2++instance Integral Int3 where quotRem = otNewtype int3 unInt3 quotRem+                             toInteger = toInteger . unInt3++instance Integral Int4 where quotRem = otNewtype int4 unInt4 quotRem+                             toInteger = toInteger . unInt4++instance Integral Word1 where quotRem = otNewtype word1 unWord1 quotRem+                              toInteger = toInteger . unWord1++instance Integral Word2 where quotRem = otNewtype word2 unWord2 quotRem+                              toInteger = toInteger . unWord2++instance Integral Word3 where quotRem = otNewtype word3 unWord3 quotRem+                              toInteger = toInteger . unWord3++instance Integral Word4 where quotRem = otNewtype word4 unWord4 quotRem+                              toInteger = toInteger . unWord4++instance Integral Nat where quotRem = otNewtype Nat unNat quotRem+                            toInteger = toInteger . unNat++instance Integral Nat1 where quotRem = otNewtype nat1 unNat1 quotRem+                             toInteger = toInteger . unNat1++instance Integral Nat2 where quotRem = otNewtype nat2 unNat2 quotRem+                             toInteger = toInteger . unNat2++instance Integral Nat3 where quotRem = otNewtype nat3 unNat3 quotRem+                             toInteger = toInteger . unNat3++instance Integral Nat4 where quotRem = otNewtype nat4 unNat4 quotRem+                             toInteger = toInteger . unNat4++instance Integral Nat5 where quotRem = otNewtype nat5 unNat5 quotRem+                             toInteger = toInteger . unNat5++instance Integral Nat6 where quotRem = otNewtype nat6 unNat6 quotRem+                             toInteger = toInteger . unNat6++instance Integral Nat7 where quotRem = otNewtype nat7 unNat7 quotRem+                             toInteger = toInteger . unNat7++instance Bounded Int1 where maxBound = Int1 0; minBound = Int1 (-1)+instance Bounded Int2 where maxBound = Int2 1; minBound = Int2 (-2)+instance Bounded Int3 where maxBound = Int3 3; minBound = Int3 (-4)+instance Bounded Int4 where maxBound = Int4 7; minBound = Int4 (-8)+instance Bounded Word1 where maxBound = Word1 1; minBound = Word1 0+instance Bounded Word2 where maxBound = Word2 3; minBound = Word2 0+instance Bounded Word3 where maxBound = Word3 7; minBound = Word3 0+instance Bounded Word4 where maxBound = Word4 15; minBound = Word4 0+instance Bounded Nat1 where maxBound = Nat1 0; minBound = Nat1 0+instance Bounded Nat2 where maxBound = Nat2 1; minBound = Nat2 0+instance Bounded Nat3 where maxBound = Nat3 2; minBound = Nat3 0+instance Bounded Nat4 where maxBound = Nat4 3; minBound = Nat4 0+instance Bounded Nat5 where maxBound = Nat5 4; minBound = Nat5 0+instance Bounded Nat6 where maxBound = Nat6 5; minBound = Nat6 0+instance Bounded Nat7 where maxBound = Nat7 6; minBound = Nat7 0++instance Enum Int1 where toEnum   = int1;   enumFrom     = boundedEnumFrom+                         fromEnum = unInt1; enumFromThen = boundedEnumFromThen++instance Enum Int2 where toEnum   = int2;   enumFrom     = boundedEnumFrom+                         fromEnum = unInt2; enumFromThen = boundedEnumFromThen++instance Enum Int3 where toEnum   = int3;   enumFrom     = boundedEnumFrom+                         fromEnum = unInt3; enumFromThen = boundedEnumFromThen++instance Enum Int4 where toEnum   = int4;   enumFrom     = boundedEnumFrom+                         fromEnum = unInt4; enumFromThen = boundedEnumFromThen++instance Enum Word1 where toEnum   = word1;   enumFrom     = boundedEnumFrom+                          fromEnum = unWord1; enumFromThen = boundedEnumFromThen++instance Enum Word2 where toEnum   = word2;   enumFrom     = boundedEnumFrom+                          fromEnum = unWord2; enumFromThen = boundedEnumFromThen++instance Enum Word3 where toEnum   = word3;   enumFrom     = boundedEnumFrom+                          fromEnum = unWord3; enumFromThen = boundedEnumFromThen++instance Enum Word4 where toEnum   = word4;   enumFrom     = boundedEnumFrom+                          fromEnum = unWord4; enumFromThen = boundedEnumFromThen++instance Enum Nat where+  toEnum   = Nat;    enumFrom     (Nat x)         = map Nat [x..]+  fromEnum = unNat;  enumFromThen (Nat x) (Nat s) = map Nat [x,s..]++instance Enum Nat1 where toEnum   = nat1;   enumFrom     = boundedEnumFrom+                         fromEnum = unNat1; enumFromThen = boundedEnumFromThen++instance Enum Nat2 where toEnum   = nat2;   enumFrom     = boundedEnumFrom+                         fromEnum = unNat2; enumFromThen = boundedEnumFromThen++instance Enum Nat3 where toEnum   = nat3;   enumFrom     = boundedEnumFrom+                         fromEnum = unNat3; enumFromThen = boundedEnumFromThen++instance Enum Nat4 where toEnum   = nat4;   enumFrom     = boundedEnumFrom+                         fromEnum = unNat4; enumFromThen = boundedEnumFromThen++instance Enum Nat5 where toEnum   = nat5;   enumFrom     = boundedEnumFrom+                         fromEnum = unNat5; enumFromThen = boundedEnumFromThen++instance Enum Nat6 where toEnum   = nat6;   enumFrom     = boundedEnumFrom+                         fromEnum = unNat6; enumFromThen = boundedEnumFromThen++instance Enum Nat7 where toEnum   = nat7;   enumFrom     = boundedEnumFrom+                         fromEnum = unNat7; enumFromThen = boundedEnumFromThen++instance Listable Int1 where list = [0,minBound]+instance Listable Int2 where list = listIntegral+instance Listable Int3 where list = listIntegral+instance Listable Int4 where list = listIntegral+instance Listable Word1 where list = [0..]+instance Listable Word2 where list = [0..]+instance Listable Word3 where list = [0..]+instance Listable Word4 where list = [0..]+instance Listable Nat where list = [0..]+instance Listable Nat1 where list = [0..]+instance Listable Nat2 where list = [0..]+instance Listable Nat3 where list = [0..]+instance Listable Nat4 where list = [0..]+instance Listable Nat5 where list = [0..]+instance Listable Nat6 where list = [0..]+instance Listable Nat7 where list = [0..]++type UInt1 = Word1+type UInt2 = Word2+type UInt3 = Word3+type UInt4 = Word4
+ leancheck.cabal view
@@ -0,0 +1,121 @@+-- LeanCheck+--+-- Template Haskell dependency is optional.  To deactivate it:+-- 1. In this file, comment out:+--   Test.Check.Derive+--   template-haskell+--   and the test-suite derive+-- 2. On Test.Most, comment out the Test.Check.Derive module+-- 3. On Test.Check, comment out Test.Check.Derive and deriveListable+--+-- I could ultimately add a flag to deactivate that, but I do not want to make+-- this cabal file too complicated.  -- Rudy++name:                leancheck+version:             0.3.0+synopsis:            Cholesterol-free property-based testing+description:+  LeanCheck is a simple enumerative property-based testing library.+  .+  It works by producing *tiers* of test values,+  which are essentially (possibly infinite) lists+  of finite lists of same-and-increasingly-sized values.+  .+  LeanCheck has "lean" core with only 180 lines of Haskell code+  but provides a selection of utilitites for property testing:+  test types (@Nat@, @Nat\<1-7\>@, @Word\<1-4\>@, @Int\<1-4\>@);+  test operators (@==>@, @===@, @&&&@, @|||@);+  type binding operators.+  .+  LeanCheck API is likely to change in the near future.++homepage:            https://github.com/rudymatela/leancheck#readme+license:             BSD3+license-file:        LICENSE+author:              Rudy Matela <rudy@matela.com.br>+maintainer:          Rudy Matela <rudy@matela.com.br>+category:            Testing+build-type:          Simple+cabal-version:       >=1.10++extra-source-files:  README.md, CREDITS.md++source-repository head+  type:            git+  location:        https://github.com/rudymatela/leancheck++source-repository this+  type:            git+  location:        https://github.com/rudymatela/leancheck+  tag:             v0.3.0++library+  exposed-modules: Test.Check+                 , Test.Check.Utils+                 , Test.Check.Basic+                 , Test.Check.Core+                 , Test.Check.Derive+                 , Test.Check.Error+                 , Test.Check.IO+                 , Test.Types+                 , Test.Operators+                 , Test.TypeBinding+                 , Test.Most+                 , Test.Check.Function+                 , Test.Check.Function.ListsOfPairs+                 , Test.Check.Function.CoListable+                 , Test.Check.Function.Periodic+                 , Test.Check.Function.Show+                 , Test.Check.ShowFunction+  other-modules:       Test.Check.Invariants+  build-depends:       base >= 4 && < 5, template-haskell+  default-language:    Haskell2010++test-suite test+  type:                exitcode-stdio-1.0+  main-is:             test.hs+  hs-source-dirs:      ., tests+  build-depends:       base >= 4 && < 5, template-haskell+  default-language:    Haskell2010++test-suite types+  type:                exitcode-stdio-1.0+  main-is:             test-types.hs+  hs-source-dirs:      ., tests+  build-depends:       base >= 4 && < 5, template-haskell+  default-language:    Haskell2010++test-suite utils+  type:                exitcode-stdio-1.0+  main-is:             test-utils.hs+  hs-source-dirs:      ., tests+  build-depends:       base >= 4 && < 5, template-haskell+  default-language:    Haskell2010++test-suite operators+  type:                exitcode-stdio-1.0+  main-is:             test-operators.hs+  hs-source-dirs:      ., tests+  build-depends:       base >= 4 && < 5, template-haskell+  default-language:    Haskell2010++test-suite most+  type:                exitcode-stdio-1.0+  main-is:             test-most.hs+  hs-source-dirs:      ., tests+  build-depends:       base >= 4 && < 5, template-haskell+  default-language:    Haskell2010++test-suite derive+  type:                exitcode-stdio-1.0+  main-is:             test-most.hs+  hs-source-dirs:      ., tests+  build-depends:       base >= 4 && < 5, template-haskell+  default-language:    Haskell2010++test-suite error+  type:                exitcode-stdio-1.0+  main-is:             test-error.hs+  hs-source-dirs:      ., tests+  build-depends:       base >= 4 && < 5, template-haskell+  default-language:    Haskell2010
+ tests/test-error.hs view
@@ -0,0 +1,60 @@+import System.Exit (exitFailure)+import Data.List (elemIndices,sort)++import Test.Check.Error+import Test.Types (Nat)+import Data.List (sort)++main :: IO ()+main =+  case elemIndices False (tests 100) of+    [] -> putStrLn "Tests passed!"+    is -> do putStrLn ("Failed tests:" ++ show is)+             exitFailure++tests n =+  [ True++  , not $ holds     n prop_sortMinE+  , fails           n prop_sortMinE+  , counterExample  n prop_sortMinE == Just ["[]"]+  , counterExamples n prop_sortMinE == [["[]"]]++  , holds           n prop_sortMin+  , not $ fails     n prop_sortMin+  , counterExample  n prop_sortMin == Nothing+  , counterExamples n prop_sortMin == []++  , exists          n someNumbers+  , witness         n someNumbers == Just ["2"]+  , witnesses       n someNumbers == map ((:[]).show) [2,3,5,7,11,13,17]++  , exists          n someOthers+  , witness         n someOthers == Just ["2","2"]+  , witnesses (100*n) someOthers == (map.map) show [[2,2],[2,4],[3,3]+                                                   ,[2,8],[3,9],[3,27]]+  ]++prop_sortMinE :: [Nat] -> Bool+prop_sortMinE xs = head (sort xs) == minimum (xs::[Nat])++prop_sortMin :: [Nat] -> Bool+prop_sortMin xs = not (null xs)+              ==> head (sort xs) == minimum (xs::[Nat])++someNumbers :: Int -> Bool+someNumbers  2 = True+someNumbers  3 = True+someNumbers  5 = True+someNumbers  7 = True+someNumbers 11 = True+someNumbers 13 = True+someNumbers 17 = True++someOthers :: Int -> Int -> Bool+someOthers = \x -> case x of 2 -> \y -> case y of 2 -> True+                                                  4 -> True+                                                  8 -> True+                             3 -> \y -> case y of 3 -> True+                                                  9 -> True+                                                  27 -> True
+ tests/test-most.hs view
@@ -0,0 +1,34 @@+-- Simple file just to test if everything is imported fine+import System.Exit (exitFailure)+import Data.List (elemIndices)++import Test.Most+import Test.Check.Invariants (strictlyOrdered)++main :: IO ()+main =+  case elemIndices False tests of+    [] -> putStrLn "Tests passed!"+    is -> do putStrLn ("Failed tests:" ++ show is)+             exitFailure++tests =+  [ True++  -- Test.Check+  , holds 1 True++  -- Test.Check.Utils+  , checkCrescent 1++  -- Test.Types+  , [minBound..maxBound :: UInt1] == [0,1]++  -- Test.Operators+  , holds 1 $ (not . not) === id+  ]++checkCrescent :: Int -> Bool+checkCrescent n = strictlyAscendingListsOf (tiers :: [[Nat]])+          =| n |= (map . filter) strictlyOrdered (tiers :: [[[Nat]]])+
+ tests/test-operators.hs view
@@ -0,0 +1,72 @@+import System.Exit (exitFailure)+import Data.List (elemIndices,sort)+import Test.Check+import Test.Operators+import Test.TypeBinding+++main :: IO ()+main =+  case elemIndices False (tests 100) of+    [] -> putStrLn "Tests passed!"+    is -> do putStrLn ("Failed tests:" ++ show is)+             exitFailure++tests :: Int -> [Bool]+tests n =+  [ True++  , holds n $ (not . not) === id+  , fails n $ abs === (* (-1))             -:> int+  , holds n $ (+) ==== (\x y -> sum [x,y]) -:> int+  , fails n $ (+) ==== (*)                 -:> int++  , holds n $ const True  &&& const True  -:> bool+  , fails n $ const False &&& const True  -:> int+  , holds n $ const False ||| const True  -:> int+  , fails n $ const False ||| const False -:> int++  , holds n $ commutative (+)  -:> int+  , holds n $ commutative (*)  -:> int+  , holds n $ commutative (++) -:> [()]+  , holds n $ commutative (&&)+  , holds n $ commutative (||)+  , fails n $ commutative (-)  -:> int+  , fails n $ commutative (++) -:> [bool]+  , fails n $ commutative (==>)++  , holds n $ associative (+)  -:> int+  , holds n $ associative (*)  -:> int+  , holds n $ associative (++) -:> [int]+  , holds n $ associative (&&)+  , holds n $ associative (||)+  , fails n $ associative (-)  -:> int+  , fails n $ associative (==>)++  , holds n $ distributive (*) (+) -:> int+  , fails n $ distributive (+) (*) -:> int++  , holds n $ transitive (==) -:> bool+  , holds n $ transitive (<)  -:> bool+  , holds n $ transitive (<=) -:> bool+  , fails n $ transitive (/=) -:> bool+  , holds n $ transitive (==) -:> int+  , holds n $ transitive (<)  -:> int+  , holds n $ transitive (<=) -:> int+  , fails n $ transitive (/=) -:> int++  , holds n $ idempotent id   -:> int+  , holds n $ idempotent abs  -:> int+  , holds n $ idempotent sort -:> [bool]+  , fails n $ idempotent not++  , holds n $ identity id   -:> int+  , holds n $ identity (+0) -:> int+  , holds n $ identity sort -:> [()]+  , holds n $ identity (not . not)+  , fails n $ identity not++  , holds n $ notIdentity not+  , fails n $ notIdentity abs    -:> int+  , fails n $ notIdentity negate -:> int+  ]
+ tests/test-types.hs view
@@ -0,0 +1,72 @@+import System.Exit (exitFailure)+import Data.List (elemIndices,delete)+import Test.Types+import Test.Check (list,fails)++main :: IO ()+main =+  case elemIndices False tests of+    [] -> putStrLn "Tests passed!"+    is -> do putStrLn ("Failed tests:" ++ show is)+             exitFailure+++-- | Given the number of bits, generates a range for signed/unsigned integers+--   of that width.+signedRange,unsignedRange :: (Num n) => Int -> [n]+signedRange n   = map fromIntegral [-(2^(n-1))..(2^(n-1))-1]+unsignedRange n = map fromIntegral [0..(2^n)-1]+++tests =+  [ True+++  , list `permutation` [minBound..maxBound :: Int1]+  , list `permutation` [minBound..maxBound :: Int2]+  , list `permutation` [minBound..maxBound :: Int3]+  , list `permutation` [minBound..maxBound :: Int4]++  , list `permutation` [minBound..maxBound :: UInt1]+  , list `permutation` [minBound..maxBound :: UInt2]+  , list `permutation` [minBound..maxBound :: UInt3]+  , list `permutation` [minBound..maxBound :: UInt4]++  , list `permutation` [minBound..maxBound :: Nat1]+  , list `permutation` [minBound..maxBound :: Nat2]+  , list `permutation` [minBound..maxBound :: Nat3]+  , list `permutation` [minBound..maxBound :: Nat4]+  , list `permutation` [minBound..maxBound :: Nat5]+  , list `permutation` [minBound..maxBound :: Nat6]+  , list `permutation` [minBound..maxBound :: Nat7]++  , [minBound..maxBound :: Int1] == signedRange 1+  , [minBound..maxBound :: Int2] == signedRange 2+  , [minBound..maxBound :: Int3] == signedRange 3+  , [minBound..maxBound :: Int4] == signedRange 4++  , [minBound..maxBound :: UInt1] == unsignedRange 1+  , [minBound..maxBound :: UInt2] == unsignedRange 2+  , [minBound..maxBound :: UInt3] == unsignedRange 3+  , [minBound..maxBound :: UInt4] == unsignedRange 4+++  -- abs minBound == minBound+  , fails 100 (\i -> abs i > (0::Int1))+  , fails 100 (\i -> abs i > (0::Int2))+  , fails 100 (\i -> abs i > (0::Int3))+  , fails 100 (\i -> abs i > (0::Int4))++  -- maxBound + 1 == minBound+  , fails 100 (\i -> i + 1 < (i::Int1))+  , fails 100 (\i -> i + 1 < (i::Int2))+  , fails 100 (\i -> i + 1 < (i::Int3))+  , fails 100 (\i -> i + 1 < (i::Int4))+  ]+++permutation :: Eq a => [a] -> [a] -> Bool+[]     `permutation` []    = True+(_:_)  `permutation` []    = False+[]     `permutation` (_:_) = False+(x:xs) `permutation` ys    = x `elem` ys  &&  xs `permutation` delete x ys
+ tests/test-utils.hs view
@@ -0,0 +1,68 @@+import System.Exit (exitFailure)+import Data.List (elemIndices, sort, nub, delete)++import Test.Check+import Test.Check.Invariants+import Test.Operators+import Test.TypeBinding+import Test.Types (Nat)+++main :: IO ()+main =+  case elemIndices False tests of+    [] -> putStrLn "Tests passed!"+    is -> do putStrLn ("Failed tests:" ++ show is)+             exitFailure++tests =+  [ True++  , checkNoDup 12+  , checkAscending 18+  , checkStrictlyAscending 20+  , checkLengthListingsOfLength 5 5+  , checkSizesListingsOfLength 5 5++  , productMaybeWith ($) [[const Nothing, Just]] [[1],[2],[3],[4]] == [[1],[2],[3],[4]]+  , productMaybeWith (flip ($))+                     [[1],[2],[3],[4]]+                     [[const Nothing],[Just]] == [[],[1],[2],[3],[4]]++  , holds 100 $ deleteT_is_map_delete 10 -:> nat+  , holds 100 $ deleteT_is_map_delete 10 -:> int+  , holds 100 $ deleteT_is_map_delete 10 -:> bool+  , holds 100 $ deleteT_is_map_delete 10 -:> int2+  ]++deleteT_is_map_delete :: (Eq a, Listable a) => Int -> a -> Bool+deleteT_is_map_delete n x = deleteT x tiers+                    =| n |= normalizeT (map (delete x) tiers)++checkNoDup :: Int -> Bool+checkNoDup n = noDupListsOf (tiers :: [[Int]])+       =| n |= tiers `suchThat` noDup+  where noDup xs = nub (sort xs) == sort xs++checkAscending :: Int -> Bool+checkAscending n = ascendingListsOf (tiers :: [[Nat]])+           =| n |= tiers `suchThat` ordered++checkStrictlyAscending :: Int -> Bool+checkStrictlyAscending n = setsOf (tiers :: [[Nat]])+                   =| n |= tiers `suchThat` strictlyOrdered++checkLengthListingsOfLength :: Int -> Int -> Bool+checkLengthListingsOfLength n m = all check [1..m]+  where check m = all (\xs -> length xs == m)+                $ concat . take n+                $ listsOfLength m natTiers++checkSizesListingsOfLength :: Int -> Int -> Bool+checkSizesListingsOfLength n m = all check [1..m]+  where check m = orderedBy compare+                $ map sum . concat . take n+                $ listsOfLength m natTiers++natTiers :: [[Nat]]+natTiers = tiers
+ tests/test.hs view
@@ -0,0 +1,84 @@+import System.Exit (exitFailure)+import Data.List (elemIndices)++import Test.Check+import Test.Check.Invariants+import Test.Types (Nat)+import Test.Operators+import Test.TypeBinding++main :: IO ()+main =+  case elemIndices False tests of+    [] -> putStrLn "Tests passed!"+    is -> do putStrLn ("Failed tests:" ++ show is)+             exitFailure++tests =+  [ True++  -- interleave+  , [1,2,3] +| [0,0,0] == [1,0,2,0,3,0]+  , take 3 ([1,2] +| (0:undefined)) == [1,0,2]+  , [0,2..] +| [1,3..] =| 100 |= [0,1..]++  -- etc+  , tNatPairOrd 100+  , tNatTripleOrd 200+  , tNatQuadrupleOrd 300+  , tNatQuintupleOrd 400+  , tNatSixtupleOrd 500+  , tNatListOrd 500+  , tListsOfNatOrd 500+  , listsOf (tiers::[[Nat]]) =| 10 |= tiers++  -- tests!+  , counterExample 10 (\x y -> x + y /= (x::Int)) == Just ["0", "0"]+  , counterExample 10 (\x y -> x + y == (x::Int)) == Just ["0", "1"]+  , counterExample 10 (maybe True (==(0::Int))) == Just ["(Just 1)"]+  , holds 100 (\x -> x == (x::Int))++  -- For when NaN is in the enumeration (by default, it is not):+  --, fails 100 (\x -> x == (x::Float))  -- NaN != NaN  :-)+  --, counterExample 100 (\x -> x == (x::Float)) == Just ["NaN"]+  , counterExample 10 (\x y -> x + y == (x::Float))  == Just ["0.0","1.0"]+  , counterExample 10 (\x y -> x + y == (x::Double)) == Just ["0.0","1.0"]+  , holds          50 (\x -> x + 1 /= (x::Int))+  , counterExample 50 (\x -> x + 1 /= (x::Float))  == Just ["Infinity"]+  , counterExample 50 (\x -> x + 1 /= (x::Double)) == Just ["Infinity"]+  , allUnique (take 100 list :: [Float])+  , allUnique (take 500 list :: [Double])++  , tPairEqParams 100+  , tTripleEqParams 100++  , tProductsIsFilterByLength (tiers :: [[ Nat ]])   10 `all` [1..10]+  , tProductsIsFilterByLength (tiers :: [[ Bool ]])   6 `all` [1..10]+  , tProductsIsFilterByLength (tiers :: [[ [Nat] ]])  6 `all` [1..10]++  , holds 100 $  (\/)  ==== zipWith' (++) [] [] -:> [[uint2]]+  , holds 100 $  (\/)  ==== zipWith' (++) [] [] -:> [[bool]]+  , holds 100 $ (\\//) ==== zipWith' (+|) [] [] -:> [[uint2]]+  , holds 100 $ (\\//) ==== zipWith' (+|) [] [] -:> [[bool]]+  ]++allUnique :: Ord a => [a] -> Bool+allUnique [] = True+allUnique (x:xs) = x `notElem` xs+                && allUnique (filter (< x) xs)+                && allUnique (filter (> x) xs)+++-- | 'zipwith\'' works similarly to 'zipWith', but takes neutral elements to+--   operate when one of the lists is exhausted, so, you don't loose elements.+--+-- > zipWith' f z e [x,y] [a,b,c,d] == [f x a, f y b, f z c, f z d]+--+-- > zipWith' f z e [x,y,z] [a] == [f x a, f y e, f z e]+--+-- > zipWith' (+) 0 0 [1,2,3] [1,2,3,4,5,6] == [2,4,6,4,5,6]+zipWith' :: (a->b->c) -> a -> b  -> [a] -> [b] -> [c]+zipWith' _ _  _  []     [] = []+zipWith' f _  zy xs     [] = map (`f` zy) xs+zipWith' f zx _  []     ys = map (f zx) ys+zipWith' f zx zy (x:xs) (y:ys) = f x y : zipWith' f zx zy xs ys