/**
         * <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
         * overfloaw and underflow.
         * </p>
         * <p>
         * Q = I - &gamma;uu<sup>T</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)
        {
            double[] u = dataQR[j];

            // find the largest value in this column
            // this is used to normalize the column and mitigate overflow/underflow
            double max = QrHelperFunctions_ZDRM.findMax(u, j, numRows - j);

            if (max == 0.0)
            {
                gamma = 0;
                error = true;
            }
            else
            {
                // computes tau and gamma, and normalizes u by max
                gamma = QrHelperFunctions_ZDRM.computeTauGammaAndDivide(j, numRows, u, max, tau);

                // divide u by u_0
                //            double u_0 = u[j] + tau;
                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);

                tau.real      *= max;
                tau.imaginary *= max;

                u[j * 2]     = -tau.real;
                u[j * 2 + 1] = -tau.imaginary;
            }

            gammas[j] = gamma;
        }
        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);
        }
        override public /**/ double quality()
        {
            return(SpecializedOps_ZDRM.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 void solve(ZMatrixRMaj B, ZMatrixRMaj X)
        {
            UtilEjml.checkReshapeSolve(numRows, numCols, B, X);

            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++)
                {
                    double[] u = QR[n];

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

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

                    QrHelperFunctions_ZDRM.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_ZDRM.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];
                }
            }
        }