diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,27 +1,30 @@
-Copyright (c) Amy de Buitléir 2010-2015
+Copyright Amy de Buitléir (c) 2010-2017
+
 All rights reserved.
 
-Redistribution and use in source and binary forms, with or without 
-modification, are permitted provided that the following conditions 
-are met:
+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 the author nor the names of other contributors
-  may be used to endorse or promote products derived from this software
-  without specific prior written permission.
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
 
-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 
-HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+    * 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 Amy de Buitléir 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 
+(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/som.cabal b/som.cabal
--- a/som.cabal
+++ b/som.cabal
@@ -1,76 +1,70 @@
-Name:              som
-Version:           9.0.1
-Stability:         experimental
-Synopsis:          Self-Organising Maps.
-Description:       A Kohonen Self-organising Map (SOM) maps input patterns 
-                   onto a regular grid (usually two-dimensional) where each
-                   node in the grid is a model of the input data, and does
-                   so using a method which ensures that any topological
-                   relationships within the input data are also represented
-                   in the grid. This implementation supports the use of 
-                   non-numeric patterns.
-                   .
-                   In layman's terms, a SOM can be useful when you you want
-                   to discover the underlying structure of some data.
-                   .
-                   The userguide is available at 
-                   <https://github.com/mhwombat/som/wiki>.
-Category:          Math
-License:           BSD3
-License-file:      LICENSE
-Copyright:         (c) Amy de Buitléir 2010-2015
-Homepage:          https://github.com/mhwombat/som
-Bug-reports:       https://github.com/mhwombat/som/issues
-Author:            Amy de Buitléir
-Maintainer:        amy@nualeargais.ie
-Build-Type:        Simple
-Cabal-Version:     >=1.8
+name: som
+version: 9.0.2
+cabal-version: >=1.10
+build-type: Simple
+license: BSD3
+license-file: LICENSE
+copyright: (c) 2010-2017 Amy de Buitléir
+maintainer: amy@nualeargais.ie
+homepage: https://github.com/mhwombat/som#readme
+bug-reports: https://github.com/mhwombat/som/issues
+synopsis: Self-Organising Maps.
+description:
+    A Kohonen Self-organising Map (SOM) maps input patterns
+    onto a regular grid (usually two-dimensional) where each
+    node in the grid is a model of the input data, and does
+    so using a method which ensures that any topological
+    relationships within the input data are also represented
+    in the grid. This implementation supports the use of
+    non-numeric patterns.
+    .
+    In layman's terms, a SOM can be useful when you you want
+    to discover the underlying structure of some data.
+    .
+    The userguide is available at
+    <https://github.com/mhwombat/som/wiki>.
+category: Math
+author: Amy de Buitléir
 
 source-repository head
-  type:     git
-  location: https://github.com/mhwombat/som.git
-
-source-repository this
-  type:     git
-  location: https://github.com/mhwombat/som.git
-  tag:      8.2.3
-
+    type: git
+    location: https://github.com/mhwombat/som
 
 library
-  hs-source-dirs:  src
-  build-depends:   assert ==0.0.*,
-                   base >=4.8 && <5,
-                   containers ==0.5.*,
-                   deepseq ==1.4.*,
-                   grid ==7.* && >=7.7,
-                   MonadRandom ==0.4.*
-  ghc-options:     -Wall
-  exposed-modules: Data.Datamining.Clustering.SOM,
-                   Data.Datamining.Clustering.SOMInternal,
-                   Data.Datamining.Clustering.DSOM,
-                   Data.Datamining.Clustering.DSOMInternal,
-                   Data.Datamining.Clustering.SGM,
-                   Data.Datamining.Clustering.SGMInternal,
-                   Data.Datamining.Clustering.Classifier,
-                   Data.Datamining.Pattern
-
-test-suite som-tests
-  type:            exitcode-stdio-1.0
-  build-depends:   assert ==0.0.*,
-                   base >=4.8 && <5,
-                   test-framework-quickcheck2 == 0.3.*,
-                   QuickCheck ==2.8.*,
-                   test-framework ==0.8.*,
-                   som,
-                   containers ==0.5.*,
-                   grid ==7.* && >=7.7,
-                   MonadRandom ==0.4.*,
-                   random ==1.1.*
-  hs-source-dirs:  test
-  ghc-options:     -Wall
-  main-is:         Main.hs
-  other-modules:   Data.Datamining.Clustering.SOMQC,
-                   Data.Datamining.Clustering.DSOMQC,
-                   Data.Datamining.Clustering.SGMQC,
-                   Data.Datamining.PatternQC
+    exposed-modules:
+        Data.Datamining.Clustering.SOM
+        Data.Datamining.Clustering.SOMInternal
+        Data.Datamining.Clustering.DSOM
+        Data.Datamining.Clustering.DSOMInternal
+        Data.Datamining.Clustering.SGM
+        Data.Datamining.Clustering.SGMInternal
+        Data.Datamining.Clustering.Classifier
+        Data.Datamining.Pattern
+    build-depends:
+        assert >=0.0.1.2 && <0.1,
+        base >=4.9.1.0 && <4.10,
+        containers >=0.5.7.1 && <0.6,
+        deepseq >=1.4.2.0 && <1.5,
+        grid >=7.8.8 && <7.9,
+        MonadRandom >=0.5.1 && <0.6
+    default-language: Haskell2010
+    hs-source-dirs: src
+    ghc-options: -Wall
 
+test-suite som-test
+    type: exitcode-stdio-1.0
+    main-is: Main.hs
+    build-depends:
+        assert >=0.0.1.2 && <0.1,
+        base >=4.9.1.0 && <4.10,
+        test-framework-quickcheck2 >=0.3.0.3 && <0.4,
+        QuickCheck >=2.9.2 && <2.10,
+        test-framework >=0.8.1.1 && <0.9,
+        som >=9.0.2 && <9.1,
+        containers >=0.5.7.1 && <0.6,
+        grid >=7.8.8 && <7.9,
+        MonadRandom >=0.5.1 && <0.6,
+        random ==1.1.*
+    default-language: Haskell2010
+    hs-source-dirs: test
+    ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
diff --git a/src/Data/Datamining/Clustering/Classifier.hs b/src/Data/Datamining/Clustering/Classifier.hs
--- a/src/Data/Datamining/Clustering/Classifier.hs
+++ b/src/Data/Datamining/Clustering/Classifier.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Clustering.Classifier
--- Copyright   :  (c) Amy de Buitléir 2012-2015
+-- Copyright   :  (c) Amy de Buitléir 2012-2016
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
diff --git a/src/Data/Datamining/Clustering/DSOM.hs b/src/Data/Datamining/Clustering/DSOM.hs
--- a/src/Data/Datamining/Clustering/DSOM.hs
+++ b/src/Data/Datamining/Clustering/DSOM.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Clustering.SOM
--- Copyright   :  (c) Amy de Buitléir 2012-2015
+-- Copyright   :  (c) Amy de Buitléir 2012-2016
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
diff --git a/src/Data/Datamining/Clustering/DSOMInternal.hs b/src/Data/Datamining/Clustering/DSOMInternal.hs
--- a/src/Data/Datamining/Clustering/DSOMInternal.hs
+++ b/src/Data/Datamining/Clustering/DSOMInternal.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Clustering.DSOMInternal
--- Copyright   :  (c) Amy de Buitléir 2012-2015
+-- Copyright   :  (c) Amy de Buitléir 2012-2016
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
diff --git a/src/Data/Datamining/Clustering/SGM.hs b/src/Data/Datamining/Clustering/SGM.hs
--- a/src/Data/Datamining/Clustering/SGM.hs
+++ b/src/Data/Datamining/Clustering/SGM.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Clustering.SGM
--- Copyright   :  (c) Amy de Buitléir 2012-2015
+-- Copyright   :  (c) Amy de Buitléir 2012-2016
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
diff --git a/src/Data/Datamining/Clustering/SGMInternal.hs b/src/Data/Datamining/Clustering/SGMInternal.hs
--- a/src/Data/Datamining/Clustering/SGMInternal.hs
+++ b/src/Data/Datamining/Clustering/SGMInternal.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Clustering.SGMInternal
--- Copyright   :  (c) Amy de Buitléir 2012-2015
+-- Copyright   :  (c) Amy de Buitléir 2012-2016
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
diff --git a/src/Data/Datamining/Clustering/SOM.hs b/src/Data/Datamining/Clustering/SOM.hs
--- a/src/Data/Datamining/Clustering/SOM.hs
+++ b/src/Data/Datamining/Clustering/SOM.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Clustering.SOM
--- Copyright   :  (c) Amy de Buitléir 2012-2015
+-- Copyright   :  (c) Amy de Buitléir 2012-2016
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
diff --git a/src/Data/Datamining/Clustering/SOMInternal.hs b/src/Data/Datamining/Clustering/SOMInternal.hs
--- a/src/Data/Datamining/Clustering/SOMInternal.hs
+++ b/src/Data/Datamining/Clustering/SOMInternal.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Clustering.SOMInternal
--- Copyright   :  (c) Amy de Buitléir 2012-2015
+-- Copyright   :  (c) Amy de Buitléir 2012-2016
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
diff --git a/src/Data/Datamining/Pattern.hs b/src/Data/Datamining/Pattern.hs
--- a/src/Data/Datamining/Pattern.hs
+++ b/src/Data/Datamining/Pattern.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Data.Datamining.Pattern
--- Copyright   :  (c) Amy de Buitléir 2012-2015
+-- Copyright   :  (c) Amy de Buitléir 2012-2016
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
diff --git a/test/Data/Datamining/Clustering/DSOMQC.hs b/test/Data/Datamining/Clustering/DSOMQC.hs
deleted file mode 100644
--- a/test/Data/Datamining/Clustering/DSOMQC.hs
+++ /dev/null
@@ -1,287 +0,0 @@
-------------------------------------------------------------------------
--- |
--- Module      :  Data.Datamining.Clustering.DSOMQC
--- Copyright   :  (c) Amy de Buitléir 2012-2015
--- License     :  BSD-style
--- Maintainer  :  amy@nualeargais.ie
--- Stability   :  experimental
--- Portability :  portable
---
--- Tests
---
-------------------------------------------------------------------------
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE CPP #-}
-{-# OPTIONS_GHC -fno-warn-type-defaults -fno-warn-orphans #-}
-
-module Data.Datamining.Clustering.DSOMQC
-  (
-    test
-  ) where
-
-import Data.Datamining.Pattern (euclideanDistanceSquared,
-  magnitudeSquared, adjustNum, absDifference)
-import Data.Datamining.Clustering.Classifier(classify,
-  classifyAndTrain, differences, diffAndTrain, models,
-  numModels, train, trainBatch)
-import Data.Datamining.Clustering.DSOMInternal
-
-#if MIN_VERSION_base(4,8,0)
-#else
-import Control.Applicative
-#endif
-
-import Data.List (sort)
-import Math.Geometry.Grid (size)
-import Math.Geometry.Grid.Hexagonal (HexHexGrid, hexHexGrid)
-import Math.Geometry.GridMap ((!), elems)
-import Math.Geometry.GridMap.Lazy (LGridMap, lazyGridMap)
-import Test.Framework as TF (Test, testGroup)
-import Test.Framework.Providers.QuickCheck2 (testProperty)
-import Test.QuickCheck ((==>), Gen, Arbitrary, arbitrary, choose,
-  Property, property, sized, suchThat, vectorOf, shrink)
-
-positive :: (Num a, Ord a, Arbitrary a) => Gen a
-positive = arbitrary `suchThat` (> 0)
-
-data RougierArgs
-  = RougierArgs Double Double Double Double Double deriving Show
-
-instance Arbitrary RougierArgs where
-  arbitrary = RougierArgs <$> choose (0,1) <*> choose (0,1)
-                <*> arbitrary <*> choose (0,1) <*> positive
-
-prop_rougierFunction_zero_if_perfect_model_exists :: RougierArgs -> Property
-prop_rougierFunction_zero_if_perfect_model_exists (RougierArgs r p _ diff dist) =
-  property $ rougierLearningFunction r p 0 diff dist == 0
-
-prop_rougierFunction_r_if_bmu_is_bad_model :: RougierArgs -> Property
-prop_rougierFunction_r_if_bmu_is_bad_model (RougierArgs r p _ _ _) =
-  property $ rougierLearningFunction r p 1 1 0 == r
-
-prop_rougierFunction_r_in_bounds :: RougierArgs -> Property
-prop_rougierFunction_r_in_bounds (RougierArgs r p bmuDiff diff dist) =
-  property $ 0 <= f && f <= 1
-  where f = rougierLearningFunction r p bmuDiff diff dist
-
-prop_rougierFunction_r_if_inelastic :: RougierArgs -> Property
-prop_rougierFunction_r_if_inelastic (RougierArgs r _ _ _ _) =
-  property $ rougierLearningFunction r 1.0 1.0 1.0 0 == r
-
-fractionDiff :: [Double] -> [Double] -> Double
-fractionDiff xs ys = if denom == 0 then 0 else d / denom
-  where d = sqrt $ euclideanDistanceSquared xs ys
-        denom = max xMag yMag
-        xMag = sqrt $ magnitudeSquared xs
-        yMag = sqrt $ magnitudeSquared ys
-
-approxEqual :: [TestPattern] -> [TestPattern] -> Bool
-approxEqual xs ys = fractionDiff xs' ys' <= 0.1
-  where xs' = map toDouble xs
-        ys' = map toDouble ys
-
--- We need to ensure that the absolute value of the difference
--- between any two test patterns is on the unit interval.
-
-newtype TestPattern = TestPattern {toDouble :: Double}
- deriving ( Eq, Ord, Show, Read)
-
-instance Arbitrary TestPattern where
-  arbitrary = fmap TestPattern $ choose (0,1)
-  shrink (TestPattern x) =
-    [ TestPattern x' | x' <- shrink x, x' >= 0, x' <= 1]
-
-testPatternDiff :: TestPattern -> TestPattern -> Double
-testPatternDiff (TestPattern a) (TestPattern b) = absDifference a b
-
-adjustTestPattern :: TestPattern -> Double -> TestPattern -> TestPattern
-adjustTestPattern (TestPattern target) r (TestPattern x)
-  = TestPattern $ adjustNum target r x
-
--- | A classifier and a training set. The training set will consist of
---   @j@ vectors of equal length, where @j@ is the number of patterns
---   the classifier can model. After running through the training set a
---   few times, the classifier should be very accurate at identifying
---   any of those @j@ vectors.
-data DSOMTestData
-  = DSOMTestData
-    {
-      som1 :: DSOM (LGridMap HexHexGrid) Double (Int, Int) TestPattern,
-      params1 :: RougierArgs,
-      trainingSet1 :: [TestPattern]
-    }
-
-instance Show DSOMTestData where
-  show s = "buildDSOMTestData " ++ show (size . gridMap . som1 $ s)
-    ++ " " ++ show (elems . gridMap . som1 $ s)
-    ++ " (" ++ show (params1 s) 
-    ++ ") " ++ show (trainingSet1 s) 
-
-buildDSOMTestData
-  :: Int -> [TestPattern] -> RougierArgs -> [TestPattern] -> DSOMTestData
-buildDSOMTestData len ps rp@(RougierArgs r p _ _ _) targets =
-  DSOMTestData s rp targets
-    where g = hexHexGrid len
-          gm = lazyGridMap g ps
-          fr = rougierLearningFunction r p
-          s = DSOM gm fr testPatternDiff adjustTestPattern
-
--- | Generate a classifier and a training set. The training set will
---   consist @j@ vectors of equal length, where @j@ is the number of
---   patterns the classifier can model. After running through the
---   training set a few times, the classifier should be very accurate at
---   identifying any of those @j@ vectors.
-sizedDSOMTestData :: Int -> Gen DSOMTestData
-sizedDSOMTestData n = do
-  sideLength <- choose (1, min (n+1) 5) --avoid long tests
-  let tileCount = 3*sideLength*(sideLength-1) + 1
-  let numberOfPatterns = tileCount
-  ps <- vectorOf numberOfPatterns arbitrary
-  rp <- arbitrary
-  targets <- vectorOf numberOfPatterns arbitrary
-  return $ buildDSOMTestData sideLength ps rp targets
-
-instance Arbitrary DSOMTestData where
-  arbitrary = sized sizedDSOMTestData
-
--- | If we use a fixed learning rate of one (regardless of the distance
---   from the BMU), and train a classifier once on one pattern, then all
---   nodes should match the input vector.
-prop_global_instant_training_works :: DSOMTestData -> Property
-prop_global_instant_training_works (DSOMTestData s _ xs) =
-  property $ finalModels `approxEqual` expectedModels
-    where x = head xs
-          gm = toGridMap s :: LGridMap HexHexGrid TestPattern
-          f _ _ _ = 1
-          s2 = DSOM gm f testPatternDiff adjustTestPattern
-          s3 = train s2 x
-          finalModels = models s3 :: [TestPattern]
-          expectedModels = replicate (numModels s) x :: [TestPattern]
-
-prop_training_works :: DSOMTestData -> Property
-prop_training_works (DSOMTestData s _ xs) = errBefore /= 0 ==>
-  errAfter < errBefore
-    where (bmu, s') = classifyAndTrain s x
-          x = head xs
-          errBefore = testPatternDiff x (gridMap s ! bmu)
-          errAfter = testPatternDiff x (gridMap s' ! bmu)
-
---   Invoking @diffAndTrain f s p@ should give identical results to
---   @(p `classify` s, train s f p)@.
-prop_classifyAndTrainEquiv :: DSOMTestData -> Property
-prop_classifyAndTrainEquiv (DSOMTestData s _ ps) = property $
-  bmu == s `classify` p && gridMap s1 == gridMap s2
-    where p = head ps
-          (bmu, s1) = classifyAndTrain s p
-          s2 = train s p
-
---   Invoking @diffAndTrain f s p@ should give identical results to
---   @(s `diff` p, train s f p)@.
-prop_diffAndTrainEquiv :: DSOMTestData -> Property
-prop_diffAndTrainEquiv (DSOMTestData s _ ps) = property $
-  diffs == s `differences` p && gridMap s1 == gridMap s2
-    where p = head ps
-          (diffs, s1) = diffAndTrain s p
-          s2 = train s p
-
---   Invoking @trainNeighbourhood s (classify s p) p@ should give
---   identical results to @train s p@.
-prop_trainNeighbourhoodEquiv :: DSOMTestData -> Property
-prop_trainNeighbourhoodEquiv (DSOMTestData s _ ps) = property $
-  gridMap s1 == gridMap s2
-    where p = head ps
-          s1 = trainNeighbourhood s (classify s p) p
-          s2 = train s p
-
--- | The training set consists of the same vectors in the same order,
---   several times over. So the resulting classifications should consist
---   of the same integers in the same order, over and over.
-prop_batch_training_works :: DSOMTestData -> Property
-prop_batch_training_works (DSOMTestData s _ xs) = property $
-  classifications == (concat . replicate 5) firstSet
-  where trainingSet = (concat . replicate 5) xs
-        s' = trainBatch s trainingSet
-        classifications = map (classify s') trainingSet
-        firstSet = take (length xs) classifications
-
-data SpecialDSOMTestData
-  = SpecialDSOMTestData
-    {
-      som2 :: DSOM (LGridMap HexHexGrid) Double (Int, Int) TestPattern,
-      params2 :: Double,
-      trainingSet2 :: [TestPattern]
-    }
-
-instance Show SpecialDSOMTestData where
-  show s = "buildDSOMTestData " ++ show (size . gridMap . som2 $ s)
-    ++ " " ++ show (elems . gridMap . som2 $ s)
-    ++ " (" ++ show (params2 s) 
-    ++ ") " ++ show (trainingSet2 s) 
-
-stepFunction :: Double -> Double -> Double -> Double -> Double
-stepFunction r _ _ d = if d == 0 then r else 0.0
-
-buildSpecialDSOMTestData
-  :: Int -> [TestPattern] -> Double -> [TestPattern] -> SpecialDSOMTestData
-buildSpecialDSOMTestData len ps r targets =
-  SpecialDSOMTestData s r targets
-    where g = hexHexGrid len
-          gm = lazyGridMap g ps
-          fr = stepFunction r
-          s = DSOM gm fr testPatternDiff adjustTestPattern
-
--- | Generate a classifier and a training set. The training set will
---   consist @j@ vectors of equal length, where @j@ is the number of
---   patterns the classifier can model. After running through the
---   training set a few times, the classifier should be very accurate at
---   identifying any of those @j@ vectors.
-sizedSpecialDSOMTestData :: Int -> Gen SpecialDSOMTestData
-sizedSpecialDSOMTestData n = do
-  sideLength <- choose (1, min (n+1) 5) --avoid long tests
-  let tileCount = 3*sideLength*(sideLength-1) + 1
-  let ps = map TestPattern $ take tileCount [0,100..]
-  r <- choose (0.001, 1)
-  let targets = map TestPattern $ take tileCount [5,105..]
-  return $ buildSpecialDSOMTestData sideLength ps r targets
-
-instance Arbitrary SpecialDSOMTestData where
-  arbitrary = sized sizedSpecialDSOMTestData
-
--- | If we train a classifier once on a set of patterns, where the
---   number of patterns in the set is equal to the number of nodes in
---   the classifier, then the classifier should become a better
---   representation of the training set. The initial models and training
---   set are designed to ensure that a single node will NOT train to
---   more than one pattern (which would render the test invalid).
-prop_batch_training_works2 :: SpecialDSOMTestData -> Property
-prop_batch_training_works2 (SpecialDSOMTestData s _ xs) =
-  errBefore /= 0 ==> errAfter < errBefore
-    where s' = trainBatch s xs
-          errBefore = euclideanDistanceSquared (map toDouble . sort $ xs) (map toDouble . sort . models $ s)
-          errAfter = euclideanDistanceSquared (map toDouble . sort $ xs) (map toDouble . sort . models $ s')
-
-test :: Test
-test = testGroup "QuickCheck Data.Datamining.Clustering.DSOM"
-  [
-    testProperty "prop_rougierFunction_zero_if_perfect_model_exists"
-      prop_rougierFunction_zero_if_perfect_model_exists,
-    testProperty "prop_rougierFunction_r_if_bmu_is_bad_model"
-      prop_rougierFunction_r_if_bmu_is_bad_model,
-    testProperty "prop_rougierFunction_r_if_inelastic"
-      prop_rougierFunction_r_if_inelastic,
-    testProperty "prop_rougierFunction_r_in_bounds"
-      prop_rougierFunction_r_in_bounds,
-    testProperty "prop_global_instant_training_works"
-      prop_global_instant_training_works,
-    testProperty "prop_training_works" prop_training_works,
-    testProperty "prop_classifyAndTrainEquiv"
-      prop_classifyAndTrainEquiv,
-    testProperty "prop_diffAndTrainEquiv" prop_diffAndTrainEquiv,
-    testProperty "prop_trainNeighbourhoodEquiv" prop_trainNeighbourhoodEquiv,
-    testProperty "prop_batch_training_works" prop_batch_training_works,
-    testProperty "prop_batch_training_works2"
-      prop_batch_training_works2
-  ]
diff --git a/test/Data/Datamining/Clustering/SGMQC.hs b/test/Data/Datamining/Clustering/SGMQC.hs
deleted file mode 100644
--- a/test/Data/Datamining/Clustering/SGMQC.hs
+++ /dev/null
@@ -1,241 +0,0 @@
-------------------------------------------------------------------------
--- |
--- Module      :  Data.Datamining.Clustering.SGMQC
--- Copyright   :  (c) Amy de Buitléir 2012-2015
--- License     :  BSD-style
--- Maintainer  :  amy@nualeargais.ie
--- Stability   :  experimental
--- Portability :  portable
---
--- Tests
---
-------------------------------------------------------------------------
-{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleInstances,
-    FlexibleContexts #-}
-{-# OPTIONS_GHC -fno-warn-type-defaults -fno-warn-orphans #-}
-
-module Data.Datamining.Clustering.SGMQC
-  (
-    test
-  ) where
-
-import Data.Datamining.Pattern (adjustNum, absDifference)
-import Data.Datamining.Clustering.SGMInternal
-import Data.List ((\\), minimumBy)
-import qualified Data.Map.Strict as M
-import Data.Ord (comparing)
-import Data.Word (Word16)
-import System.Random (Random)
-import Test.Framework as TF (Test, testGroup)
-import Test.Framework.Providers.QuickCheck2 (testProperty)
-import Test.QuickCheck ((==>), Gen, Arbitrary, Property, Positive,
-  arbitrary, shrink, choose, property, sized, suchThat, vectorOf,
-  getPositive)
-
-newtype UnitInterval a = UnitInterval {getUnitInterval :: a}
- deriving ( Eq, Ord, Show, Read)
-
-instance Functor UnitInterval where
-  fmap f (UnitInterval x) = UnitInterval (f x)
-
-instance (Num a, Ord a, Random a, Arbitrary a)
-    => Arbitrary (UnitInterval a) where
-  arbitrary = fmap UnitInterval $ choose (0,1)
-  shrink (UnitInterval x) =
-    [ UnitInterval x' | x' <- shrink x, x' >= 0, x' <= 1]
-
-prop_Exponential_starts_at_r0
-  :: UnitInterval Double -> Positive Double -> Property
-prop_Exponential_starts_at_r0 r0 d
-  = property $ abs (exponential r0' d' 0 - r0') < 0.01
-  where r0' = getUnitInterval r0
-        d' = getPositive d
-
-prop_Exponential_ge_0
-  :: UnitInterval Double -> Positive Double -> Positive Int -> Property
-prop_Exponential_ge_0 r0 d t = property $ exponential r0' d' t' >= 0
-  where r0' = getUnitInterval r0
-        d' = getPositive d
-        t' = getPositive t
-
-positive :: (Num a, Ord a, Arbitrary a) => Gen a
-positive = arbitrary `suchThat` (> 0)
-
-data TestSGM = TestSGM (SGM Int Double Word16 Double) String
-
-instance Show TestSGM where
-  show (TestSGM _ desc) = desc
-
-buildTestSGM
-  :: Double -> Double -> Int -> Double -> Bool -> [Double] -> TestSGM
-buildTestSGM r0 d maxSz dt ad ps = TestSGM s' desc
-  where lrf = exponential r0 d
-        s = makeSGM lrf maxSz dt ad absDifference adjustNum
-        desc = "buildTestSGM " ++ show r0 ++ " " ++ show d
-                 ++ " " ++ show maxSz
-                 ++ " " ++ show dt
-                 ++ " " ++ show ad
-                 ++ " " ++ show ps
-        s' = trainBatch s ps
-
-sizedTestSGM :: Int -> Gen TestSGM
-sizedTestSGM n = do
-  maxSz <- choose (1, n+1)
-  let numPatterns = n
-  r0 <- choose (0, 1)
-  d <- positive
-  dt <- choose (0, 1)
-  ad <- arbitrary
-  ps <- vectorOf numPatterns arbitrary
-  return $ buildTestSGM r0 d maxSz dt ad ps
-
-instance Arbitrary TestSGM where
-  arbitrary = sized sizedTestSGM
-
-prop_classify_chooses_best_fit :: TestSGM -> Double -> Property
-prop_classify_chooses_best_fit (TestSGM s _) x
-  = property $ bmu == fst (minimumBy (comparing snd) diffs)
-  where (bmu, _, diffs, _) = trainAndClassify s x
-
-prop_classify_never_creates_model :: TestSGM -> Double -> Property
-prop_classify_never_creates_model (TestSGM s _) x
-  = not (isEmpty s) ==> bmu `elem` (labels s)
-  where (bmu, _, _) = classify s x
-
-prop_trainNode_reduces_diff :: TestSGM -> Double -> Property
-prop_trainNode_reduces_diff (TestSGM s _) x = not (isEmpty s) ==>
-  diffAfter < diffBefore || diffBefore == 0
-                         || learningRate s (time s) < 1e-10
-  where (bmu, diffBefore, _) = classify s x
-        s2 = trainNode s bmu x
-        (_, diffAfter, _) = classify s2 x
-
-prop_diff_lt_threshold_after_training :: TestSGM -> Double -> Property
-prop_diff_lt_threshold_after_training (TestSGM s _) x =
-  numModels s < maxSize s ==> diffAfter < diffThreshold s
-  where (_, _, _, s') = trainAndClassify s x
-        (_, diffAfter, _) = classify s' x
-
-prop_training_reduces_diff :: TestSGM -> Double -> Property
-prop_training_reduces_diff (TestSGM s _) x = not (isEmpty s) ==>
-  diffAfter < diffBefore || diffBefore == 0
-                         || learningRate s (time s) < 1e-10
-  where (_, diffBefore, _) = classify s x
-        s2 = train s x
-        (_, diffAfter, _) = classify s2 x
-
--- TODO prop: map will never exceed maxSize
-
-prop_train_only_modifies_one_model
-  :: TestSGM -> Double -> Property
-prop_train_only_modifies_one_model (TestSGM s _) p
-  = numModels s < maxSize s ==> otherModelsBefore == otherModelsAfter
-    where (bmu, _, _, s2) = trainAndClassify s p
-          otherModelsBefore = M.delete bmu . M.map fst . toMap $ s
-          otherModelsAfter = M.delete bmu . M.map fst . toMap $ s2
-
-prop_train_increments_counter :: TestSGM -> Double -> Property
-prop_train_increments_counter (TestSGM s _) x
-  = numModels s < maxSize s ==> countAfter == countBefore + 1
-  -- We have to check if the SGM is full, otherwise we'll replace an
-  -- existing model (and its counter), which means that the total
-  -- count could change by an arbitrary amount.
-  where countBefore = time s
-        countAfter = time $ train s x
-
--- | The training set consists of the same vectors in the same order,
---   several times over. So the resulting classifications should consist
---   of the same integers in the same order, over and over.
-prop_batch_training_works :: TestSGM -> [Double] -> Property
-prop_batch_training_works (TestSGM s _) ps
-  -- = maxSize s > length ps
-  --   ==> classifications == (concat . replicate 5) firstSet
-  = property $ classifications == (concat . replicate 5) firstSet
-  where trainingSet = (concat . replicate 5) ps
-        sRightSize = if maxSize s >= length ps
-          then s
-          else s { maxSize=length ps + 1}
-        s' = trainBatch sRightSize trainingSet
-        classifications = map (justBMU . classify s') trainingSet
-        justBMU = \(bmu, _, _) -> bmu
-        firstSet = take (length ps) classifications
-
--- | WARNING: This can fail when two nodes are close enough in
---   value so that after training they become identical.
-prop_classification_is_consistent :: TestSGM -> Double -> Property
-prop_classification_is_consistent (TestSGM s _) x
-  = property $ bmu == bmu'
-  where (bmu, _, _, s2) = trainAndClassify s x
-        (bmu', _, _) = classify s2 x
-
-prop_classification_results_are_consistent
-  :: TestSGM -> Double -> Property
-prop_classification_results_are_consistent (TestSGM s _) x
-  = property $ bmu == fst (minimumBy (comparing snd) diffs)
-  where (bmu, _, diffs, _) = trainAndClassify s x
-
-prop_classification_results_are_consistent2
-  :: TestSGM -> Double -> Property
-prop_classification_results_are_consistent2 (TestSGM s _) x
-  = property $ bmuDiff == snd (minimumBy (comparing snd) diffs)
-  where (_, bmuDiff, diffs, _) = trainAndClassify s x
-
-prop_classification_stabilises :: TestSGM -> [Double] -> Property
-prop_classification_stabilises (TestSGM s _)  ps
-  = (not . null $ ps) && maxSize s > length ps ==> k2 == k1
-  where sStable = trainBatch s . concat . replicate 10 $ ps
-        (k1, _, _, sStable2) = trainAndClassify sStable (head ps)
-        sStable3 = trainBatch sStable2 ps
-        (k2, _, _) = classify sStable3 (head ps)
-
-prop_models_not_deleted_unless_allowed
-  :: TestSGM -> Double -> Property
-prop_models_not_deleted_unless_allowed (TestSGM s _) x =
-  (not . allowDeletion $ s) ==> null (labelsBefore \\ labelsAfter)
-  where labelsBefore = M.keys $ modelMap s
-        labelsAfter = M.keys $ modelMap s'
-        (_, _, _, s') = trainAndClassify s x
-
-prop_models_not_deleted_unless_allowed2
-  :: TestSGM -> Double -> Property
-prop_models_not_deleted_unless_allowed2 (TestSGM s _) x =
-  (not . allowDeletion $ s) ==> null (labelsBefore \\ labelsAfter)
-  where labelsBefore = M.keys $ modelMap s
-        labelsAfter = M.keys $ modelMap s'
-        s' = train s x
-
-test :: Test
-test = testGroup "QuickCheck Data.Datamining.Clustering.SGM"
-  [
-    testProperty "prop_Exponential_starts_at_r0"
-      prop_Exponential_starts_at_r0,
-    testProperty "prop_Exponential_ge_0"
-      prop_Exponential_ge_0,
-    testProperty "prop_classify_chooses_best_fit"
-      prop_classify_chooses_best_fit,
-    testProperty "prop_classify_never_creates_model"
-      prop_classify_never_creates_model,
-    testProperty "prop_trainNode_reduces_diff"
-      prop_trainNode_reduces_diff,
-    testProperty "prop_diff_lt_threshold_after_training"
-      prop_diff_lt_threshold_after_training,
-    testProperty "prop_training_reduces_diff"
-      prop_training_reduces_diff,
-    testProperty "prop_train_only_modifies_one_model"
-      prop_train_only_modifies_one_model,
-    testProperty "prop_train_increments_counter"
-      prop_train_increments_counter,
-    testProperty "prop_batch_training_works" prop_batch_training_works,
-    testProperty "prop_classification_is_consistent"
-      prop_classification_is_consistent,
-    testProperty "prop_classification_results_are_consistent"
-      prop_classification_results_are_consistent,
-    testProperty "prop_classification_results_are_consistent2"
-      prop_classification_results_are_consistent2,
-    testProperty "prop_classification_stabilises"
-      prop_classification_stabilises,
-    testProperty "prop_models_not_deleted_unless_allowed"
-      prop_models_not_deleted_unless_allowed,
-    testProperty "prop_models_not_deleted_unless_allowed2"
-      prop_models_not_deleted_unless_allowed2    
-  ]
diff --git a/test/Data/Datamining/Clustering/SOMQC.hs b/test/Data/Datamining/Clustering/SOMQC.hs
deleted file mode 100644
--- a/test/Data/Datamining/Clustering/SOMQC.hs
+++ /dev/null
@@ -1,338 +0,0 @@
-------------------------------------------------------------------------
--- |
--- Module      :  Data.Datamining.Clustering.SOMQC
--- Copyright   :  (c) Amy de Buitléir 2012-2015
--- License     :  BSD-style
--- Maintainer  :  amy@nualeargais.ie
--- Stability   :  experimental
--- Portability :  portable
---
--- Tests
---
-------------------------------------------------------------------------
-{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, FlexibleInstances,
-    FlexibleContexts #-}
-{-# OPTIONS_GHC -fno-warn-type-defaults -fno-warn-orphans #-}
-
-module Data.Datamining.Clustering.SOMQC
-  (
-    test
-  ) where
-
-import Data.Datamining.Pattern (euclideanDistanceSquared,
-  magnitudeSquared, adjustNum, absDifference)
-import Data.Datamining.Clustering.Classifier(classify,
-  classifyAndTrain, reportAndTrain, differences, diffAndTrain, models,
-  numModels, train, trainBatch)
-import Data.Datamining.Clustering.SOMInternal
-
-import Data.List (sort)
-import Math.Geometry.Grid (size)
-import Math.Geometry.Grid.Hexagonal (HexHexGrid, hexHexGrid)
-import Math.Geometry.GridMap ((!), elems)
-import Math.Geometry.GridMap.Lazy (LGridMap, lazyGridMap)
-import System.Random (Random)
-import Test.Framework as TF (Test, testGroup)
-import Test.Framework.Providers.QuickCheck2 (testProperty)
-import Test.QuickCheck ((==>), Gen, Arbitrary, arbitrary, choose,
-  Property, property, sized, suchThat, vectorOf)
-
-positive :: (Num a, Ord a, Arbitrary a) => Gen a
-positive = arbitrary `suchThat` (> 0)
-
-data DecayingGaussianParams a = DecayingGaussianParams a a a a a
-  deriving (Eq, Show)
-
-instance
-  (Random a, Num a, Ord a, Arbitrary a)
-  => Arbitrary (DecayingGaussianParams a) where
-  arbitrary = do
-    r0 <- choose (0,1)
-    rf <- choose (0,r0)
-    w0 <- positive
-    wf <- choose (0,w0)
-    tf <- positive
-    return $ DecayingGaussianParams r0 rf w0 wf tf
-
-prop_DecayingGaussian_starts_at_r0
-  :: DecayingGaussianParams Double -> Property
-prop_DecayingGaussian_starts_at_r0 (DecayingGaussianParams r0 rf w0 wf tf)
-  = property $ abs ((decayingGaussian r0 rf w0 wf tf 0 0) - r0) < 0.01
-
-prop_DecayingGaussian_starts_at_w0
-  :: DecayingGaussianParams Double -> Property
-prop_DecayingGaussian_starts_at_w0 (DecayingGaussianParams r0 rf w0 wf tf)
-  = property $
-    decayingGaussian r0 rf w0 wf tf 0 inside >= r0 * exp (-0.5)
-      && decayingGaussian r0 rf w0 wf tf 0 outside < r0 * exp (-0.5)
-  where inside = w0 * 0.99999
-        outside = w0 * 1.00001
-
-prop_DecayingGaussian_decays_to_rf
-  :: DecayingGaussianParams Double -> Property
-prop_DecayingGaussian_decays_to_rf (DecayingGaussianParams r0 rf w0 wf tf)
-  = property $ abs ((decayingGaussian r0 rf w0 wf tf tf 0) - rf) < 0.01
-
-prop_DecayingGaussian_shrinks_to_wf
-  :: DecayingGaussianParams Double -> Property
-prop_DecayingGaussian_shrinks_to_wf (DecayingGaussianParams r0 rf w0 wf tf)
-  = property $
-    decayingGaussian r0 rf w0 wf tf tf inside >= rf * exp (-0.5)
-      && decayingGaussian r0 rf w0 wf tf tf outside < rf * exp (-0.5)
-  where inside = wf * 0.99999
-        outside = wf * 1.00001
-
-fractionDiff :: [Double] -> [Double] -> Double
-fractionDiff xs ys = if denom == 0 then 0 else d / denom
-  where d = sqrt $ euclideanDistanceSquared xs ys
-        denom = max xMag yMag
-        xMag = sqrt $ magnitudeSquared xs
-        yMag = sqrt $ magnitudeSquared ys
-
-approxEqual :: [Double] -> [Double] -> Bool
-approxEqual xs ys = fractionDiff xs ys <= 0.1
-
--- | A classifier and a training set. The training set will consist of
---   @j@ vectors of equal length, where @j@ is the number of patterns
---   the classifier can model. After running through the training set a
---   few times, the classifier should be very accurate at identifying
---   any of those @j@ vectors.
-data SOMTestData
-  = SOMTestData
-    {
-      som1 :: SOM Double Double (LGridMap HexHexGrid) Double (Int, Int) Double,
-      params1 :: DecayingGaussianParams Double,
-      trainingSet1 :: [Double]
-    }
-
-instance Show SOMTestData where
-  show s = "buildSOMTestData " ++ show (size . gridMap . som1 $ s)
-    ++ " " ++ show (elems . gridMap . som1 $ s)
-    ++ " (" ++ show (params1 s) 
-    ++ ") " ++ show (trainingSet1 s) 
-
-buildSOMTestData
-  :: Int -> [Double] -> DecayingGaussianParams Double
-     -> [Double] -> SOMTestData
-buildSOMTestData len ps p@(DecayingGaussianParams r0 rf w0 wf tf) targets =
-  SOMTestData s p targets
-    where g = hexHexGrid len
-          gm = lazyGridMap g ps
-          fr = decayingGaussian r0 rf w0 wf tf
-          s = SOM gm fr absDifference adjustNum 0
-
-sizedSOMTestData :: Int -> Gen SOMTestData
-sizedSOMTestData n = do
-  sideLength <- choose (1, min (n+1) 5) --avoid long tests
-  let tileCount = 3*sideLength*(sideLength-1) + 1
-  let numberOfPatterns = tileCount
-  ps <- vectorOf numberOfPatterns arbitrary
-  r0 <- choose (0, 1)
-  rf <- choose (0, r0)
-  w0 <- choose (0, fromIntegral sideLength)
-  wf <- choose (0, w0)
-  tf <- choose (1, 10)
-  targets <- vectorOf numberOfPatterns arbitrary
-  return $ buildSOMTestData sideLength ps (DecayingGaussianParams r0 rf w0 wf tf) targets
-
-instance Arbitrary SOMTestData where
-  arbitrary = sized sizedSOMTestData
-
--- | If we use a fixed learning rate of one (regardless of the distance
---   from the BMU), and train a classifier once on one pattern, then all
---   nodes should match the input vector.
-prop_global_instant_training_works :: SOMTestData -> Property
-prop_global_instant_training_works (SOMTestData s _ xs) =
-  property $ finalModels `approxEqual` expectedModels
-    where x = head xs
-          gm = toGridMap s :: LGridMap HexHexGrid Double
-          f _ _ = 1
-          s2 = SOM gm f absDifference adjustNum 0
-          s3 = train s2 x
-          finalModels = models s3 :: [Double]
-          expectedModels = replicate (numModels s) x :: [Double]
-
-prop_training_reduces_error :: SOMTestData -> Property
-prop_training_reduces_error (SOMTestData s _ xs) = errBefore /= 0 ==>
-  errAfter < errBefore
-    where (bmu, s') = classifyAndTrain s x
-          x = head xs
-          errBefore = abs $ x - (gridMap s ! bmu)
-          errAfter = abs $ x - (gridMap s' ! bmu)
-
---   Invoking @diffAndTrain f s p@ should give identical results to
---   @(p `classify` s, train s f p)@.
-prop_classifyAndTrainEquiv :: SOMTestData -> Property
-prop_classifyAndTrainEquiv (SOMTestData s _ ps) = property $
-  bmu == s `classify` p && gridMap s1 == gridMap s2
-    where p = head ps
-          (bmu, s1) = classifyAndTrain s p
-          s2 = train s p
-
---   Invoking @diffAndTrain f s p@ should give identical results to
---   @(s `diff` p, train s f p)@.
-prop_diffAndTrainEquiv :: SOMTestData -> Property
-prop_diffAndTrainEquiv (SOMTestData s _ ps) = property $
-  diffs == s `differences` p && gridMap s1 == gridMap s2
-    where p = head ps
-          (diffs, s1) = diffAndTrain s p
-          s2 = train s p
-
---   Invoking @trainNeighbourhood s (classify s p) p@ should give
---   identical results to @train s p@.
-prop_trainNeighbourhoodEquiv :: SOMTestData -> Property
-prop_trainNeighbourhoodEquiv (SOMTestData s _ ps) = property $
-  gridMap s1 == gridMap s2
-    where p = head ps
-          s1 = trainNeighbourhood s (classify s p) p
-          s2 = train s p
-
--- | The training set consists of the same vectors in the same order,
---   several times over. So the resulting classifications should consist
---   of the same integers in the same order, over and over.
-prop_batch_training_works :: SOMTestData -> Property
-prop_batch_training_works (SOMTestData s _ xs) = property $
-  classifications == (concat . replicate 5) firstSet
-  where trainingSet = (concat . replicate 5) xs
-        s' = trainBatch s trainingSet
-        classifications = map (classify s') trainingSet
-        firstSet = take (length xs) classifications
-
--- | WARNING: This can fail when two nodes are close enough in
---   value so that after training they become identical.
---   This only happens rarely, so if the test fails, try again.
-prop_classification_is_consistent
-  :: SOMTestData -> Property
-prop_classification_is_consistent (SOMTestData s _ (x:_))
-  = property $ bmu == bmu'
-  where (bmu, _, s') = reportAndTrain s x
-        (bmu', _, _) = reportAndTrain s' x
-prop_classification_is_consistent _ = error "Should not happen"
-
--- | Same as SOMTestData, except that the initial models and training
---   set are designed to ensure that a single node will NOT train to
---   more than one pattern.
-data SpecialSOMTestData
-  = SpecialSOMTestData
-    {
-      som2 :: SOM Int Int (LGridMap HexHexGrid) Double (Int, Int) Double,
-      params2 :: Double,
-      trainingSet2 :: [Double]
-    }
-
-instance Show SpecialSOMTestData where
-  show s = "buildSpecialSOMTestData " ++ show (size . gridMap . som2 $ s)
-    ++ " " ++ show (elems . gridMap . som2 $ s)
-    ++ " " ++ show (params2 s) 
-    ++ " " ++ show (trainingSet2 s) 
-
-buildSpecialSOMTestData
-  :: Int -> [Double] -> Double -> [Double] -> SpecialSOMTestData
-buildSpecialSOMTestData len ps r targets =
-  SpecialSOMTestData s r targets
-    where g = hexHexGrid len
-          gm = lazyGridMap g ps
-          s = SOM gm (stepFunction r) absDifference adjustNum 0
-
-sizedSpecialSOMTestData :: Int -> Gen SpecialSOMTestData
-sizedSpecialSOMTestData n = do
-  sideLength <- choose (1, min (n+1) 5) --avoid long tests
-  let tileCount = 3*sideLength*(sideLength-1) + 1
-  let ps = take tileCount [0,100..]
-  r <- choose (0.001, 1)
-  let targets = take tileCount [5,105..]
-  return $ buildSpecialSOMTestData sideLength ps r targets
-
-instance Arbitrary SpecialSOMTestData where
-  arbitrary = sized sizedSpecialSOMTestData
-
--- | If we train a classifier once on a set of patterns, where the
---   number of patterns in the set is equal to the number of nodes in
---   the classifier, then the classifier should become a better
---   representation of the training set. The initial models and training
---   set are designed to ensure that a single node will NOT train to
---   more than one pattern (which would render the test invalid).
-prop_batch_training_works2 :: SpecialSOMTestData -> Property
-prop_batch_training_works2 (SpecialSOMTestData s _ xs) =
-  errBefore /= 0 ==> errAfter < errBefore
-    where s' = trainBatch s xs
-          errBefore = euclideanDistanceSquared (sort xs) (sort (models s))
-          errAfter = euclideanDistanceSquared (sort xs) (sort (models s'))
-
-data IncompleteSOMTestData
-  = IncompleteSOMTestData
-    {
-      som3 :: SOM Double Double (LGridMap HexHexGrid) Double (Int, Int) Double,
-      params3 :: DecayingGaussianParams Double,
-      trainingSet3 :: [Double]
-    }
-
-instance Show IncompleteSOMTestData where
-  show s = "buildIncompleteSOMTestData " ++ show (size . gridMap . som3 $ s)
-    ++ " " ++ show (elems . gridMap . som3 $ s)
-    ++ " " ++ show (params3 s) 
-    ++ " " ++ show (trainingSet3 s) 
-
-buildIncompleteSOMTestData
-  :: Int -> [Double] -> DecayingGaussianParams Double
-     -> [Double] -> IncompleteSOMTestData
-buildIncompleteSOMTestData len ps p@(DecayingGaussianParams r0 rf w0 wf tf) targets =
-  IncompleteSOMTestData s p targets
-    where g = hexHexGrid len
-          gm = lazyGridMap g ps
-          fr = decayingGaussian r0 rf w0 wf tf
-          s = SOM gm fr absDifference adjustNum 0
-
--- | Same as sizedSOMTestData, except some nodes don't have a value.
-sizedIncompleteSOMTestData :: Int -> Gen IncompleteSOMTestData
-sizedIncompleteSOMTestData n = do
-  sideLength <- choose (2, min (n+2) 5) --avoid long tests
-  let tileCount = 3*sideLength*(sideLength-1) + 1
-  numberOfPatterns <- choose (1,tileCount-1)
-  ps <- vectorOf numberOfPatterns arbitrary
-  r0 <- choose (0, 1)
-  rf <- choose (0, r0)
-  w0 <- choose (0, fromIntegral sideLength)
-  wf <- choose (0, w0)
-  tf <- choose (1, 10)
-  targets <- vectorOf numberOfPatterns arbitrary
-  return $ buildIncompleteSOMTestData sideLength ps (DecayingGaussianParams r0 rf w0 wf tf) targets
-
-instance Arbitrary IncompleteSOMTestData where
-  arbitrary = sized sizedIncompleteSOMTestData
-
-prop_can_train_incomplete_SOM :: IncompleteSOMTestData -> Property
-prop_can_train_incomplete_SOM (IncompleteSOMTestData s _ xs) = errBefore /= 0 ==>
-  errAfter < errBefore
-    where (bmu, s') = classifyAndTrain s x
-          x = head xs
-          errBefore = abs $ x - (gridMap s ! bmu)
-          errAfter = abs $ x - (gridMap s' ! bmu)
-
-test :: Test
-test = testGroup "QuickCheck Data.Datamining.Clustering.SOM"
-  [
-    testProperty "prop_DecayingGaussian_starts_at_r0"
-      prop_DecayingGaussian_starts_at_r0,
-    testProperty "prop_DecayingGaussian_starts_at_w0"
-      prop_DecayingGaussian_starts_at_w0,
-    testProperty "prop_DecayingGaussian_decays_to_rf"
-      prop_DecayingGaussian_decays_to_rf,
-    testProperty "prop_DecayingGaussian_shrinks_to_wf"
-      prop_DecayingGaussian_shrinks_to_wf,
-    testProperty "prop_global_instant_training_works"
-      prop_global_instant_training_works,
-    testProperty "prop_training_reduces_error"
-      prop_training_reduces_error,
-    testProperty "prop_classifyAndTrainEquiv"
-      prop_classifyAndTrainEquiv,
-    testProperty "prop_diffAndTrainEquiv" prop_diffAndTrainEquiv,
-    testProperty "prop_trainNeighbourhoodEquiv" prop_trainNeighbourhoodEquiv,
-    testProperty "prop_batch_training_works" prop_batch_training_works,
-    testProperty "prop_classification_is_consistent"
-      prop_classification_is_consistent,
-    testProperty "prop_batch_training_works2"
-      prop_batch_training_works2,
-    testProperty "prop_can_train_incomplete_SOM"
-      prop_can_train_incomplete_SOM
-  ]
diff --git a/test/Data/Datamining/PatternQC.hs b/test/Data/Datamining/PatternQC.hs
deleted file mode 100644
--- a/test/Data/Datamining/PatternQC.hs
+++ /dev/null
@@ -1,87 +0,0 @@
-------------------------------------------------------------------------
--- |
--- Module      :  Data.Datamining.PatternQC
--- Copyright   :  (c) Amy de Buitléir 2012-2015
--- License     :  BSD-style
--- Maintainer  :  amy@nualeargais.ie
--- Stability   :  experimental
--- Portability :  portable
---
--- Tests
---
-------------------------------------------------------------------------
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE CPP #-}
-{-# OPTIONS_GHC -fno-warn-type-defaults -fno-warn-orphans #-}
-
-module Data.Datamining.PatternQC
-  (
-    test
-  ) where
-
-import Data.Datamining.Pattern
-
-import Test.Framework as TF (Test, testGroup)
-import Test.Framework.Providers.QuickCheck2 (testProperty)
-import Test.QuickCheck ((==>), Gen, Arbitrary, arbitrary, choose, 
-  Property, property, sized, vector)
-
-#if MIN_VERSION_base(4,8,0)
-#else
-import Control.Applicative
-#endif
-
-newtype UnitInterval = FromDouble Double deriving Show
-
-instance Arbitrary UnitInterval where
-  arbitrary = FromDouble <$> choose (0,1)
-
-prop_adjustVector_doesnt_choke_on_infinite_lists ::
-  [Double] -> UnitInterval -> Property
-prop_adjustVector_doesnt_choke_on_infinite_lists xs (FromDouble d) = 
-  property $ 
-    length (adjustVector xs d [0,1..]) == length xs
-
-data TwoVectorsSameLength = TwoVectorsSameLength [Double] [Double] 
-  deriving Show
-
-sizedTwoVectorsSameLength :: Int -> Gen TwoVectorsSameLength
-sizedTwoVectorsSameLength n = 
-  TwoVectorsSameLength <$> vector n <*> vector n
-
-instance Arbitrary TwoVectorsSameLength where
-  arbitrary = sized sizedTwoVectorsSameLength
-
-prop_zero_adjustment_is_no_adjustment :: 
-  TwoVectorsSameLength -> Property
-prop_zero_adjustment_is_no_adjustment (TwoVectorsSameLength xs ys) = 
-  property $ adjustVector xs 0 ys == ys
-
-prop_full_adjustment_gives_perfect_match :: 
-  TwoVectorsSameLength -> Property
-prop_full_adjustment_gives_perfect_match (TwoVectorsSameLength xs ys) = 
-  property $ adjustVector xs 1 ys == xs
-
-prop_adjustVector_improves_similarity :: 
-  TwoVectorsSameLength -> UnitInterval -> Property
-prop_adjustVector_improves_similarity 
-  (TwoVectorsSameLength xs ys) (FromDouble a) = 
-    a > 0 && a < 1 && not (null xs) ==> d2 < d1
-      where d1 = euclideanDistanceSquared xs ys
-            d2 = euclideanDistanceSquared xs ys'
-            ys' = adjustVector xs a ys
-
-test :: Test
-test = testGroup "QuickCheck Data.Datamining.Clustering.PatternQC"
-  [
-    testProperty "prop_adjustVector_doesnt_choke_on_infinite_lists"
-      prop_adjustVector_doesnt_choke_on_infinite_lists,
-    testProperty "prop_zero_adjustment_is_no_adjustment"
-      prop_zero_adjustment_is_no_adjustment,
-    testProperty "prop_full_adjustment_gives_perfect_match"
-      prop_full_adjustment_gives_perfect_match,
-    testProperty "prop_adjustVector_improves_similarity"
-      prop_adjustVector_improves_similarity
-  ]
-
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,7 +1,7 @@
 ------------------------------------------------------------------------
 -- |
 -- Module      :  Main
--- Copyright   :  (c) Amy de Buitléir 2012-2015
+-- Copyright   :  (c) Amy de Buitléir 2012-2016
 -- License     :  BSD-style
 -- Maintainer  :  amy@nualeargais.ie
 -- Stability   :  experimental
