protected void computeV(int k)
        {
            double[] b = UBV.data;

            int row = k * n;

            // find the largest value in this column
            // this is used to normalize the column and mitigate overflow/underflow
            double max = QrHelperFunctions_DDRM.findMax(b, row + k + 1, n - k - 1);

            if (max > 0)
            {
                // -------- set up the reflector Q_k

                double tau = QrHelperFunctions_DDRM.computeTauAndDivide(k + 1, n, b, row, max);

                // write the reflector into the lower left column of the matrix
                double nu = b[row + k + 1] + tau;
                QrHelperFunctions_DDRM.divideElements_Brow(k + 2, n, u, b, row, nu);

                u[k + 1] = 1.0;

                double gamma = nu / tau;
                gammasV[k] = gamma;

                // writing to u could be avoided by working directly with b.
                // requires writing a custom rank1Update function
                // ---------- multiply on the left by Q_k
                QrHelperFunctions_DDRM.rank1UpdateMultL(UBV, u, gamma, k + 1, k + 1, n);

                b[row + k + 1] = -tau * max;
            }
            else
            {
                gammasV[k] = 0;
            }
        }
        private void performDecomposition(DMatrixSparseCSC A)
        {
            int[] w        = gwork.data;
            int[] permCol  = applyReduce.getArrayQ();
            int[] parent   = structure.getParent();
            int[] leftmost = structure.getLeftMost();
            // permutation that was done to ensure all rows have non-zero elements
            int[] pinv_structure = structure.getPinv();
            int   s = m2;

            // clear mark nodes. See addRowsInAInToC
            //Arrays.fill(w, 0, m2, -1);
            for (var i = 0; i < m2; i++)
            {
                w[i] = -1;
            }
            //Arrays.fill(x, 0, m2, 0);
            Array.Clear(x, 0, m2);

            // the counts from structure are actually an upper limit. the actual counts can be lower
            R.nz_length = 0;
            V.nz_length = 0;

            // compute V and R
            for (int k = 0; k < n; k++)
            {
                R.col_idx[k] = R.nz_length;
                int p1 = V.col_idx[k] = V.nz_length;
                w[k] = k;
                V.nz_rows[V.nz_length++] = k; // Add V(k,k) to V's pattern
                int top = n;
                int col = permCol != null ? permCol[k] : k;

                int idx0 = A.col_idx[col];
                int idx1 = A.col_idx[col + 1];

                for (int p = idx0; p < idx1; p++)
                {
                    int i = leftmost[A.nz_rows[p]];
                    int len;
                    for (len = 0; w[i] != k; i = parent[i])
                    {
                        w[s + len++] = i;
                        w[i]         = k;
                    }
                    while (len > 0)
                    {
                        w[s + --top] = w[s + --len];
                    }
                    i    = pinv_structure[A.nz_rows[p]];
                    x[i] = A.nz_values[p];
                    if (i > k && w[i] < k)
                    {
                        V.nz_rows[V.nz_length++] = i;
                        w[i] = k;
                    }
                }
                // apply previously computed Householder vectors to the current columns
                for (int p = top; p < n; p++)
                {
                    int i = w[s + p];
                    QrHelperFunctions_DSCC.applyHouseholder(V, i, beta[i], x);
                    R.nz_rows[R.nz_length]     = i;
                    R.nz_values[R.nz_length++] = x[i];
                    x[i] = 0;
                    if (parent[i] == k)
                    {
                        ImplSparseSparseMult_DSCC.addRowsInAInToC(V, i, V, k, w);
                    }
                }
                for (int p = p1; p < V.nz_length; p++)
                {
                    V.nz_values[p]  = x[V.nz_rows[p]];
                    x[V.nz_rows[p]] = 0;
                }
                R.nz_rows[R.nz_length] = k;
                double max = QrHelperFunctions_DDRM.findMax(V.nz_values, p1, V.nz_length - p1);
                if (max == 0.0)
                {
                    singular = true;
                    R.nz_values[R.nz_length] = 0;
                    beta[k] = 0;
                }
                else
                {
                    R.nz_values[R.nz_length] =
                        QrHelperFunctions_DSCC.computeHouseholder(V.nz_values, p1, V.nz_length, max, Beta);
                    beta[k] = Beta.value;
                }
                R.nz_length++;
            }
            R.col_idx[n] = R.nz_length;
            V.col_idx[n] = V.nz_length;
        }