/// <summary>
        /// Use Gobul Reinsch to decompose the matrix.
        /// http://people.duke.edu/~hpgavin/SystemID/References/Golub+Reinsch-NM-1970.pdf
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        public SingularValueDecomposition DecomposeGolubReinsch(double[][] a)
        {
            // http://people.duke.edu/~hpgavin/SystemID/References/Golub+Reinsch-NM-1970.pdf
            int    maxIteration = 50;
            double s = 0.0, h = 0.0, f = 0.0, c = 0.0, z = 0.0, y = 0.0;
            int    l   = 0;
            var    eps = double.Epsilon;
            var    tol = 1.11022302462516E-49;
            int    m   = a.Length;
            int    n   = a[0].Length;
            var    u   = a.ToArray();

            double[]   e = new double[n];
            double[]   q = new double[n];
            double[][] v = new double[n][];
            for (int k = 0; k < n; k++)
            {
                v[k] = new double[n];
            }

            double g = 0.0, x = 0.0;

            for (int i = 0; i < n; i++)
            {
                s    = 0.0;
                e[i] = g;
                l    = i + 1;
                for (int j = i; j < m; j++)
                {
                    s += System.Math.Pow(u[j][i], 2);
                }

                if (s <= tol)
                {
                    g = 0.0;
                }
                else
                {
                    f = u[i][i];
                    if (f < 0.0)
                    {
                        g = System.Math.Sqrt(s);
                    }
                    else
                    {
                        g = -System.Math.Sqrt(s);
                    }

                    h       = f * g - s;
                    u[i][i] = f - g;
                    for (int j = l; j < n; j++)
                    {
                        s = 0.0;
                        for (int k = i; k < m; k++)
                        {
                            s += u[k][i] * u[k][j];
                        }
                        f = s / h;
                        for (int k = i; k < m; k++)
                        {
                            u[k][j] = u[k][j] + f * u[k][i];
                        }
                    }
                }

                q[i] = g;
                s    = 0.0;
                for (int j = l; j < n; j++)
                {
                    s = s + u[i][j] * u[i][j];
                }

                if (s <= tol)
                {
                    g = 0.0;
                }
                else
                {
                    f = u[i][i + 1];
                    if (f < 0.0)
                    {
                        g = System.Math.Sqrt(s);
                    }
                    else
                    {
                        g = -System.Math.Sqrt(s);
                    }

                    h           = f * g - s;
                    u[i][i + 1] = f - g;
                    for (int j = l; j < n; j++)
                    {
                        e[j] = u[i][j] / h;
                    }

                    for (int j = l; j < m; j++)
                    {
                        s = 0.0;
                        for (int k = l; k < n; k++)
                        {
                            s = s + (u[j][k] * u[i][k]);
                        }

                        for (int k = l; k < n; k++)
                        {
                            u[j][k] = u[j][k] + (s * e[k]);
                        }
                    }
                }

                y = System.Math.Abs(q[i]) + System.Math.Abs(e[i]);
                if (y > x)
                {
                    x = y;
                }
            }

            // accumulation of right hand transformations.
            for (int i = n - 1; i >= 0; i--)
            {
                if (g != 0.0)
                {
                    h = g * u[i][i + 1];
                    for (int j = l; j < n; j++)
                    {
                        v[j][i] = u[i][j] / h;
                    }

                    for (int j = l; j < n; j++)
                    {
                        s = 0.0;
                        for (int k = l; k < n; k++)
                        {
                            s += u[i][k] * v[k][j];
                        }

                        for (int k = l; k < n; k++)
                        {
                            v[k][j] += (s * v[k][i]);
                        }
                    }
                }

                for (int j = l; j < n; j++)
                {
                    v[i][j] = 0.0;
                    v[j][i] = 0.0;
                }

                v[i][i] = 1.0;
                g       = e[i];
                l       = i;
            }

            // accumulation of the left hand transformations.
            for (int i = n - 1; i >= 0; i--)
            {
                l = i + 1;
                g = q[i];
                for (int j = l; j < n; j++)
                {
                    u[i][j] = 0.0;
                }

                if (g != 0.0)
                {
                    h = u[i][i] * g;
                    for (int j = l; j < n; j++)
                    {
                        s = 0.0;
                        for (int k = l; k < m; k++)
                        {
                            s += (u[k][i] * u[k][j]);
                        }

                        f = s / h;
                        for (int k = i; k < m; k++)
                        {
                            u[k][j] = u[k][j] + f * u[k][i];
                        }
                    }

                    for (int j = i; j < m; j++)
                    {
                        u[j][i] = u[j][i] / g;
                    }
                }
                else
                {
                    for (int j = i; j < m; j++)
                    {
                        u[j][i] = 0.0;
                    }
                }

                u[i][i] = u[i][i] + 1.0;
            }

            eps = eps * x;
            // diagonalization of the bidiagonal form.
            for (int k = n - 1; k >= 0; k--)
            {
                for (int iteration = 0; iteration < maxIteration; iteration++)
                {
                    bool testFConvergence = false;
                    // test f splitting.
                    for (l = k; l >= 0; l--)
                    {
                        if (System.Math.Abs(e[l]) <= eps)
                        {
                            testFConvergence = true;
                            break;
                        }

                        if (System.Math.Abs(q[l - 1]) <= eps)
                        {
                            break;
                        }
                    }

                    if (!testFConvergence)
                    {
                        c = 0.0;
                        s = 0.0;
                        var l1 = l - 1;
                        for (int i = l; i < k + 1; i++)
                        {
                            f    = s * e[i];
                            e[i] = c * e[i];
                            if (System.Math.Abs(f) <= eps)
                            {
                                break;
                            }

                            g    = q[i];
                            h    = Hypotenuse(f, g);
                            q[i] = h;
                            c    = g / h;
                            s    = -f / h;
                            for (int j = 0; j < m; j++)
                            {
                                y        = u[j][l1];
                                z        = u[j][i];
                                u[j][l1] = y * c + z * s;
                                u[j][i]  = -y * s + z * c;
                            }
                        }
                    }

                    // test f convergence.
                    z = q[k];
                    if (l == k)
                    {
                        if (z < 0.0)
                        {
                            q[k] = -z;
                            for (int j = 0; j < n; j++)
                            {
                                v[j][k] = -v[j][k];
                            }
                        }

                        break;
                    }

                    if (iteration >= maxIteration - 1)
                    {
                        break;
                    }

                    // shift from bottom 2x2 minor.
                    x = q[l];
                    y = q[k - 1];
                    g = e[k - 1];
                    h = e[k];
                    f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y);
                    g = Hypotenuse(f, 1.0);
                    if (f < 0)
                    {
                        f = ((x - z) * (x + z) + h * (y / (f - g) - h)) / x;
                    }
                    else
                    {
                        f = ((x - z) * (x + z) + h * (y / (f + g) - h)) / x;
                    }

                    // next QR transformation.
                    c = 1.0;
                    s = 1.0;
                    for (int i = l + 1; i < k + 1; i++)
                    {
                        g        = e[i];
                        y        = q[i];
                        h        = s * g;
                        g        = c * g;
                        z        = Hypotenuse(f, h);
                        e[i - 1] = z;
                        c        = f / z;
                        s        = h / z;
                        f        = x * c + g * s;
                        g        = -x * s + g * c;
                        h        = y * s;
                        y        = y * c;
                        for (int j = 0; j < n; j++)
                        {
                            x           = v[j][i - 1];
                            z           = v[j][i];
                            v[j][i - 1] = x * c + z * s;
                            v[j][i]     = -x * s + z * c;
                        }

                        z        = Hypotenuse(f, h);
                        q[i - 1] = z;
                        c        = f / z;
                        s        = h / z;
                        f        = c * g + s * y;
                        x        = -s * g + c * y;
                        for (int j = 0; j < m; j++)
                        {
                            y           = u[j][i - 1];
                            z           = u[j][i];
                            u[j][i - 1] = y * c + z * s;
                            u[j][i]     = -y * s + z * c;
                        }
                    }

                    e[l] = 0.0;
                    e[k] = f;
                    q[k] = x;
                }
            }

            Result = new SingularValueDecompositionResult
            {
                S     = Matrix.BuildIdentityMatrix(q),
                U     = u,
                V     = v,
                Input = a
            };
            return(this);
        }
        public SingularValueDecomposition DecomposeNaive(Matrix matrix)
        {
            var ata = matrix.Transpose().Multiply(matrix).Evaluate();
            var ataNormalizedVectors = DecomposeMatrix(ata);
            var sum = Matrix.BuildEmptyMatrix(matrix.NbRows, matrix.NbColumns);

            for (int i = 0; i < ataNormalizedVectors.Count; i++)
            {
                if (sum.NbRows <= i || sum.NbColumns <= i)
                {
                    break;
                }

                sum.SetValue(i, i, MathEntity.Sqrt(ataNormalizedVectors[i].EingenValue).Eval());
            }

            var v         = Matrix.BuildEmptyMatrix(ataNormalizedVectors[0].Vector.Length, ataNormalizedVectors.Count);
            var nbColumns = ataNormalizedVectors.Where(_ =>
            {
                if (_.EingenValue == 0)
                {
                    return(false);
                }

                return(true);
            }).Count();
            var u = Matrix.BuildEmptyMatrix(sum.NbRows, nbColumns);

            for (int column = 0; column < ataNormalizedVectors.Count; column++)
            {
                var eingenValue  = (NumberEntity)ataNormalizedVectors[column].EingenValue;
                var vector       = ataNormalizedVectors[column].Vector;
                var vectorMatrix = Matrix.BuildEmptyMatrix(vector.Length, 1);
                for (int row = 0; row < vector.Length; row++)
                {
                    v.SetValue(row, column, vector[row]);
                    vectorMatrix.SetValue(row, 0, vector[row]);
                }

                if (eingenValue.Number.Value == 0)
                {
                    continue;
                }

                var val     = (Number.Create(1) / MathEntity.Sqrt(ataNormalizedVectors[column].EingenValue)).Eval();
                var uColumn = matrix.Multiply(val).Multiply(vectorMatrix);
                for (int row = 0; row < uColumn.NbRows; row++)
                {
                    u.SetValue(row, column, uColumn.GetValue(row, 0).Eval());
                }
            }

            Result = new SingularValueDecompositionResult
            {
                U     = u,
                S     = sum,
                V     = v,
                Input = matrix
            };
            return(this);
        }