code: @Override
      public QR qr() {
          // Initialize.
          int m = nrows();
          int n = ncols();
          double[] rDiagonal = new double[n];
  
          // Main loop.
          for (int k = 0; k < n; k++) {
              // Compute 2-norm of k-th column without under/overflow.
              double nrm = 0.0;
              for (int i = k; i < m; i++) {
                  nrm = Math.hypot(nrm, get(i, k));
              }
  
              if (nrm != 0.0) {
                  // Form k-th Householder vector.
                  if (get(k, k) < 0) {
                      nrm = -nrm;
                  }
                  for (int i = k; i < m; i++) {
                      div(i, k, nrm);
                  }
                  add(k, k, 1.0);
  
                  // Apply transformation to remaining columns.
                  for (int j = k + 1; j < n; j++) {
                      double s = 0.0;
                      for (int i = k; i < m; i++) {
                          s += get(i, k) * get(i, j);
                      }
                      s = -s / get(k, k);
                      for (int i = k; i < m; i++) {
                          add(i, j, s * get(i, k));
                      }
                  }
              }
              rDiagonal[k] = -nrm;
          }
  
          boolean singular = false;
          for (int j = 0; j < rDiagonal.length; j++) {
              if (rDiagonal[j] == 0) {
                  singular = true;
                  break;
              }
          }
  
          return new QR(this, rDiagonal, singular);
      }
docs: QR Decomposition is computed by Householder reflections.