diff --git a/hmm-hmatrix.cabal b/hmm-hmatrix.cabal
--- a/hmm-hmatrix.cabal
+++ b/hmm-hmatrix.cabal
@@ -1,5 +1,5 @@
 Name:                hmm-hmatrix
-Version:             0.1.0.1
+Version:             0.1.1
 Synopsis:            Hidden Markov Models using HMatrix primitives
 Description:
   Hidden Markov Models implemented using HMatrix data types and operations.
@@ -41,7 +41,7 @@
   Changes.md
 
 Source-Repository this
-  Tag:         0.1.0.1
+  Tag:         0.1.1
   Type:        darcs
   Location:    http://hub.darcs.net/thielema/hmm-hmatrix
 
@@ -58,14 +58,16 @@
     Math.HiddenMarkovModel.Example.TrafficLight
     Math.HiddenMarkovModel.Example.SineWave
     Math.HiddenMarkovModel.Example.Circle
+    Math.HiddenMarkovModel.Test
   Other-Modules:
+    Math.HiddenMarkovModel.Example.TrafficLightPrivate
     Math.HiddenMarkovModel.Normalized
     Math.HiddenMarkovModel.Private
     Math.HiddenMarkovModel.Utility
     Math.HiddenMarkovModel.CSV
-    Math.HiddenMarkovModel.Test
   Build-Depends:
     hmatrix >=0.16 && <0.17,
+    QuickCheck >=2.5 && <3,
     explicit-exception >=0.1.7 && <0.2,
     lazy-csv >=0.5 && <0.6,
     random >=1.0 && <1.2,
@@ -78,5 +80,16 @@
     deepseq >=1.3 && <1.5,
     base >=4.5 && <5
   Hs-Source-Dirs:      src
+  Default-Language:    Haskell2010
+  GHC-Options:         -Wall
+
+Test-Suite hmm-test
+  Type: exitcode-stdio-1.0
+  Build-Depends:
+    hmm-hmatrix,
+    QuickCheck,
+    base
+  Main-Is: Main.hs
+  Hs-Source-Dirs:      test
   Default-Language:    Haskell2010
   GHC-Options:         -Wall
