/**
        * Computes the Moore–Penrose pseudoinverse using the SVD method.
        *
        * Modified version of the original implementation by Kim van der Linde.
        */
        public static GeneralMatrix pinv(GeneralMatrix x)
        {
            if (x.Rank() < 1)
                return null;

            if (x.ColumnDimension > x.RowDimension)
                return pinv(x.Transpose()).Transpose();

            SingularValueDecomposition svdX = new SingularValueDecomposition(x);
            double[] singularValues = svdX.SingularValues;
            double tol = Math.Max(x.ColumnDimension, x.RowDimension)
                    * singularValues[0] * 2E-16;

            double[] singularValueReciprocals = new double[singularValues.Count()];
            for (int i = 0; i < singularValues.Count(); i++)
                singularValueReciprocals[i] = Math.Abs(singularValues[i]) < tol ? 0
                        : (1.0 / singularValues[i]);

            double[][] u = svdX.GetU().Array;
            double[][] v = svdX.GetV().Array;

            int min = Math.Min(x.ColumnDimension, u[0].Count());

            double[][] inverse = new double[x.ColumnDimension][];

            for (int i = 0; i < x.ColumnDimension; i++) {
                inverse[i] = new double[x.RowDimension];

                for (int j = 0; j < u.Count(); j++)
                    for (int k = 0; k < min; k++)
                        inverse[i][j] += v[i][k] * singularValueReciprocals[k] * u[j][k];
            }
            return new GeneralMatrix(inverse);
        }
        private void computeaccCalButton_Click(object sender, EventArgs e)
        {
            int i,j;

            calStatusText.Text = "Computing Calibration...";

            // Construct D matrix
            // D = [x.^2, y.^2, z.^2, x.*y, x.*z, y.*z, x, y, z, ones(N,1)];
            for (i = 0; i < SAMPLES; i++ )
            {
                // x^2 term
                D.SetElement(i,0, loggedData[i,0]*loggedData[i,0]);

                // y^2 term
                D.SetElement(i,1,loggedData[i,1]*loggedData[i,1]);

                // z^2 term
                D.SetElement(i, 2, loggedData[i, 2] * loggedData[i, 2]);

                // x*y term
                D.SetElement(i,3,loggedData[i,0]*loggedData[i,1]);

                // x*z term
                D.SetElement(i,4,loggedData[i,0]*loggedData[i,2]);

                // y*z term
                D.SetElement(i,5,loggedData[i,1]*loggedData[i,2]);

                // x term
                D.SetElement(i,6,loggedData[i,0]);

                // y term
                D.SetElement(i,7,loggedData[i,1]);

                // z term
                D.SetElement(i,8,loggedData[i,2]);

                // Constant term
                D.SetElement(i,9,1);
            }

            // QR=triu(qr(D))
            QRDecomposition QR = new QRDecomposition(D);
            // [U,S,V] = svd(D)
            SingularValueDecomposition SVD = new SingularValueDecomposition(QR.R);
            GeneralMatrix V = SVD.GetV();

            GeneralMatrix A = new GeneralMatrix(3, 3);

            double[] p = new double[V.RowDimension];

            for (i = 0; i < V.RowDimension; i++ )
            {
                p[i] = V.GetElement(i,V.ColumnDimension-1);
            }

            /*
            A = [p(1) p(4)/2 p(5)/2;
            p(4)/2 p(2) p(6)/2;
            p(5)/2 p(6)/2 p(3)];
             */

            if (p[0] < 0)
            {
                for (i = 0; i < V.RowDimension; i++)
                {
                    p[i] = -p[i];
                }
            }

            A.SetElement(0,0,p[0]);
            A.SetElement(0,1,p[3]/2);
            A.SetElement(1,2,p[4]/2);

            A.SetElement(1,0,p[3]/2);
            A.SetElement(1,1,p[1]);
            A.SetElement(1,2,p[5]/2);

            A.SetElement(2,0,p[4]/2);
            A.SetElement(2,1,p[5]/2);
            A.SetElement(2,2,p[2]);

            CholeskyDecomposition Chol = new CholeskyDecomposition(A);
            GeneralMatrix Ut = Chol.GetL();
            GeneralMatrix U = Ut.Transpose();

            double[] bvect = {p[6]/2,p[7]/2,p[8]/2};
            double d = p[9];
            GeneralMatrix b = new GeneralMatrix(bvect,3);

            GeneralMatrix v = Ut.Solve(b);

            double vnorm_sqrd = v.GetElement(0,0)*v.GetElement(0,0) + v.GetElement(1,0)*v.GetElement(1,0) + v.GetElement(2,0)*v.GetElement(2,0);
            double s = 1/Math.Sqrt(vnorm_sqrd - d);

            GeneralMatrix c = U.Solve(v);
            for (i = 0; i < 3; i++)
            {
                c.SetElement(i, 0, -c.GetElement(i, 0));
            }

            U = U.Multiply(s);

            for (i = 0; i < 3; i++)
            {
                for (j = 0; j < 3; j++)
                {
                    calMat[i, j] = U.GetElement(i, j);
                }
            }

            for (i = 0; i < 3; i++)
            {
                bias[i] = c.GetElement(i, 0);
            }

            accAlignment00.Text = calMat[0, 0].ToString();
            accAlignment01.Text = calMat[0, 1].ToString();
            accAlignment02.Text = calMat[0, 2].ToString();

            accAlignment10.Text = calMat[1, 0].ToString();
            accAlignment11.Text = calMat[1, 1].ToString();
            accAlignment12.Text = calMat[1, 2].ToString();

            accAlignment20.Text = calMat[2, 0].ToString();
            accAlignment21.Text = calMat[2, 1].ToString();
            accAlignment22.Text = calMat[2, 2].ToString();

            biasX.Text = bias[0].ToString();
            biasY.Text = bias[1].ToString();
            biasZ.Text = bias[2].ToString();

            calStatusText.Text = "Done";
            flashCommitButton.Enabled = true;
            accAlignmentCommitButton.Enabled = true;
        }
        public GeneralMatrix[] decompose(int svCount, GeneralMatrix m)
        {
            SingularValueDecomposition s = new SingularValueDecomposition(m);
            try
            {
                sv = svCount;
                frameScale = m.RowDimension;

                if (m.RowDimension > m.ColumnDimension)
                {
                    frameScale = m.ColumnDimension;
                }
                else if (m.RowDimension < m.ColumnDimension)
                {
                    frameScale = m.RowDimension;
                }

                // Make square matrix of data
                if (m.RowDimension != m.ColumnDimension)
                {
                    squareM = m.GetMatrix(0, frameScale - 1, 0, frameScale - 1);
                }
                else
                {
                    squareM = m;
                }

                //perform the SVD here:
                SingularValueDecomposition svd = new SingularValueDecomposition(squareM);

                GeneralMatrix U = svd.GetU().GetMatrix(0, frameScale - 1, 0, sv - 1);
                GeneralMatrix V = svd.GetV();
                double[] D = svd.SingularValues;

                GeneralMatrix dMat = makeDiagonalSquare(D, sv);
                GeneralMatrix vMat = V.Transpose().GetMatrix(0, sv - 1, 0, frameScale - 1);

                /*Console.WriteLine(U.RowDimension + "x" + U.ColumnDimension
                        + " * " + dMat.RowDimension + "x" + dMat.ColumnDimension
                        + " * " + vMat.RowDimension + "x" + vMat.ColumnDimension);

                recomposition = U.Multiply(dMat).Multiply(vMat);

                int[,] recompositionData = new int[recomposition.ColumnDimension, recomposition.RowDimension];

                for (int i = 0; i < recomposition.ColumnDimension; i++)
                {
                    for (int j = 0; j < recomposition.RowDimension; j++)
                    {
                        recompositionData[j, i] = (int)(recomposition.GetElement(j, i));
                    }
                }*/

                GeneralMatrix[] result = new GeneralMatrix[2];
                result[0] = U;
                result[1] = vMat;
                return result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            return null;
        }
        private void GetFiles_Decompose_WriteToArff()
        {
            try {
                // sampleFactor is the amount to divide by the total size of the
                // data set
                // when determining the subsample that will be used in svd

                Console.WriteLine("Creating arff file...");
                DisplayMessage("Creating arff file...");

                // Create folder
                System.IO.Directory.CreateDirectory(OutputDir + "Results/");
                StreamWriter output = new StreamWriter(OutputDir + "Results/" + "resultsGalaxy.arff");

                output.Write("@relation 'galaxy'\n");
                output.Write("@attribute class real\n");
                output.Write("@attribute colorF real\n");
                output.Write("@attribute bulgeF real\n");
                output.Write("@attribute constF real\n");
                for (int i = 0; i < sv * 3; i++) {
                    output.Write("@attribute " + i + " real\n");
                }
                output.Write("@data\n");

                Console.WriteLine("Begin galaxy sampling");
                DisplayMessage("Begin galaxy sampling");

                // Initialize a three matrices that will hold all of the images
                // (r,g,b of each image where each row is an image)
                dataRed = new GeneralMatrix(galaxyData.Count() / sampleFactor,
                        imageScaleSize * imageScaleSize);
                dataGreen = new GeneralMatrix(galaxyData.Count() / sampleFactor,
                        imageScaleSize * imageScaleSize);
                dataBlue = new GeneralMatrix(galaxyData.Count() / sampleFactor,
                        imageScaleSize * imageScaleSize);

                // subsample from galaxydata
                System.Threading.Tasks.Parallel.For(0, galaxyData.Count() / sampleFactor, (int index) => {
                    Bitmap tempImage = getImage(OutputDir + "galaxies/"
                            + galaxyData[sampleFactor * index][0] + ".jpg", imageScaleSize);

                    for (int i = 0; i < imageScaleSize; i++) {
                        for (int j = 0; j < imageScaleSize; j++) {
                            int pixelColor = tempImage.GetPixel(i, j).ToArgb();
                            int[] rgb = new int[3];
                            rgb[0] += ((pixelColor & 0x00ff0000) >> 16);
                            rgb[1] += ((pixelColor & 0x0000ff00) >> 8);
                            rgb[2] += (pixelColor & 0x000000ff);

                            dataRed.SetElement(index, i * imageScaleSize + j, rgb[0]);
                            dataGreen.SetElement(index, i * imageScaleSize + j, rgb[1]);
                            dataBlue.SetElement(index, i * imageScaleSize + j, rgb[2]);
                        }
                    }

                    //if (index % 10 == 0)
                        //DisplayImage(index);
                    //Console.WriteLine("Galaxy " + (sampleFactor * index) + " finished");
                });

                Console.WriteLine("Galaxy sampling finished\nBegin R, G and B channel SVD");
                DisplayMessage("Galaxy sampling finished, Begin R, G and B channel SVD");

                // Perform svd on subsample:
                var redWorker = System.Threading.Tasks.Task.Factory.StartNew(() => svdR = new SingularValueDecomposition(dataRed));
                var greenWorker = System.Threading.Tasks.Task.Factory.StartNew(() => svdG = new SingularValueDecomposition(dataGreen));
                var blueWorker = System.Threading.Tasks.Task.Factory.StartNew(() => svdB = new SingularValueDecomposition(dataBlue));
                System.Threading.Tasks.Task.WaitAll(redWorker, greenWorker, blueWorker);

                // Create the basis for each component
                GeneralMatrix rV = svdR.GetV();
                Console.Write("dim rU: " + rV.RowDimension + ", "
                        + rV.ColumnDimension);
                GeneralMatrix gV = svdG.GetV();
                GeneralMatrix bV = svdB.GetV();

                rV = GetSVs(rV, sv);
                Console.Write("Svs: " + sv);
                Console.Write("Dim SsV: " + rV.RowDimension + ", "
                        + rV.ColumnDimension);
                gV = GetSVs(gV, sv);
                bV = GetSVs(bV, sv);

                // Perform the pseudoinverses
                frV = pinv(rV.Transpose());
                fgV = pinv(gV.Transpose());
                fbV = pinv(bV.Transpose());

                // Stores frV, fgV and fbV to file
                WriteToFile();

                Console.WriteLine("SVD finished");
                DisplayMessage("SVD finished, load full dataset for testing");

                Console.WriteLine("Begin filling dataset for testing");

                // fill the 'full' datasets
                dataRed = new GeneralMatrix(galaxyData.Count(), imageScaleSize
                        * imageScaleSize);
                dataGreen = new GeneralMatrix(galaxyData.Count(), imageScaleSize
                        * imageScaleSize);
                dataBlue = new GeneralMatrix(galaxyData.Count(), imageScaleSize
                        * imageScaleSize);

                System.Threading.Tasks.Parallel.For(0, galaxyData.Count(), (int index) => {
                    Bitmap tempImage = getImage(OutputDir + "galaxies/"
                            + galaxyData[index][0] + ".jpg", imageScaleSize);

                    for (int i = 0; i < imageScaleSize; i++) {
                        for (int j = 0; j < imageScaleSize; j++) {
                            int pixelColor = tempImage.GetPixel(i, j).ToArgb();
                            int[] rgb = new int[3];
                            rgb[0] += ((pixelColor & 0x00ff0000) >> 16);
                            rgb[1] += ((pixelColor & 0x0000ff00) >> 8);
                            rgb[2] += (pixelColor & 0x000000ff);

                            dataRed.SetElement(index, i * imageScaleSize + j, rgb[0]);
                            dataGreen.SetElement(index, i * imageScaleSize + j,
                                    rgb[1]);
                            dataBlue.SetElement(index, i * imageScaleSize + j, rgb[2]);
                        }
                    }

                });

                Console.WriteLine("Finished filling dataset for testing");
                DisplayMessage("Finished filling dataset for testing, begin projecting galaxies to U coordinate system");

                Console.WriteLine("Begin projecting galaxies to U coordinate system, writing to ARFF file");

                // Do the coordinate conversion
                rV = dataRed.Multiply(frV);
                gV = dataGreen.Multiply(fgV);
                bV = dataBlue.Multiply(fbV);

                Console.Write("Dim Final rU: " + rV.ColumnDimension
                        + ", " + rV.RowDimension);
                Console.WriteLine("galaxyData.Count(): " + galaxyData.Count());

                // write to the output file here:
                for (int imageIndex = 0; imageIndex < galaxyData.Count(); imageIndex++) {

                    Bitmap tempImage = getImage(OutputDir + "galaxies/"
                            + galaxyData[imageIndex][0] + ".jpg", imageScaleSize);

                    float colorFactor = (GetColor(tempImage)[0] / GetColor(tempImage)[2]);
                    float centralBulgeFactor = getCentralBulge(tempImage);
                    float consistencyFactor = GetConsistency(tempImage);

                    output.Write(galaxyData[imageIndex][1] + ", ");
                    output.Write(colorFactor + ", ");
                    output.Write(centralBulgeFactor + ", ");
                    output.Write(consistencyFactor + ", ");

                    // output data (r,g,b)
                    for (int i = 0; i < rV.ColumnDimension; i++) {
                        output.Write(rV.GetElement(imageIndex, i) + ", ");
                        output.Write(gV.GetElement(imageIndex, i) + ", ");
                        if (i == rV.ColumnDimension - 1) {
                            output.Write(bV.GetElement(imageIndex, i) + "\n");
                        }
                        else {
                            output.Write(bV.GetElement(imageIndex, i) + ", ");
                        }
                    }

                    //if (imageIndex % (galaxyData.Count() / 100) == 0)
                    //    DisplayImage(imageIndex);
                    DisplayMessage("Finished galaxy " + imageIndex.ToString() + " - " + (100 * imageIndex / galaxyData.Count()).ToString() + "%");

                }

                output.Flush();
                output.Close();
                output.Dispose();

                Console.Write("Finished creating arff file...");
                DisplayMessage("Finished creating arff file...");

            }
            catch (Exception ex) {
                Console.Write(ex.ToString());
            }
        }
        public int classify(Bitmap currentImage)
        {
            try {
                currentImage.Save(OutputDir + "Results/temp.png", System.Drawing.Imaging.ImageFormat.Png);

                // Create folder
                System.IO.Directory.CreateDirectory(OutputDir + "Results/");
                StreamWriter output = new StreamWriter(OutputDir + "Results/" + "temp.arff");

                output.Write("@relation 'galaxy'\n");
                output.Write("@attribute class real\n");
                output.Write("@attribute colorF real\n");
                output.Write("@attribute bulgeF real\n");
                output.Write("@attribute constF real\n");
                for (int i = 0; i < sv * 3; i++) {
                    output.Write("@attribute " + i + " real\n");
                }
                output.Write("@data\n");

                Bitmap tempImage = getImage(OutputDir + "Results/temp.png",
                        imageScaleSize);

                for (int i = 0; i < imageScaleSize; i++) {
                    for (int j = 0; j < imageScaleSize; j++) {
                        int pixelColor = tempImage.GetPixel(i, j).ToArgb();
                        int[] rgb = new int[3];
                        rgb[0] += ((pixelColor & 0x00ff0000) >> 16);
                        rgb[1] += ((pixelColor & 0x0000ff00) >> 8);
                        rgb[2] += (pixelColor & 0x000000ff);

                        dataRed.SetElement(galaxyData.Count() - 1, i * imageScaleSize + j,
                                rgb[0]);
                        dataGreen.SetElement(galaxyData.Count() - 1,
                                i * imageScaleSize + j, rgb[1]);
                        dataBlue.SetElement(galaxyData.Count() - 1, i * imageScaleSize + j,
                                rgb[2]);
                    }
                }

                var redWorker = System.Threading.Tasks.Task.Factory.StartNew(() => svdR = new SingularValueDecomposition(dataRed));
                var greenWorker = System.Threading.Tasks.Task.Factory.StartNew(() => svdG = new SingularValueDecomposition(dataGreen));
                var blueWorker = System.Threading.Tasks.Task.Factory.StartNew(() => svdB = new SingularValueDecomposition(dataBlue));
                System.Threading.Tasks.Task.WaitAll(redWorker, greenWorker, blueWorker);

                GeneralMatrix rU = svdR.GetU();
                GeneralMatrix gU = svdG.GetU();
                GeneralMatrix bU = svdB.GetU();
                rU = GetSVs(rU, sv);
                gU = GetSVs(gU, sv);
                bU = GetSVs(bU, sv);

                float colorFactor = (GetColor(tempImage)[0] / GetColor(tempImage)[2]);
                float centralBulgeFactor = getCentralBulge(tempImage);
                float consistencyFactor = GetConsistency(tempImage);

                output.Write(galaxyData[galaxyData.Count() - 1][1] + ", ");
                output.Write(colorFactor + ", ");
                output.Write(centralBulgeFactor + ", ");
                output.Write(consistencyFactor + ", ");

                // output data (r,g,b)
                for (int i = 0; i < rU.ColumnDimension; i++) {
                    output.Write(rU.GetElement(galaxyData.Count() - 1, i) + ", ");
                    output.Write(gU.GetElement(galaxyData.Count() - 1, i) + ", ");
                    if (i == rU.ColumnDimension - 1) {
                        output.Write(bU.GetElement(galaxyData.Count() - 1, i) + "\n");
                    }
                    else {
                        output.Write(bU.GetElement(galaxyData.Count() - 1, i) + ", ");
                    }
                }

                output.Flush();
                output.Close();
                output.Dispose();

                weka.core.converters.ConverterUtils.DataSource source = new weka.core.converters.ConverterUtils.DataSource(OutputDir + "Results/temp.arff");
                weka.core.Instances test = source.getDataSet();
                test.setClassIndex(0);

                int classPrediction = (int)Math.Round(fc.classifyInstance(test.instance(0)));
                if (classPrediction < -6) {
                    classPrediction = -6;
                }
                else if (classPrediction > 11) {
                    classPrediction = 11;
                }

                return classPrediction;

            }
            catch (Exception ex) {
                Console.Write(ex.ToString());
            }

            return -99;
        }