public KMeansFunctionRecognitionAlgorithm(ClusteringSettings settings)
        {
            this.Settings        = settings;
            this.Score           = new KMeansFunctionRecognitonScore();
            this.Score.Centroids = new double[settings.NumberOfClusters][];

            if (this.Settings.FuncRecogMethod == 1)
            {
                this.Score.InClusterMaxDistance = new double[settings.NumberOfClusters];
                this.Score.MaxCentroid          = null;
                this.Score.MinCentroid          = null;

                for (int i = 0; i < settings.NumberOfClusters; i++)
                {
                    this.Score.Centroids[i] = new double[settings.NumOfDimensions];
                }
            }
            else
            {
                this.Score.InClusterMaxDistance = null;
                this.Score.MaxCentroid          = new double[settings.NumberOfClusters][];
                this.Score.MinCentroid          = new double[settings.NumberOfClusters][];

                for (int i = 0; i < settings.NumberOfClusters; i++)
                {
                    this.Score.MaxCentroid[i] = new double[settings.NumOfDimensions];
                    this.Score.MinCentroid[i] = new double[settings.NumOfDimensions];
                    this.Score.Centroids[i]   = new double[settings.NumOfDimensions];

                    for (int dim = 0; dim < settings.NumOfDimensions; dim++)
                    {
                        this.Score.MaxCentroid[i][dim] = double.MinValue;
                        this.Score.MinCentroid[i][dim] = double.MaxValue;
                    }
                }
            }
        }
        public static int OptimalNumberOfClusters_TwoFunctions(double[][] functions1, double[][] functions2, ClusteringSettings settings, int MinNumClusters, int MaxNumClusters, out double[] Fmins_k)
        {
            if (MinNumClusters < 2)
            {
                MinNumClusters = 2;
            }

            double[][] oneFunction = new double[settings.NumOfDimensions][];
            int        numFun      = 0;
            double     dist;
            int        score = 0;
            KMeansFunctionRecognitionAlgorithm kMeansFR1, kMeansFR2;
            KMeansFunctionRecognitonScore      res1, res2;

            double Fmax = 0;
            double Fmin = double.MaxValue;

            double[] Fmins;
            Fmins_k = new double[MaxNumClusters - MinNumClusters + 1];
            double F       = 0;
            int    cluster = -1;

            for (int k = MinNumClusters; k <= MaxNumClusters; k++)
            {
                settings.InitialCentroids = null;
                settings.NumberOfClusters = k;

                kMeansFR1 = new KMeansFunctionRecognitionAlgorithm(settings);
                kMeansFR2 = new KMeansFunctionRecognitionAlgorithm(settings);
                res1      = new KMeansFunctionRecognitonScore();
                res2      = new KMeansFunctionRecognitonScore();

                numFun = functions1.Length / settings.NumOfDimensions;
                for (int f = 0; f < numFun; f++)
                {
                    oneFunction = KMeansAlgorithm.transposeFunction(KMeansAlgorithm.selectFunction(functions1, f + 1, settings.NumOfDimensions));
                    res1        = kMeansFR1.Run(oneFunction, null) as KMeansFunctionRecognitonScore;
                }

                numFun = functions2.Length / settings.NumOfDimensions;

                settings.InitialCentroids = null;
                for (int f = 0; f < numFun; f++)
                {
                    oneFunction = KMeansAlgorithm.transposeFunction(KMeansAlgorithm.selectFunction(functions2, f + 1, settings.NumOfDimensions));
                    res2        = kMeansFR2.Run(oneFunction, null) as KMeansFunctionRecognitonScore;
                }

                Fmins = new double[k];
                // check if there is a non intersection cluster
                for (int i = 0; i < k; i++)
                {
                    Fmin  = double.MaxValue;
                    score = 0;
                    for (int j = 0; j < k; j++)
                    {
                        dist = KMeansAlgorithm.calculateDistance(res1.Centroids[i], res2.Centroids[j]);
                        if (dist > res1.InClusterMaxDistance[i] + res2.InClusterMaxDistance[j])
                        {
                            score++;
                        }
                    }

                    if (score == k)
                    {
                        //calculate F;
                        for (int j = 0; j < k; j++)
                        {
                            F = KMeansAlgorithm.calculateDistance(res1.Centroids[i], res2.Centroids[j]) / (res1.InClusterMaxDistance[i] + res2.InClusterMaxDistance[j]);
                            // select min F among the two functions
                            if (F < Fmin)
                            {
                                Fmin = F;
                            }
                        }
                    }
                    //save Fmin of each centroid
                    if (Fmin != double.MaxValue)
                    {
                        Fmins[i] = Fmin;
                    }
                }
                // save max Fmin per number of clusters
                for (int i = 0; i < k; i++)
                {
                    if (Fmins[i] > Fmins_k[k - MinNumClusters])
                    {
                        Fmins_k[k - MinNumClusters] = Fmins[i];
                    }
                }
            }

            //select max F among different number of cluters
            for (int i = 0; i < Fmins_k.Length; i++)
            {
                if (Fmins_k[i] > Fmax)
                {
                    Fmax    = Fmins_k[i];
                    cluster = i + MinNumClusters;
                }
            }

            return(cluster);
        }