diff --git a/src/Math/HiddenMarkovModel/Example/TrafficLight.hs b/src/Math/HiddenMarkovModel/Example/TrafficLight.hs
--- a/src/Math/HiddenMarkovModel/Example/TrafficLight.hs
+++ b/src/Math/HiddenMarkovModel/Example/TrafficLight.hs
@@ -34,139 +34,16 @@
 -}
 module Math.HiddenMarkovModel.Example.TrafficLight
 {-# WARNING "do not import that module, it is only intended for demonstration" #-}
-   where
-
-import qualified Math.HiddenMarkovModel as HMM
-import qualified Math.HiddenMarkovModel.Distribution as Distr
-
-import qualified Data.Packed.Matrix as Matrix
-import qualified Data.Packed.Vector as Vector
-
-import Text.Read.HT (maybeRead)
-
-import Control.Monad (liftM2)
-
-import qualified Data.Map as Map
-import qualified Data.NonEmpty as NonEmpty
-import qualified Data.List.HT as ListHT
-import Data.NonEmpty ((!:))
-
-
-
-data Color = Red | Yellow | Green
-   deriving (Eq, Ord, Enum, Show, Read)
-
-{- |
-Using 'show' and 'read' is not always a good choice
-since they must format and parse Haskell expressions
-which is not of much use to the outside world.
--}
-instance Distr.CSVSymbol Color where
-   cellFromSymbol = show
-   symbolFromCell = maybeRead
-
-
-hmm :: HMM.Discrete Double Color
-hmm =
-   HMM.Cons {
-      HMM.initial = Vector.fromList [1/3, 1/6, 1/3, 1/6],
-      HMM.transition =
-         Matrix.fromLists $
-            [0.8, 0.0, 0.0, 0.2] :
-            [0.2, 0.8, 0.0, 0.0] :
-            [0.0, 0.2, 0.8, 0.0] :
-            [0.0, 0.0, 0.2, 0.8] :
-            [],
-      HMM.distribution =
-         Distr.Discrete $ Map.fromList $
-            (Red,    Vector.fromList [1,0,0,0]) :
-            (Yellow, Vector.fromList [0,1,0,1]) :
-            (Green,  Vector.fromList [0,0,1,0]) :
-            []
-   }
-
-hmmDisturbed :: HMM.Discrete Double Color
-hmmDisturbed =
-   HMM.Cons {
-      HMM.initial = Vector.fromList [1/4, 1/4, 1/4, 1/4],
-      HMM.transition =
-         Matrix.fromLists $
-            [0.3, 0.2, 0.2, 0.3] :
-            [0.3, 0.3, 0.2, 0.2] :
-            [0.2, 0.3, 0.3, 0.2] :
-            [0.2, 0.2, 0.3, 0.3] :
-            [],
-      HMM.distribution =
-         Distr.Discrete $ Map.fromList $
-            (Red,    Vector.fromList [0.6, 0.2, 0.2, 0.2]) :
-            (Yellow, Vector.fromList [0.2, 0.6, 0.2, 0.6]) :
-            (Green,  Vector.fromList [0.2, 0.2, 0.6, 0.2]) :
-            []
-   }
-
-
-red, yellowRG, green, yellowGR :: (HMM.State, Color)
-red      = (HMM.state 0, Red)
-yellowRG = (HMM.state 1, Yellow)
-green    = (HMM.state 2, Green)
-yellowGR = (HMM.state 3, Yellow)
-
-labeledSequences :: NonEmpty.T [] (NonEmpty.T [] (HMM.State, Color))
-labeledSequences =
-   (red !: red : red : red :
-    yellowRG : yellowRG :
-    green : green : green : green : green :
-    yellowGR :
-    red : red : red :
-    []) !:
-   (green !: green : green :
-    yellowGR :
-    red : red : red : red :
-    yellowRG :
-    green : green : green : green : green :
-    yellowGR : yellowGR :
-    []) :
-   []
-
-{- |
-Construct a Hidden Markov model by watching a set
-of manually created sequences of emissions and according states.
--}
-hmmTrainedSupervised :: HMM.Discrete Double Color
-hmmTrainedSupervised =
-   HMM.trainMany (HMM.trainSupervised 4) labeledSequences
-
-
-stateSequences :: NonEmpty.T [] (NonEmpty.T [] Color)
-stateSequences = fmap (fmap snd) labeledSequences
-
-{- |
-Construct a Hidden Markov model starting from a known model
-and a set of sequences that contain only the emissions, but no states.
--}
-hmmTrainedUnsupervised :: HMM.Discrete Double Color
-hmmTrainedUnsupervised =
-   HMM.trainMany (HMM.trainUnsupervised hmm) stateSequences
-
-{- |
-Repeat unsupervised training until convergence.
--}
-hmmIterativelyTrained :: HMM.Discrete Double Color
-hmmIterativelyTrained =
-   snd $ head $ dropWhile fst $
-   ListHT.mapAdjacent (\hmm0 hmm1 -> (HMM.deviation hmm0 hmm1 > 1e-5, hmm1)) $
-   iterate
-      (flip HMM.trainMany stateSequences . HMM.trainUnsupervised)
-      hmmDisturbed
-
-
-verifyRevelation ::
-   HMM.Discrete Double Color -> NonEmpty.T [] (HMM.State, Color) -> Bool
-verifyRevelation model xs =
-   fmap fst xs == HMM.reveal model (fmap snd xs)
+   (
+   Color(..),
+   hmm,
+   hmmDisturbed,
+   red, yellowRG, green, yellowGR,
+   labeledSequences,
+   hmmTrainedSupervised,
+   stateSequences,
+   hmmTrainedUnsupervised,
+   hmmIterativelyTrained,
+   ) where
 
-verifyRevelations :: [Bool]
-verifyRevelations =
-   liftM2 verifyRevelation
-      [hmm, hmmDisturbed, hmmTrainedSupervised, hmmTrainedUnsupervised]
-      (NonEmpty.flatten labeledSequences)
+import Math.HiddenMarkovModel.Example.TrafficLightPrivate
diff --git a/src/Math/HiddenMarkovModel/Example/TrafficLightPrivate.hs b/src/Math/HiddenMarkovModel/Example/TrafficLightPrivate.hs
new file mode 100644
--- /dev/null
+++ b/src/Math/HiddenMarkovModel/Example/TrafficLightPrivate.hs
@@ -0,0 +1,136 @@
+module Math.HiddenMarkovModel.Example.TrafficLightPrivate where
+
+import qualified Math.HiddenMarkovModel as HMM
+import qualified Math.HiddenMarkovModel.Distribution as Distr
+
+import qualified Data.Packed.Matrix as Matrix
+import qualified Data.Packed.Vector as Vector
+
+import Text.Read.HT (maybeRead)
+
+import Control.Monad (liftM2)
+
+import qualified Data.Map as Map
+import qualified Data.NonEmpty as NonEmpty
+import qualified Data.List.HT as ListHT
+import Data.NonEmpty ((!:))
+
+
+
+data Color = Red | Yellow | Green
+   deriving (Eq, Ord, Enum, Show, Read)
+
+{- |
+Using 'show' and 'read' is not always a good choice
+since they must format and parse Haskell expressions
+which is not of much use to the outside world.
+-}
+instance Distr.CSVSymbol Color where
+   cellFromSymbol = show
+   symbolFromCell = maybeRead
+
+
+hmm :: HMM.Discrete Double Color
+hmm =
+   HMM.Cons {
+      HMM.initial = Vector.fromList [1/3, 1/6, 1/3, 1/6],
+      HMM.transition =
+         Matrix.fromLists $
+            [0.8, 0.0, 0.0, 0.2] :
+            [0.2, 0.8, 0.0, 0.0] :
+            [0.0, 0.2, 0.8, 0.0] :
+            [0.0, 0.0, 0.2, 0.8] :
+            [],
+      HMM.distribution =
+         Distr.Discrete $ Map.fromList $
+            (Red,    Vector.fromList [1,0,0,0]) :
+            (Yellow, Vector.fromList [0,1,0,1]) :
+            (Green,  Vector.fromList [0,0,1,0]) :
+            []
+   }
+
+hmmDisturbed :: HMM.Discrete Double Color
+hmmDisturbed =
+   HMM.Cons {
+      HMM.initial = Vector.fromList [1/4, 1/4, 1/4, 1/4],
+      HMM.transition =
+         Matrix.fromLists $
+            [0.3, 0.2, 0.2, 0.3] :
+            [0.3, 0.3, 0.2, 0.2] :
+            [0.2, 0.3, 0.3, 0.2] :
+            [0.2, 0.2, 0.3, 0.3] :
+            [],
+      HMM.distribution =
+         Distr.Discrete $ Map.fromList $
+            (Red,    Vector.fromList [0.6, 0.2, 0.2, 0.2]) :
+            (Yellow, Vector.fromList [0.2, 0.6, 0.2, 0.6]) :
+            (Green,  Vector.fromList [0.2, 0.2, 0.6, 0.2]) :
+            []
+   }
+
+
+red, yellowRG, green, yellowGR :: (HMM.State, Color)
+red      = (HMM.state 0, Red)
+yellowRG = (HMM.state 1, Yellow)
+green    = (HMM.state 2, Green)
+yellowGR = (HMM.state 3, Yellow)
+
+labeledSequences :: NonEmpty.T [] (NonEmpty.T [] (HMM.State, Color))
+labeledSequences =
+   (red !: red : red : red :
+    yellowRG : yellowRG :
+    green : green : green : green : green :
+    yellowGR :
+    red : red : red :
+    []) !:
+   (green !: green : green :
+    yellowGR :
+    red : red : red : red :
+    yellowRG :
+    green : green : green : green : green :
+    yellowGR : yellowGR :
+    []) :
+   []
+
+{- |
+Construct a Hidden Markov model by watching a set
+of manually created sequences of emissions and according states.
+-}
+hmmTrainedSupervised :: HMM.Discrete Double Color
+hmmTrainedSupervised =
+   HMM.trainMany (HMM.trainSupervised 4) labeledSequences
+
+
+stateSequences :: NonEmpty.T [] (NonEmpty.T [] Color)
+stateSequences = fmap (fmap snd) labeledSequences
+
+{- |
+Construct a Hidden Markov model starting from a known model
+and a set of sequences that contain only the emissions, but no states.
+-}
+hmmTrainedUnsupervised :: HMM.Discrete Double Color
+hmmTrainedUnsupervised =
+   HMM.trainMany (HMM.trainUnsupervised hmm) stateSequences
+
+{- |
+Repeat unsupervised training until convergence.
+-}
+hmmIterativelyTrained :: HMM.Discrete Double Color
+hmmIterativelyTrained =
+   snd $ head $ dropWhile fst $
+   ListHT.mapAdjacent (\hmm0 hmm1 -> (HMM.deviation hmm0 hmm1 > 1e-5, hmm1)) $
+   iterate
+      (flip HMM.trainMany stateSequences . HMM.trainUnsupervised)
+      hmmDisturbed
+
+
+verifyRevelation ::
+   HMM.Discrete Double Color -> NonEmpty.T [] (HMM.State, Color) -> Bool
+verifyRevelation model xs =
+   fmap fst xs == HMM.reveal model (fmap snd xs)
+
+verifyRevelations :: [Bool]
+verifyRevelations =
+   liftM2 verifyRevelation
+      [hmm, hmmDisturbed, hmmTrainedSupervised, hmmTrainedUnsupervised]
+      (NonEmpty.flatten labeledSequences)
diff --git a/src/Math/HiddenMarkovModel/Test.hs b/src/Math/HiddenMarkovModel/Test.hs
--- a/src/Math/HiddenMarkovModel/Test.hs
+++ b/src/Math/HiddenMarkovModel/Test.hs
@@ -1,5 +1,11 @@
-module Math.HiddenMarkovModel.Test where
+{- |
+Do not import this module, it is only intended for testing!
+-}
+module Math.HiddenMarkovModel.Test (tests) where
 
+import qualified Math.HiddenMarkovModel.Example.TrafficLightPrivate
+                                                            as TrafficLight
+
 import qualified Math.HiddenMarkovModel as HMM
 import qualified Math.HiddenMarkovModel.Normalized as Normalized
 import qualified Math.HiddenMarkovModel.Private as Priv
@@ -11,6 +17,7 @@
 import Data.Packed.Matrix (Matrix)
 import Data.Packed.Vector (Vector)
 
+import qualified Test.QuickCheck as QC
 import qualified System.Random as Rnd
 
 import qualified Data.NonEmpty.Class as NonEmptyC
@@ -21,7 +28,9 @@
 import qualified Data.Map as Map
 import Data.NonEmpty ((!:))
 
+import Text.Printf (printf)
 
+
 hmm :: HMM.Discrete Double Char
 hmm =
    HMM.Cons {
@@ -159,3 +168,52 @@
 nonEmptyScanr :: Int -> [Int] -> Bool
 nonEmptyScanr x xs =
    Normalized.nonEmptyScanr (-) x xs == NonEmpty.scanr (-) x xs
+
+
+allPair :: (a -> Bool, b -> Bool) -> (a,b) -> Bool
+allPair (f,g) (a,b) = f a && g b
+
+allTriple :: (a -> Bool, b -> Bool, c -> Bool) -> (a,b,c) -> Bool
+allTriple (f,g,h) (a,b,c) = f a && g b && h c
+
+almostZero :: Double -> Bool
+almostZero x  =  x < 1e-10
+
+almostOne :: Double -> Bool
+almostOne x  =  almostZero $ abs (x-1)
+
+almostEqual :: Double -> Double -> Bool
+almostEqual x y  =  almostZero $ abs (x-y)
+
+tests :: [(String, QC.Property)]
+tests =
+   ("sequLikelihood",
+      QC.property $
+      case sequLikelihood of
+         (forwardBackward, expLog, sumProb, alphaBetas) ->
+            allPair (almostEqual sumProb, almostEqual sumProb) forwardBackward
+            &&
+            almostEqual sumProb expLog
+            &&
+            length (NonEmpty.tail sequ) == length (NonEmpty.tail alphaBetas)
+            &&
+            Fold.all (almostEqual sumProb) alphaBetas) :
+   ("sequLikelihoodNormalized",
+      QC.property $
+      length (NonEmpty.tail sequ) ==
+         length (NonEmpty.tail sequLikelihoodNormalized)
+      &&
+      Fold.all almostOne sequLikelihoodNormalized) :
+   ("zetasDiff",
+      QC.property $ allTriple (id, almostZero, almostZero) zetasDiff) :
+   ("xisDiff", QC.property $ allPair (id, almostZero) xisDiff) :
+   ("reveal", QC.property reveal) :
+   ("trainUnsupervisedDiff",
+      QC.property $
+      allTriple (almostZero, almostZero, allPair (id, almostZero)) $
+      trainUnsupervisedDiff) :
+   ("nonEmptyScanr", QC.property nonEmptyScanr) :
+   (zipWith
+      (\k b -> (printf "TrafficLight.verifyRevelation.%d" k, QC.property b))
+      [(0::Int) ..] TrafficLight.verifyRevelations) ++
+   []
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,10 @@
+module Main where
+
+import Math.HiddenMarkovModel.Test (tests)
+
+import qualified Test.QuickCheck as QC
+
+
+main :: IO ()
+main =
+   mapM_ (\(name,prop) -> putStr (name ++ ": ") >> QC.quickCheck prop) tests
