/// <summary>
        /// The function return the Minor of element[Row,Col] of a Matrix object
        /// </summary>
        public static MATRIX2 Minor(MATRIX2 matrix, int iRow, int iCol)
        {
            MATRIX2 minor = new MATRIX2(matrix.Rows - 1, matrix.Cols - 1);
            int     m = 0, n = 0;

            for (int i = 0; i < matrix.Rows; i++)
            {
                if (i == iRow)
                {
                    continue;
                }
                n = 0;
                for (int j = 0; j < matrix.Cols; j++)
                {
                    if (j == iCol)
                    {
                        continue;
                    }
                    minor[m, n] = matrix[i, j];
                    n++;
                }
                m++;
            }
            return(minor);
        }
        /// <summary>
        /// The function returns the Echelon form of a given matrix
        /// </summary>

        /*public static MATRIX2 EchelonForm(MATRIX2 matrix)
         * {
         *      try
         *      {
         *              MATRIX2 EchelonMatrix=matrix.Duplicate();
         *              for (int i=0;i<matrix.Rows;i++)
         *              {
         *                      if (EchelonMatrix[i,i]==0)	// if diagonal entry is zero,
         *                              for (int j=i+1;j<EchelonMatrix.Rows;j++)
         *                                      if (EchelonMatrix[j,i]!=0)	 //check if some below entry is non-zero
         *                                              EchelonMatrix.InterchangeRow(i,j);	// then interchange the two rows
         *                      if (EchelonMatrix[i,i]==0)	// if not found any non-zero diagonal entry
         *                              continue;	// increment i;
         *                      if ( EchelonMatrix[i,i]!=1)	// if diagonal entry is not 1 ,
         *                              for (int j=i+1;j<EchelonMatrix.Rows;j++)
         *                                      if (EchelonMatrix[j,i]==1)	 //check if some below entry is 1
         *                                              EchelonMatrix.InterchangeRow(i,j);	// then interchange the two rows
         *                      EchelonMatrix.MultiplyRow(i, Fraction.Inverse(EchelonMatrix[i,i]));
         *                      for (int j=i+1;j<EchelonMatrix.Rows;j++)
         *                              EchelonMatrix.AddRow( j, i, -EchelonMatrix[j,i]);
         *              }
         *              return EchelonMatrix;
         *      }
         *      catch(Exception)
         *      {
         *              throw new MatrixException("Matrix can not be reduced to Echelon form");
         *      }
         * }
         *
         * /// <summary>
         * /// The function returns the Echelon form of the current matrix
         * /// </summary>
         * public MATRIX2 EchelonForm()
         * {
         *      return EchelonForm(this);
         * }
         *
         * /// <summary>
         * /// The function returns the reduced echelon form of a given matrix
         * /// </summary>
         * public static MATRIX2 ReducedEchelonForm(MATRIX2 matrix)
         * {
         *      try
         *      {
         *              MATRIX2 ReducedEchelonMatrix=matrix.Duplicate();
         *              for (int i=0;i<matrix.Rows;i++)
         *              {
         *                      if (ReducedEchelonMatrix[i,i]==0)	// if diagonal entry is zero,
         *                              for (int j=i+1;j<ReducedEchelonMatrix.Rows;j++)
         *                                      if (ReducedEchelonMatrix[j,i]!=0)	 //check if some below entry is non-zero
         *                                              ReducedEchelonMatrix.InterchangeRow(i,j);	// then interchange the two rows
         *                      if (ReducedEchelonMatrix[i,i]==0)	// if not found any non-zero diagonal entry
         *                              continue;	// increment i;
         *                      if ( ReducedEchelonMatrix[i,i]!=1)	// if diagonal entry is not 1 ,
         *                              for (int j=i+1;j<ReducedEchelonMatrix.Rows;j++)
         *                                      if ( ReducedEchelonMatrix[j,i]==1 )	 //check if some below entry is 1
         *                                              ReducedEchelonMatrix.InterchangeRow(i,j);	// then interchange the two rows
         *                      ReducedEchelonMatrix.MultiplyRow(i, Fraction.Inverse(ReducedEchelonMatrix[i,i]));
         *                      for (int j=i+1;j<ReducedEchelonMatrix.Rows;j++)
         *                              ReducedEchelonMatrix.AddRow( j, i, -ReducedEchelonMatrix[j,i]);
         *                      for (int j=i-1;j>=0;j--)
         *                              ReducedEchelonMatrix.AddRow( j, i, -ReducedEchelonMatrix[j,i]);
         *              }
         *              return ReducedEchelonMatrix;
         *      }
         *      catch(Exception)
         *      {
         *              throw new MatrixException2("Matrix can not be reduced to Echelon form");
         *      }
         * }
         *
         * /// <summary>
         * /// The function returns the reduced echelon form of the current matrix
         * /// </summary>
         * public MATRIX2 ReducedEchelonForm()
         * {
         *      return ReducedEchelonForm(this);
         * }*/

        /// <summary>
        /// The function returns the inverse of a given matrix
        /// </summary>
        public static MATRIX2 Inverse(MATRIX2 matrix)
        {
            if (Determinent(matrix) == 0)
            {
                throw new MatrixException2("Inverse of a singular matrix is not possible");
            }
            return(Adjoint(matrix) / Determinent(matrix));
        }
        /// <summary>
        /// The function duplicates the current Matrix object
        /// </summary>
        public MATRIX2 Duplicate()
        {
            MATRIX2 matrix = new MATRIX2(Rows, Cols);

            for (int i = 0; i < Rows; i++)
            {
                for (int j = 0; j < Cols; j++)
                {
                    matrix[i, j] = this[i, j];
                }
            }
            return(matrix);
        }
        /// <summary>
        /// The function returns the transpose of a given matrix
        /// </summary>
        public static MATRIX2 Transpose(MATRIX2 matrix)
        {
            MATRIX2 TransposeMatrix = new MATRIX2(matrix.Cols, matrix.Rows);

            for (int i = 0; i < TransposeMatrix.Rows; i++)
            {
                for (int j = 0; j < TransposeMatrix.Cols; j++)
                {
                    TransposeMatrix[i, j] = matrix[j, i];
                }
            }
            return(TransposeMatrix);
        }
        private static MATRIX2 Multiply(MATRIX2 matrix, double frac)
        {
            MATRIX2 result = new MATRIX2(matrix.Rows, matrix.Cols);

            for (int i = 0; i < matrix.Rows; i++)
            {
                for (int j = 0; j < matrix.Cols; j++)
                {
                    result[i, j] = matrix[i, j] * frac;
                }
            }
            return(result);
        }
        /// <summary>
        /// The function takes a Matrix object and returns it as a string
        /// </summary>
        public static string ConvertToString(MATRIX2 matrix)
        {
            string str = "";

            for (int i = 0; i < matrix.Rows; i++)
            {
                for (int j = 0; j < matrix.Cols; j++)
                {
                    str += /*matrix[i,j].ConvertToString()*/ matrix[i, j] + "\t";
                }
                str += "\n";
            }
            return(str);
        }
        /// <summary>
        /// The function returns a Null Matrix of dimension ( Row x Col )
        /// </summary>
        public static MATRIX2 NullMatrix(int iRows, int iCols)
        {
            //Fraction temp=new Fraction(0);
            double  temp   = 0;
            MATRIX2 matrix = new MATRIX2(iRows, iCols);

            for (int i = 0; i < iRows; i++)
            {
                for (int j = 0; j < iCols; j++)
                {
                    matrix[i, j] = temp;
                }
            }
            return(matrix);
        }
        private static MATRIX2 Add(MATRIX2 matrix1, MATRIX2 matrix2)
        {
            if (matrix1.Rows != matrix2.Rows || matrix1.Cols != matrix2.Cols)
            {
                throw new MatrixException2("Operation not possible");
            }
            MATRIX2 result = new MATRIX2(matrix1.Rows, matrix1.Cols);

            for (int i = 0; i < result.Rows; i++)
            {
                for (int j = 0; j < result.Cols; j++)
                {
                    result[i, j] = matrix1[i, j] + matrix2[i, j];
                }
            }
            return(result);
        }
        /// <summary>
        /// The function returns the adjoint of a given matrix
        /// </summary>
        public static MATRIX2 Adjoint(MATRIX2 matrix)
        {
            if (matrix.Rows != matrix.Cols)
            {
                throw new MatrixException2("Adjoint of a non-square matrix does not exists");
            }
            MATRIX2 AdjointMatrix = new MATRIX2(matrix.Rows, matrix.Cols);

            for (int i = 0; i < matrix.Rows; i++)
            {
                for (int j = 0; j < matrix.Cols; j++)
                {
                    AdjointMatrix[i, j] = Math.Pow(-1, i + j) * Determinent(Minor(matrix, i, j));
                }
            }
            AdjointMatrix = Transpose(AdjointMatrix);
            return(AdjointMatrix);
        }
        /// <summary>
        /// The function returns the determinent of a Matrix object as Fraction
        /// </summary>
        public static double Determinent(MATRIX2 matrix)
        {
            //Fraction det=new Fraction(0);
            double det = 0;

            if (matrix.Rows != matrix.Cols)
            {
                throw new MatrixException2("Determinent of a non-square matrix doesn't exist");
            }
            if (matrix.Rows == 1)
            {
                return(matrix[0, 0]);
            }
            for (int j = 0; j < matrix.Cols; j++)
            {
                det += (matrix[0, j] * Determinent(MATRIX2.Minor(matrix, 0, j)) * (int)System.Math.Pow(-1, 0 + j));
            }
            return(det);
        }
        private static MATRIX2 Multiply(MATRIX2 matrix1, MATRIX2 matrix2)
        {
            if (matrix1.Cols != matrix2.Rows)
            {
                throw new MatrixException2("Operation not possible");
            }
            MATRIX2 result = MATRIX2.NullMatrix(matrix1.Rows, matrix2.Cols);

            for (int i = 0; i < result.Rows; i++)
            {
                for (int j = 0; j < result.Cols; j++)
                {
                    for (int k = 0; k < matrix1.Cols; k++)
                    {
                        result[i, j] += matrix1[i, k] * matrix2[k, j];
                    }
                }
            }
            return(result);
        }
        /// <summary>
        /// The function returns a Scalar Matrix of dimension ( Row x Col ) and scalar K
        /// </summary>
        public static MATRIX2 ScalarMatrix(int iRows, int iCols, int K)
        {
            //Fraction zero=new Fraction(0);
            //Fraction scalar=new Fraction(K);
            double  zero = 0, scalar = K;
            MATRIX2 matrix = new MATRIX2(iRows, iCols);

            for (int i = 0; i < iRows; i++)
            {
                for (int j = 0; j < iCols; j++)
                {
                    if (i == j)
                    {
                        matrix[i, j] = scalar;
                    }
                    else
                    {
                        matrix[i, j] = zero;
                    }
                }
            }
            return(matrix);
        }
        /// <summary>
        /// The function concatenates the two given matrices column-wise
        /// </summary>
        public static MATRIX2 Concatenate(MATRIX2 matrix1, MATRIX2 matrix2)
        {
            if (matrix1.Rows != matrix2.Rows)
            {
                throw new MatrixException2("Concatenation not possible");
            }
            MATRIX2 matrix = new MATRIX2(matrix1.Rows, matrix1.Cols + matrix2.Cols);

            for (int i = 0; i < matrix.Rows; i++)
            {
                for (int j = 0; j < matrix.Cols; j++)
                {
                    if (j < matrix1.Cols)
                    {
                        matrix[i, j] = matrix1[i, j];
                    }
                    else
                    {
                        matrix[i, j] = matrix2[i, j - matrix1.Cols];
                    }
                }
            }
            return(matrix);
        }
 /// <summary>
 /// Operators for the Matrix object
 /// includes -(unary), and binary opertors such as +,-,*,/
 /// </summary>
 public static MATRIX2 operator -(MATRIX2 matrix)
 {
     return(MATRIX2.Negate(matrix));
 }
        /*public static MATRIX2 operator /(MATRIX2 matrix1, Fraction frac)
         * {	return MATRIX2.Multiply(matrix1, Fraction.Inverse(frac));	}*/


        /// <summary>
        /// Internal Fucntions for the above operators
        /// </summary>
        private static MATRIX2 Negate(MATRIX2 matrix)
        {
            return(MATRIX2.Multiply(matrix, -1));
        }
 public static MATRIX2 operator /(MATRIX2 matrix1, double dbl)
 {
     return(MATRIX2.Multiply(matrix1, /*Fraction.Inverse(Fraction.ConvertToFraction(dbl))*/ 1.0d / dbl));
 }
        /*public static MATRIX2 operator *(Fraction frac, MATRIX2 matrix1)
         * {	return MATRIX2.Multiply(matrix1, frac);	}*/

        public static MATRIX2 operator /(MATRIX2 matrix1, int iNo)
        {
            return(MATRIX2.Multiply(matrix1, /*Fraction.Inverse(new Fraction(iNo))*/ 1.0d / iNo));
        }
 public static MATRIX2 operator *(double dbl, MATRIX2 matrix1)
 {
     return(MATRIX2.Multiply(matrix1, dbl));
 }
        /*public static MATRIX2 operator *(MATRIX2 matrix1, Fraction frac)
         * {	return MATRIX2.Multiply(matrix1, frac);	}*/

        public static MATRIX2 operator *(int iNo, MATRIX2 matrix1)
        {
            return(MATRIX2.Multiply(matrix1, iNo));
        }
 public static MATRIX2 operator *(MATRIX2 matrix1, MATRIX2 matrix2)
 {
     return(MATRIX2.Multiply(matrix1, matrix2));
 }
 public static MATRIX2 operator -(MATRIX2 matrix1, MATRIX2 matrix2)
 {
     return(MATRIX2.Add(matrix1, -matrix2));
 }
        public double[][] clusterByGK(double[] clusterV)
        {
            double  denominator = 0;
            VECTOR  holdData, nominator = new VECTOR(new double[dataSet[0].Length]);
            MATRIX2 nominator2, hold1, hold2, temp1, temp2;

            double[][] Distances = new double[clusterNB][];
            double[][] tempPartMat;
            double[,] Zk, Vi;
            this.P = clusterV;
            F      = new double[clusterNB][, ];
            while (true)
            {
                tempPartMat = new double[clusterNB][];
                /////////////   STEP 1 + 2
                for (int i = 0; i < clusterNB; i++)
                {
                    denominator = 0;
                    nominator   = new VECTOR(new double[dataSet[0].Length]);
                    nominator2  = new MATRIX2(dataSet[0].Length, dataSet[0].Length);
                    //nominator2.initMatrix2();
                    for (int k = 0; k < dataSet.Length; k++)
                    {
                        holdData     = new VECTOR(dataSet[k]);
                        nominator    = nominator + (Math.Pow(partitionMatrix[i][k], exponent) * holdData);
                        denominator += Math.Pow(partitionMatrix[i][k], exponent);
                    }
                    holdData = nominator / denominator;
                    V[i]     = holdData.Val;
                    //// Step 2
                    Vi    = convertToTable(V[i], false);
                    hold2 = new MATRIX2(Vi);
                    for (int k = 0; k < dataSet.Length; k++)
                    {
                        //holdData = new VECTOR(dataSet[k]);
                        //hold2 = new VECTOR(V[i]);
                        //hold2 = holdData - hold2;
                        Zk         = convertToTable(dataSet[k], false);
                        hold1      = new MATRIX2(Zk);
                        hold1      = hold1 - hold2;
                        temp1      = hold1 * hold1.Transpose();
                        temp1      = Math.Pow(partitionMatrix[i][k], exponent) * temp1;
                        nominator2 = nominator2 + temp1;
                        //nominator2 = nominator2 + (Math.Pow(partitionMatrix[i][k], exponent) * (hold1 * hold1.Transpose()));
                    }
                    nominator2 = nominator2 / denominator;
                    F[i]       = nominator2.Elements;
                    //// Step 3
                    for (int k = 0; k < dataSet.Length; k++)
                    {
                        Distances[i] = new double[dataSet.Length];
                    }
                    for (int k = 0; k < dataSet.Length; k++)
                    {
                        Zk              = convertToTable(dataSet[k], false);
                        hold1           = new MATRIX2(Zk);
                        hold1           = hold1 - hold2;
                        temp1           = (P[i] * Math.Pow(nominator2.Determinent(), 1.0d / dataSet[0].Length)) * nominator2.Inverse();
                        temp2           = temp1 * hold1;
                        Distances[i][k] = (hold1.Transpose() * temp2).Determinent();
                        //Distances[i][k] = hold1.Transpose() * (((P[i] * Math.Pow(nominator2.Determinent().ConvertToDouble(), 1.0d / dataSet[0].Length)) * nominator2.Inverse()) * hold1);
                    }
                }
                //////////////     END OF STEP 1 + 2 + 3

                //////////////     STEP 4
                for (int u = 0; u < clusterNB; u++)
                {
                    tempPartMat[u] = new double[dataSet.Length];
                }
                for (int k = 0; k < dataSet.Length; k++)
                {
                    if (allClusterValuesPositive(Distances, k))
                    {
                        for (int c = 0; c < clusterNB; c++)
                        {
                            denominator = 0;
                            for (int j = 0; j < clusterNB; j++)
                            {
                                denominator += Math.Pow((Distances[c][k] / Distances[j][k]), (2 / (exponent - 1)));
                            }
                            tempPartMat[c][k] = 1.0d / denominator;
                        }
                    }
                    else
                    {
                        int count = 0;
                        for (int c = 0; c < clusterNB; c++)
                        {
                            if (Distances[c][k] > 0)
                            {
                                tempPartMat[c][k] = 0;
                            }
                            else
                            {
                                count++;
                            }
                        }
                        denominator = 1.0d / count;
                        for (int h = 0; h < clusterNB; h++)
                        {
                            if (Distances[h][k] <= 0)
                            {
                                tempPartMat[h][k] = denominator;
                            }
                        }
                    }
                }
                if (!diferenceGreaterThanEbsilon(tempPartMat))
                {
                    break;
                }
                partitionMatrix = tempPartMat;
            }
            partitionMatrix = tempPartMat;
            return(partitionMatrix);
        }
 /// <summary>
 /// The function returns the current Matrix object as a string
 /// </summary>
 public string ConvertToString()
 {
     return(MATRIX2.ConvertToString(this));
 }