public /**/ double quality()
        {
            return(SpecializedOps_CDRM.qualityTriangular(LU));
        }

        /**
         * a specialized version of solve that avoid additional checks that are not needed.
         */
        public void _solveVectorInternal(float[] vv)
        {
            // Solve L*Y = B
            solveL(vv);

            // Solve U*X = Y;
            TriangularSolver_CDRM.solveU(dataLU, vv, n);
        }
        public override /**/ double quality()
        {
            return(SpecializedOps_CDRM.qualityTriangular(R));
        }

        /**
         * Solves for X using the QR decomposition.
         *
         * @param B A matrix that is n by m.  Not modified.
         * @param X An n by m matrix where the solution is written to.  Modified.
         */
        //@Override
        public override void solve(CMatrixRMaj B, CMatrixRMaj X)
        {
            if (X.numRows != numCols)
            {
                throw new ArgumentException("Unexpected dimensions for X: X rows = " + X.numRows + " expected = " +
                                            numCols);
            }
            else if (B.numRows != numRows || B.numCols != X.numCols)
            {
                throw new ArgumentException("Unexpected dimensions for B");
            }

            int BnumCols = B.numCols;

            // solve each column one by one
            for (int colB = 0; colB < BnumCols; colB++)
            {
                // make a copy of this column in the vector
                for (int i = 0; i < numRows; i++)
                {
                    int indexB = (i * BnumCols + colB) * 2;
                    a.data[i * 2]     = B.data[indexB];
                    a.data[i * 2 + 1] = B.data[indexB + 1];
                }

                // Solve Qa=b
                // a = Q'b
                // a = Q_{n-1}...Q_2*Q_1*b
                //
                // Q_n*b = (I-gamma*u*u^T)*b = b - u*(gamma*U^T*b)
                for (int n = 0; n < numCols; n++)
                {
                    float[] u = QR[n];

                    float realVV = u[n * 2];
                    float imagVV = u[n * 2 + 1];

                    u[n * 2]     = 1;
                    u[n * 2 + 1] = 0;

                    QrHelperFunctions_CDRM.rank1UpdateMultR(a, u, 0, gammas[n], 0, n, numRows, temp.data);

                    u[n * 2]     = realVV;
                    u[n * 2 + 1] = imagVV;
                }

                // solve for Rx = b using the standard upper triangular solver
                TriangularSolver_CDRM.solveU(R.data, a.data, numCols);

                // save the results
                for (int i = 0; i < numCols; i++)
                {
                    int indexB = (i * BnumCols + colB) * 2;
                    X.data[indexB]     = a.data[i * 2];
                    X.data[indexB + 1] = a.data[i * 2 + 1];
                }
            }
        }
        public override /**/ double quality()
        {
            return(SpecializedOps_CDRM.qualityTriangular(R));
        }

        /**
         * Solves for X using the QR decomposition.
         *
         * @param B A matrix that is n by m.  Not modified.
         * @param X An n by m matrix where the solution is written to.  Modified.
         */
        //@Override
        public override void solve(CMatrixRMaj B, CMatrixRMaj X)
        {
            if (X.numRows != numCols)
            {
                throw new ArgumentException("Unexpected dimensions for X");
            }
            else if (B.numRows != numRows || B.numCols != X.numCols)
            {
                throw new ArgumentException("Unexpected dimensions for B");
            }

            int BnumCols = B.numCols;

            Y.reshape(numRows, 1);
            Z.reshape(numRows, 1);

            // solve each column one by one
            for (int colB = 0; colB < BnumCols; colB++)
            {
                // make a copy of this column in the vector
                for (int i = 0; i < numRows; i++)
                {
                    int indexB = B.getIndex(i, colB);
                    Y.data[i * 2]     = B.data[indexB];
                    Y.data[i * 2 + 1] = B.data[indexB + 1];
                }

                // Solve Qa=b
                // a = Q'b
                CommonOps_CDRM.mult(Qt, Y, Z);

                // solve for Rx = b using the standard upper triangular solver
                TriangularSolver_CDRM.solveU(R.data, Z.data, numCols);

                // save the results
                for (int i = 0; i < numCols; i++)
                {
                    X.set(i, colB, Z.data[i * 2], Z.data[i * 2 + 1]);
                }
            }
        }
        public override /**/ double quality()
        {
            // even those it is transposed the diagonal elements are at the same
            // elements
            return(SpecializedOps_CDRM.qualityTriangular(QR));
        }

        /**
         * Solves for X using the QR decomposition.
         *
         * @param B A matrix that is n by m.  Not modified.
         * @param X An n by m matrix where the solution is written to.  Modified.
         */
        //@Override
        public override void solve(CMatrixRMaj B, CMatrixRMaj X)
        {
            if (X.numRows != numCols)
            {
                throw new ArgumentException("Unexpected dimensions for X: X rows = " + X.numRows + " expected = " +
                                            numCols);
            }
            else if (B.numRows != numRows || B.numCols != X.numCols)
            {
                throw new ArgumentException("Unexpected dimensions for B");
            }

            U = decomposer.getR(U, true);
            float[] gammas = decomposer.getGammas();
            float[] dataQR = QR.data;

            int BnumCols = B.numCols;

            // solve each column one by one
            for (int colB = 0; colB < BnumCols; colB++)
            {
                // make a copy of this column in the vector
                for (int i = 0; i < numRows; i++)
                {
                    int indexB = (i * BnumCols + colB) * 2;
                    a[i * 2]     = B.data[indexB];
                    a[i * 2 + 1] = B.data[indexB + 1];
                }

                // Solve Qa=b
                // a = Q'b
                // a = Q_{n-1}...Q_2*Q_1*b
                //
                // Q_n*b = (I-gamma*u*u^H)*b = b - u*(gamma*U^H*b)
                for (int n = 0; n < numCols; n++)
                {
                    int indexU = (n * numRows + n + 1) * 2;

                    float realUb = a[n * 2];
                    float imagUb = a[n * 2 + 1];

                    // U^H*b
                    for (int i = n + 1; i < numRows; i++)
                    {
                        float realU = dataQR[indexU++];
                        float imagU = -dataQR[indexU++];

                        float realB = a[i * 2];
                        float imagB = a[i * 2 + 1];

                        realUb += realU * realB - imagU * imagB;
                        imagUb += realU * imagB + imagU * realB;
                    }

                    // gamma*U^T*b
                    realUb *= gammas[n];
                    imagUb *= gammas[n];

                    a[n * 2]     -= realUb;
                    a[n * 2 + 1] -= imagUb;

                    indexU = (n * numRows + n + 1) * 2;
                    for (int i = n + 1; i < numRows; i++)
                    {
                        float realU = dataQR[indexU++];
                        float imagU = dataQR[indexU++];

                        a[i * 2]     -= realU * realUb - imagU * imagUb;
                        a[i * 2 + 1] -= realU * imagUb + imagU * realUb;
                    }
                }

                // solve for Rx = b using the standard upper triangular solver
                TriangularSolver_CDRM.solveU(U.data, a, numCols);

                // save the results

                for (int i = 0; i < numCols; i++)
                {
                    int indexX = (i * X.numCols + colB) * 2;

                    X.data[indexX]     = a[i * 2];
                    X.data[indexX + 1] = a[i * 2 + 1];
                }
            }
        }
        public override /**/ double quality()
        {
            return(SpecializedOps_CDRM.qualityTriangular(QR));
        }

        /**
         * Solves for X using the QR decomposition.
         *
         * @param B A matrix that is n by m.  Not modified.
         * @param X An n by m matrix where the solution is writen to.  Modified.
         */
        //@Override
        public override void solve(CMatrixRMaj B, CMatrixRMaj X)
        {
            if (X.numRows != numCols)
            {
                throw new ArgumentException("Unexpected dimensions for X");
            }
            else if (B.numRows != numRows || B.numCols != X.numCols)
            {
                throw new ArgumentException("Unexpected dimensions for B");
            }

            int BnumCols = B.numCols;

            // solve each column one by one
            for (int colB = 0; colB < BnumCols; colB++)
            {
                // make a copy of this column in the vector
                for (int i = 0; i < numRows; i++)
                {
                    int indexB = (i * BnumCols + colB) * 2;
                    a[i * 2]     = B.data[indexB];
                    a[i * 2 + 1] = B.data[indexB + 1];
                }

                // Solve Qa=b
                // a = Q'b
                // a = Q_{n-1}...Q_2*Q_1*b
                //
                // Q_n*b = (I-gamma*u*u^H)*b = b - u*(gamma*U^H*b)
                for (int n = 0; n < numCols; n++)
                {
                    u[n * 2]     = 1;
                    u[n * 2 + 1] = 0;

                    float realUb = a[2 * n];
                    float imagUb = a[2 * n + 1];
                    // U^H*b
                    for (int i = n + 1; i < numRows; i++)
                    {
                        int   indexQR = (i * QR.numCols + n) * 2;
                        float realU   = u[i * 2] = QR.data[indexQR];
                        float imagU   = u[i * 2 + 1] = QR.data[indexQR + 1];

                        float realB = a[i * 2];
                        float imagB = a[i * 2 + 1];

                        realUb += realU * realB + imagU * imagB;
                        imagUb += realU * imagB - imagU * realB;
                    }

                    // gamma*U^H*b
                    realUb *= gammas[n];
                    imagUb *= gammas[n];

                    for (int i = n; i < numRows; i++)
                    {
                        float realU = u[i * 2];
                        float imagU = u[i * 2 + 1];

                        a[i * 2]     -= realU * realUb - imagU * imagUb;
                        a[i * 2 + 1] -= realU * imagUb + imagU * realUb;
                    }
                }

                // solve for Rx = b using the standard upper triangular solver
                TriangularSolver_CDRM.solveU(QR.data, a, numCols);

                // save the results
                for (int i = 0; i < numCols; i++)
                {
                    int indexX = (i * X.numCols + colB) * 2;

                    X.data[indexX]     = a[i * 2];
                    X.data[indexX + 1] = a[i * 2 + 1];
                }
            }
        }