/**
         * Computes the Q matrix from the imformation stored in the QR matrix.  This
         * operation requires about 4(m<sup>2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
         *
         * @param Q The orthogonal Q matrix.
         */
        //@Override
        public ZMatrixRMaj getQ(ZMatrixRMaj Q, bool compact)
        {
            if (compact)
            {
                Q = UtilDecompositons_ZDRM.checkIdentity(Q, numRows, minLength);
            }
            else
            {
                Q = UtilDecompositons_ZDRM.checkIdentity(Q, numRows, numRows);
            }

            for (int j = minLength - 1; j >= 0; j--)
            {
                double[] u = dataQR[j];

                double vvReal = u[j * 2];
                double vvImag = u[j * 2 + 1];

                u[j * 2]     = 1;
                u[j * 2 + 1] = 0;
                double gammaReal = gammas[j];

                QrHelperFunctions_ZDRM.rank1UpdateMultR(Q, u, 0, gammaReal, j, j, numRows, v);

                u[j * 2]     = vvReal;
                u[j * 2 + 1] = vvImag;
            }

            return(Q);
        }
        /**
         * Computes the Q matrix from the information stored in the QR matrix.  This
         * operation requires about 4(m<sup>2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
         *
         * @param Q The orthogonal Q matrix.
         */
        //@Override
        public ZMatrixRMaj getQ(ZMatrixRMaj Q, bool compact)
        {
            if (compact)
            {
                Q = UtilDecompositons_ZDRM.checkIdentity(Q, numRows, minLength);
            }
            else
            {
                Q = UtilDecompositons_ZDRM.checkIdentity(Q, numRows, numRows);
            }

            // Unlike applyQ() this takes advantage of zeros in the identity matrix
            // by not multiplying across all rows.
            for (int j = minLength - 1; j >= 0; j--)
            {
                int    diagIndex  = (j * numRows + j) * 2;
                double realBefore = QR.data[diagIndex];
                double imagBefore = QR.data[diagIndex + 1];

                QR.data[diagIndex]     = 1;
                QR.data[diagIndex + 1] = 0;

                QrHelperFunctions_ZDRM.rank1UpdateMultR(Q, QR.data, j * numRows, gammas[j], j, j, numRows, v);

                QR.data[diagIndex]     = realBefore;
                QR.data[diagIndex + 1] = imagBefore;
            }

            return(Q);
        }
        /**
         * A = Q<sup>H</sup>*A
         *
         * @param A Matrix that is being multiplied by Q<sup>T</sup>.  Is modified.
         */
        public void applyTranQ(ZMatrixRMaj A)
        {
            for (int j = 0; j < minLength; j++)
            {
                int    diagIndex  = (j * numRows + j) * 2;
                double realBefore = QR.data[diagIndex];
                double imagBefore = QR.data[diagIndex + 1];

                QR.data[diagIndex]     = 1;
                QR.data[diagIndex + 1] = 0;

                QrHelperFunctions_ZDRM.rank1UpdateMultR(A, QR.data, j * numRows, gammas[j], 0, j, numRows, v);

                QR.data[diagIndex]     = realBefore;
                QR.data[diagIndex + 1] = imagBefore;
            }
        }
        /**
         * Computes the Q matrix from the information stored in the QR matrix.  This
         * operation requires about 4(m<sup>2</sup>n-mn<sup>2</sup>+n<sup>3</sup>/3) flops.
         *
         * @param Q The orthogonal Q matrix.
         */
        //@Override
        public ZMatrixRMaj getQ(ZMatrixRMaj Q, bool compact)
        {
            if (compact)
            {
                Q = UtilDecompositons_ZDRM.checkIdentity(Q, numRows, minLength);
            }
            else
            {
                Q = UtilDecompositons_ZDRM.checkIdentity(Q, numRows, numRows);
            }

            for (int j = minLength - 1; j >= 0; j--)
            {
                QrHelperFunctions_ZDRM.extractHouseholderColumn(QR, j, numRows, j, u, 0);
                QrHelperFunctions_ZDRM.rank1UpdateMultR(Q, u, 0, gammas[j], j, j, numRows, v);
            }

            return(Q);
        }
        /**
         * <p>
         * Computes the householder vector "u" for the first column of submatrix j.  Note this is
         * a specialized householder for this problem.  There is some protection against
         * overflow and underflow.
         * </p>
         * <p>
         * Q = I - &gamma;uu<sup>H</sup>
         * </p>
         * <p>
         * This function finds the values of 'u' and '&gamma;'.
         * </p>
         *
         * @param j Which submatrix to work off of.
         */
        protected void householder(int j)
        {
            // find the element with the largest absolute value in the column and make a copy
            double max = QrHelperFunctions_ZDRM.extractColumnAndMax(QR, j, numRows, j, u, 0);

            if (max <= 0.0)
            {
                gammas[j] = 0;
                error     = true;
            }
            else
            {
                double gamma = QrHelperFunctions_ZDRM.computeTauGammaAndDivide(j, numRows, u, max, tau);
                gammas[j] = gamma;

                // divide u by u_0
                double real_u_0 = u[j * 2] + tau.real;
                double imag_u_0 = u[j * 2 + 1] + tau.imaginary;
                QrHelperFunctions_ZDRM.divideElements(j + 1, numRows, u, 0, real_u_0, imag_u_0);

                // write the reflector into the lower left column of the matrix
                for (int i = j + 1; i < numRows; i++)
                {
                    dataQR[(i * numCols + j) * 2]     = u[i * 2];
                    dataQR[(i * numCols + j) * 2 + 1] = u[i * 2 + 1];
                }

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

                QrHelperFunctions_ZDRM.rank1UpdateMultR(QR, u, 0, gamma, j + 1, j, numRows, v);

                // since the first element in the householder vector is known to be 1
                // store the full upper hessenberg
                if (j < numCols)
                {
                    dataQR[(j * numCols + j) * 2]     = -tau.real * max;
                    dataQR[(j * numCols + j) * 2 + 1] = -tau.imaginary * max;
                }
            }
        }
        /**
         * A = Q*A
         *
         * @param A Matrix that is being multiplied by Q.  Is modified.
         */
        public void applyQ(ZMatrixRMaj A)
        {
            if (A.numRows != numRows)
            {
                throw new ArgumentException("A must have at least " + numRows + " rows.");
            }

            for (int j = minLength - 1; j >= 0; j--)
            {
                int    diagIndex  = (j * numRows + j) * 2;
                double realBefore = QR.data[diagIndex];
                double imagBefore = QR.data[diagIndex + 1];

                QR.data[diagIndex]     = 1;
                QR.data[diagIndex + 1] = 0;

                QrHelperFunctions_ZDRM.rank1UpdateMultR(A, QR.data, j * numRows, gammas[j], 0, j, numRows, v);

                QR.data[diagIndex]     = realBefore;
                QR.data[diagIndex + 1] = imagBefore;
            }
        }