public PositiveDefiniteMatrix SetToInverse(PositiveDefiniteMatrix A)
        {
#if LAPACK
            this.SetTo(A);
            Lapack.SymmetricInverseInPlace(this);
            return(this);
#else
            return(SetToInverse(A, new LowerTriangularMatrix(rows, cols)));
#endif
        }
        /// <summary>
        /// Gets the Cholesky decomposition of the matrix (L*L' = A), replacing its contents.
        /// </summary>
        /// <param name="isPosDef">True if <c>this</c> is positive definite, otherwise false.</param>
        /// <returns>The Cholesky decomposition L.  If <c>this</c> is positive semidefinite,
        /// then L will satisfy L*L' = A.
        /// Otherwise, L will only approximately satisfy L*L' = A.</returns>
        /// <remarks>
        /// <c>this</c> must be symmetric, but need not be positive definite.
        /// </remarks>
        public LowerTriangularMatrix CholeskyInPlace(out bool isPosDef)
        {
            LowerTriangularMatrix L = new LowerTriangularMatrix(rows, cols, data);

#if LAPACK
            isPosDef = Lapack.CholeskyInPlace(this);
#else
            isPosDef = L.SetToCholesky(this);
#endif
            return(L);
        }
        public bool SetToCholesky(Matrix A)
        {
            A.CheckSymmetry("A");
#if LAPACK
            SetTo(A);
            bool isPosDef = Lapack.CholeskyInPlace(this);
#else
            CheckCompatible(A, "A");

            bool isPosDef           = true;
            LowerTriangularMatrix L = this;
            // compute the Cholesky factor
            // Reference: Golub and van Loan (1996)
            for (int i = 0; i < A.Cols; i++)
            {
                // upper triangle
                for (int j = 0; j < i; j++)
                {
                    L[j, i] = 0;
                }
                // lower triangle
                for (int j = i; j < A.Rows; j++)
                {
                    double sum    = A[i, j];
                    int    index1 = i * cols;
                    int    index2 = j * cols;
                    for (int k = 0; k < i; k++)
                    {
                        //sum -= L[i, k] * L[j, k];
                        sum -= data[index1++] * data[index2++];
                    }
                    if (i == j)
                    {
                        // diagonal entry
                        if (sum <= 0)
                        {
                            isPosDef = false;
                            L[i, i]  = 0;
                        }
                        else
                        {
                            L[i, i] = System.Math.Sqrt(sum);
                        }
                    }
                    else
                    {
                        // off-diagonal entry
                        if (L[i, i] > 0)
                        {
                            L[j, i] = sum / L[i, i];
                        }
                        else
                        {
                            L[j, i] = 0;
                        }
                    }
                }
            }
            CheckLowerTriangular();
#endif
            return(isPosDef);
        }
        /// <summary>
        /// Modifies <c>this</c> to be the inverse of A.
        /// </summary>
        /// <param name="A">Can be the same object as <c>this</c></param>
        public void SetToInverse(LowerTriangularMatrix A)
        {
#if LAPACK
            if (object.ReferenceEquals(this, A))
            {
                A = (LowerTriangularMatrix)A.Clone();
            }
            this.SetToIdentity();
            Lapack.PredivideBy(this, A);
#else
            // Reference: Golub and van Loan (1996)
            Assert.IsTrue(A.Rows == A.Cols);
            Assert.IsTrue(A.Rows == this.Rows);
            for (int k = 0; k < rows; k++)
            {
                if (A[k, k] == 0)
                {
                    throw new MatrixSingularException(A);
                }
            }
            for (int j = 0; j < cols; j++)
            {
                int index1 = j;
                // k < j case
                for (int k = 0; k < j; k++)
                {
                    //B[k, j] = 0.0;
                    data[index1] = 0.0;
                    index1      += cols;
                }
                // k == j case
                int    index2 = j + j * cols;
                double v      = 1.0 / A.data[index2];
                data[index1] = v;
                for (int i = j + 1; i < rows; i++)
                {
                    // B[i, j] = -v * A[i, j];
                    index1      += cols;
                    index2      += cols;
                    data[index1] = -v * A.data[index2];
                }
                // k > j case
                for (int k = j + 1; k < rows; k++)
                {
                    index1 = j + k * cols;
                    index2 = k + k * cols;
                    if (data[index1] != 0)
                    {
                        //double u = B[k,j]/A[k,k];
                        //B[k, j] = u;
                        // TM: this style of indexing may look ugly but it runs much faster
                        double u = data[index1] / A.data[index2];
                        data[index1] = u;
                        for (int i = k + 1; i < rows; i++)
                        {
                            // B[i, j] -= u * A[i, k];
                            index1       += cols;
                            index2       += cols;
                            data[index1] -= u * A.data[index2];
                        }
                    }
                }
            }
#endif
        }