-- | Perform K-means clustering, using a feature extraction function
kmeansBy :: G.Vector v a
         => Int                   -- ^ The number of clusters
         -> v a                   -- ^ Input data
         -> (a -> U.Vector Double)
         -> KMeansOpts
         -> KMeans a
kmeansBy k dat fn opts
    | containNaN = error "Input data contains NaN."
    | otherwise = KMeans member cs grps sse'
  where
    containNaN = G.foldl (\acc x -> acc || U.any isNaN (fn x)) False dat
    (member, cs, sse') = kmeans' initial (kmeansMaxIter opts) dat fn
    grps = if kmeansClusters opts
        then Just $ decode member $ G.toList dat
        else Nothing
    initial = runST $ do
        gen <- initialize $ kmeansSeed opts
        case kmeansMethod opts of
            Forgy -> forgy gen k dat fn
            KMeansPP -> kmeansPP gen k dat fn
            Centers c -> return c

kmeansBy :: G.Vector v a
         => Int                   -- ^ The number of clusters
         -> v a                   -- ^ Input data
         -> (a -> U.Vector Double)
         -> KMeansOpts
         -> KMeans a
kmeansBy k dat fn opts
    | containNaN = error "Input data contains NaN."
    | otherwise = KMeans member cs grps sse'
  where
    containNaN = G.foldl (\acc x -> acc || U.any isNaN (fn x)) False dat
    (member, cs, sse') = kmeans' initial (kmeansMaxIter opts) dat fn
    grps = if kmeansClusters opts
        then Just $ decode member $ G.toList dat
        else Nothing
    initial = runST $ do
        gen <- initialize $ kmeansSeed opts
        case kmeansMethod opts of
            Forgy -> forgy gen k dat fn
            KMeansPP -> kmeansPP gen k dat fn
            Centers c -> return c

kmeansBy k dat fn opts
    | containNaN = error "Input data contains NaN."
    | otherwise = KMeans member cs grps sse'
  where
    containNaN = G.foldl (\acc x -> acc || U.any isNaN (fn x)) False dat
    (member, cs, sse') = kmeans' initial (kmeansMaxIter opts) dat fn
    grps = if kmeansClusters opts
        then Just $ decode member $ G.toList dat
        else Nothing
    initial = runST $ do
        gen <- initialize $ kmeansSeed opts
        case kmeansMethod opts of
            Forgy -> forgy gen k dat fn
            KMeansPP -> kmeansPP gen k dat fn
            Centers c -> return c
