docs: public static int searchNzRowsInX(DMatrixSparseCSC G, DMatrixSparseCSC B, int colB, int pinv[],
                                        int xi[], int w[]) {
  
          int X_rows = G.numCols;
          if (xi.length < X_rows)
              throw new IllegalArgumentException("xi must be at least G.numCols=" + G.numCols);
          if( w.length < 2*X_rows)
              throw new IllegalArgumentException("w must be at least 2*G.numCols in length (2*number of rows in X) and first N elements must be zero");
  
          // Here is a change from csparse. CSparse modifies G by "marking" elements in it (making them negative) then
          // undoing it. That's undesirable because most people don't read the documentation and if a matrix is used
          // in multiple threads it will have erratic behavior. However, by doing that they avoid an O(N) fill each iteration.
          //
          // Instead,the w array is filled with 0 once before this function is called. Marked nodes are then set back to
          // 0 when it's done. Thus a one time extra cost of N is the price of not modifying G.
          // This is much better than N*N
  
          int idx0 = B.col_idx[colB];
          int idx1 = B.col_idx[colB+1];
  
          int top = X_rows;
          for (int i = idx0; i < idx1; i++) {
              int rowB = B.nz_rows[i];
  
              if( rowB < X_rows  && w[rowB] == 0) {
                  top = searchNzRowsInX_DFS(rowB,G,top,pinv,xi,w);
              }
          }
  
          // Undo the marking only on the stack nodes
          for (int i = top; i < X_rows; i++) {
              w[xi[i]] = 0;
          }
  
          return top;
      }