diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2015, Florian Knupfer
+
+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 Florian Knupfer 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.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/bench/Bench.hs b/bench/Bench.hs
new file mode 100644
--- /dev/null
+++ b/bench/Bench.hs
@@ -0,0 +1,12 @@
+module Main where
+
+import Criterion.Main
+import Test.Invariant
+import Test.QuickCheck
+
+main :: IO ()
+main = defaultMain benchmarks
+
+benchmarks :: [Benchmark]
+benchmarks = [ bench "Monotonic" . nfIO . quickCheck $ monotonicIncreasing (*(0::Int)) ]
+
diff --git a/src/Test/Invariant.hs b/src/Test/Invariant.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/Invariant.hs
@@ -0,0 +1,181 @@
+module Test.Invariant where
+
+import Test.QuickCheck
+infix 1 <=>, &>
+
+-- | Defines extensional equality.  This allows concise, point-free,
+-- definitions of laws.  For example idempotence:
+--
+-- > f <=> f . f
+(<=>) :: Eq b => (a -> b) -> (a -> b) -> a -> Bool
+(f <=> g) x = f x == g x
+
+-- | Pointfree version of QuickChecks ==>.  This notation reduces a
+-- lot of lambdas, for example:
+--
+-- >>> quickCheck $ (/=0) &> not . idempotent (*(2::Int))
+-- +++ OK, passed 100 tests.
+(&>) :: Testable b => (a -> Bool) -> (a -> b) -> a -> Property
+(a &> b) x = a x ==> b x
+
+-- | Checks whether a function is idempotent.
+--
+-- > f(f(x)) == f(x)
+--
+-- >>> quickCheck $ idempotent (abs :: Int -> Int)
+-- +++ OK, passed 100 tests.
+idempotent :: Eq a => (a -> a) -> a -> Bool
+idempotent f = f <=> f . f
+
+-- | Checks whether a function is pointSymmetric.
+--
+-- > f(-x) == -f(x)
+--
+-- >>> quickCheck $ pointSymmetric (^3)
+-- +++ OK, passed 100 tests.
+pointSymmetric :: (Num a, Num b, Eq b) => (a -> b) -> a -> Bool
+pointSymmetric f = f . negate <=> negate . f
+
+-- | Checks whether a function is reflectionSymmetric.
+--
+-- > f(x) == f(-x)
+--
+-- >>> quickCheck $ pointSymmetric (^2)
+-- +++ OK, passed 100 tests.
+reflectionSymmetric :: (Num a, Eq b) => (a -> b) -> a -> Bool
+reflectionSymmetric f = f . negate <=> f
+
+-- | Checks whether a function is monotonicIncreasing.
+--
+-- > x >= y,  f(x) >= f(y)
+--
+-- >>> quickCheck $ monotonicIncreasing ceiling
+-- +++ OK, passed 100 tests.
+monotonicIncreasing :: (Ord a, Ord b) => (a -> b) -> a -> a -> Bool
+monotonicIncreasing f x y  = compare (f x) (f y) `elem` [EQ, compare x y]
+
+-- | Checks whether a function is strictly monotonicIncreasing'.
+--
+-- > x > y,  f(x) > f(y)
+--
+-- >>> quickCheck $ monotonicIncreasing' (+1)
+-- +++ OK, passed 100 tests.
+monotonicIncreasing' :: (Ord a, Ord b) => (a -> b) -> a -> a -> Bool
+monotonicIncreasing' f x y = compare (f x) (f y) == compare x y
+
+-- | Checks whether a function is monotonicDecreasing.
+--
+-- > x >= y,  f(x) <= f(y)
+--
+-- >>> quickCheck $ monotonicDecreasing (\x -> floor $ negate x)
+-- +++ OK, passed 100 tests.
+monotonicDecreasing :: (Ord a, Ord b) => (a -> b) -> a -> a -> Bool
+monotonicDecreasing f x y  = compare (f x) (f y) `elem` [EQ, compare y x]
+
+-- | Checks whether a function is strictly monotonicDecreasing'.
+--
+-- > x > y,  f(x) < f(y)
+--
+-- >>> quickCheck $ monotonicDecreasing' (-1)
+-- +++ OK, passed 100 tests.
+monotonicDecreasing' :: (Ord a, Ord b) => (a -> b) -> a -> a -> Bool
+monotonicDecreasing' f x y = compare (f x) (f y) == compare y x
+
+-- TODO create sorted list and fold with predicate over it
+
+-- | Checks whether a function is involutory.
+--
+-- > f(f(x)) = x
+--
+-- >>> quickCheck $ involutory negate
+-- +++ OK, passed 100 tests.
+involutory :: Eq a => (a -> a) -> a -> Bool
+involutory f = f . f <=> id
+
+-- | Checks whether a function is the inverse of another function.
+--
+-- > f(g(x)) = x
+--
+-- >>> quickCheck $ (`div` 2) `inverts` (*2)
+-- +++ OK, passed 100 tests.
+inverts :: Eq a => (b -> a) -> (a -> b) -> a -> Bool
+f `inverts` g = f . g <=> id
+
+-- | Checks whether an binary operator is commutative.
+--
+-- > a * b = b * a
+--
+-- >>> quickCheck $ commutative (+)
+-- +++ OK, passed 100 tests.
+commutative :: Eq b => (a -> a -> b) -> a -> a -> Bool
+commutative f x y = x `f` y == y `f` x
+
+-- | Checks whether an binary operator is associative.
+--
+-- > a + (b + c) = (a + b) + c
+--
+-- >>> quickCheck $ associative (+)
+-- +++ OK, passed 100 tests.
+associative :: Eq a => (a -> a -> a) -> a -> a -> a -> Bool
+associative f x y z = x `f` (y `f` z) == (x `f` y) `f` z
+
+-- | Checks whether an operator is left-distributive over an other operator.
+--
+-- > a * (b + c) = (a * b) + (a * c)
+--
+-- >>> quickCheck $ (*) `distributesLeftOver` (+)
+-- +++ OK, passed 100 tests.
+distributesLeftOver :: Eq a => (a -> a -> a) -> (a -> a -> a) -> a -> a -> a -> Bool
+(f `distributesLeftOver` g) x y z = x `f` (y `g` z) == (x `f` y) `g` (x `f` z)
+
+-- | Checks whether an operator is right-distributive over an other operator.
+--
+-- > (b + c) / a = (b / a) + (c / a)
+--
+-- >>> quickCheck $ (/) `distributesRightOver` (+)
+-- +++ OK, passed 100 tests.
+distributesRightOver :: Eq a => (a -> a -> a) -> (a -> a -> a) -> a -> a -> a -> Bool
+(f `distributesRightOver` g) x y z = (y `g` z) `f` x == (x `f` y) `g` (x `f` z)
+
+-- | Checks whether an operator is distributive over an other operator.
+--
+-- > a * (b + c) = (a * b) + (a * c) = (b + c) * a
+--
+-- >>> quickCheck $ (*) `distributesOver` (+)
+-- +++ OK, passed 100 tests.
+distributesOver :: Eq a => (a -> a -> a) -> (a -> a -> a) -> a -> a -> a -> Bool
+(f `distributesOver` g) x y z = (f `distributesLeftOver` g) x y z
+                            && (f `distributesRightOver` g) x y z
+
+-- | Checks whether a function increases the size of a foldable.
+--
+-- >>> quickCheck $ inflating (1:)
+-- +++ OK, passed 100 tests.
+inflating :: (Foldable f, Foldable f') => (f a -> f' b) -> f a -> Bool
+inflating f xs = length (f xs) > length xs
+
+-- | Checks whether a function decreases the size of a foldable.
+--
+--
+-- >>> quickCheck $ deflating tail
+-- +++ OK, passed 100 tests.
+deflating :: (Foldable f, Foldable f') => (f a -> f' b) -> f a -> Bool
+deflating f xs = null xs || length (f xs) < length xs
+
+-- | Checks whether a function is cyclic by applying its result to
+-- itself within n applications.
+--
+-- >>> quickCheck $ (`div` 10) `cyclesWithin` 100
+-- +++ OK, passed 100 tests.
+cyclesWithin :: Eq a => (a -> a) -> Int -> a -> Bool
+f `cyclesWithin` n = go [] . take (n + 1) . iterate f
+             where go xs (y:ys) | y `elem` xs = True
+                                | otherwise   = go (y:xs) ys
+                   go _ _ = False
+
+-- | Checks whether a function is invariant over an other function.
+--
+-- >>> quickCheck $ length `invariatesOver` reverse
+-- +++ OK, passed 100 tests.
+invariatesOver :: Eq b => (a -> b) -> (a -> a) -> a -> Bool
+f `invariatesOver` g = f . g <=> f
diff --git a/test-invariant.cabal b/test-invariant.cabal
new file mode 100644
--- /dev/null
+++ b/test-invariant.cabal
@@ -0,0 +1,56 @@
+name:                test-invariant
+version:             0.3.1.2
+synopsis:            Provide common invariants to be checked with QuickCheck
+
+description: test-invariant is a library for providing common invariants of
+             functions as higher order polymorphic functions.  This reduces for a
+             lot of cases the need for writing prop_ functions for QuickCheck.
+             .
+             >>> quickCheck $ idempotent (abs :: Int -> Int)
+             >>> quickCheck $ involutory not
+             >>> quickCheck $ not . involutory (+ (2 :: Int))
+
+license:             BSD3
+license-file:        LICENSE
+author:              Florian Knupfer <fknupfer@gmail.com>
+maintainer:          Florian Knupfer <fknupfer@gmail.com>
+copyright:           2015 Florian Knupfer
+category:            Testing
+build-type:          Simple
+homepage:            https://github.com/knupfer/test-invariant
+-- extra-source-files:
+cabal-version:       >=1.10
+
+source-repository head
+  type:     git
+  location: https://github.com/knupfer/test-invariant
+                     
+library
+  exposed-modules:   Test.Invariant
+  -- other-modules:
+  -- other-extensions:
+  build-depends:     base >=4.8 && <4.9
+                   , QuickCheck
+  hs-source-dirs:    src
+  default-language:  Haskell2010
+
+test-suite Tasty
+  type:              exitcode-stdio-1.0
+  build-depends:     base >=4.8 && <4.9
+                   , test-invariant
+                   , QuickCheck
+                   , tasty
+                   , tasty-quickcheck
+  hs-source-dirs:    test
+  main-is:           Test.hs
+  default-language:  Haskell2010
+
+benchmark Criterion
+  build-depends:     base
+                   , test-invariant
+                   , criterion
+                   , QuickCheck
+  default-language:  Haskell2010
+  hs-source-dirs:    bench
+  main-is:           Bench.hs
+  type:              exitcode-stdio-1.0
diff --git a/test/Test.hs b/test/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Test.hs
@@ -0,0 +1,31 @@
+module Main where
+
+import Test.Invariant
+import Test.Tasty
+import Test.Tasty.QuickCheck
+
+main :: IO ()
+main = defaultMain $
+  testGroup "Tests"
+    [ testGroup "Arity 1"
+      [ testProperty "Idempotence" $
+        idempotent (* (0 :: Int))
+
+      , testProperty "Idempotence" $
+        (/=0) &> not . idempotent (* (2 :: Int))
+
+      , testProperty "Idempotence" $
+        idempotent (abs :: Double -> Double)
+
+      , testProperty "Idempotence" $
+        idempotent (signum :: Int -> Int)
+
+      , testProperty "Idempotence" $
+        ((*) :: Int -> Int -> Int) `distributesOver` (+)
+
+      , testProperty "Idempotence" $
+        (*) `distributesOver` ((+) :: Int -> Int -> Int)
+      ]
+    ]
+
+
