module Algorithms.Lloyd.StrategiesSpec (spec) where

import Prelude hiding (take, zipWith, length, head)
import Algorithms.Lloyd.Strategies (Point(..), Cluster(..), step)
import Algorithms.Lloyd.SequentialSpec (y)
import Control.Applicative ((<$>),(<*>))
import Data.Metric (Euclidean(..))
import Data.Random.Normal (mkNormals)
import Data.Vector (Vector, generate, fromList, take, zipWith, length, head)
import Data.Vector.Split (chunkInto)
import Test.Hspec (Spec(..), describe, it)
import Test.QuickCheck (property, Arbitrary(..), choose)

data Structure = Structure
  { points   :: Vector (Vector Point)
  , clusters :: Vector Cluster
  } deriving (Show)

instance Arbitrary Structure where
  arbitrary = do
    normals   <- mkNormals <$> arbitrary
    (a0,a1,k) <- (,,) <$> choose (1,200) <*> choose (1,200) <*> choose (1,200)
    let points   = chunkInto a0 . generate a1 $ \n -> Point $ fromList [normals !! n, normals !! (n*2)]
        clusters = zipWith Cluster (fromList [0..k-1]) (take k $ head points)
    return $ Structure points clusters

spec :: Spec
spec = do
  describe "step" . it "Cluster length should be invariant upon repeated application." . property $ \(Structure points clusters) -> do
    let fixedPoint = y (step Euclidean `flip` points) clusters
    length clusters == length fixedPoint