/// <summary>
        /// Gram-Schmidt orthogonalization of an m by n matrix A, such that
        /// {Q, R} is returned, where A = QR, Q is m by n and orthogonal, R is
        /// n by n and upper triangular matrix.
        /// </summary>
        /// <returns></returns>
        public MMatrix[] QRGramSchmidt()
        {
            int m = row;
            int n = col;

            MMatrix Q = new MMatrix(m, n);
            MMatrix R = new MMatrix(n, n);

            // the first column of Q equals the first column of this matrix
            for (int i = 0; i < m; ++i)
                Q[i, 0] = this[i, 0];

            R[0, 0] = 1;

            for (int k = 0; k < n; ++k)
            {
                R[k, k] = Vector.L2Norm(this.Column(k));

                for (int i = 0; i < m; ++i)
                    Q[i, k] = this[i, k] / R[k, k];

                for (int j = k + 1; j < n; ++j)
                {
                    R[k, j] = Vector.DotProduct(Q.Column(k), this.Column(j));

                    for (int i = 0; i < m; ++i)
                        this[i, j] = this[i, j] - Q[i, k] * R[k, j];
                }
            }

            return new MMatrix[] { Q, R };
        }