docs: protected double computePLOFs(Relation<O> relation, KNNQuery<O> knn, WritableDoubleDataStore pdists, WritableDoubleDataStore plofs) {
      FiniteProgress progressPLOFs = LOG.isVerbose() ? new FiniteProgress("PLOFs for objects", relation.size(), LOG) : null;
      double nplof = 0.;
      for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
        final KNNList neighbors = knn.getKNNForDBID(iditer, kcomp + 1); // + query
                                                                        // point
        // use first kref neighbors as comparison set.
        int ks = 0;
        double sum = 0.;
        for(DBIDIter neighbor = neighbors.iter(); neighbor.valid() && ks < kcomp; neighbor.advance()) {
          if(DBIDUtil.equal(neighbor, iditer)) {
            continue;
          }
          sum += pdists.doubleValue(neighbor);
          ks++;
        }
        double plof = MathUtil.max(pdists.doubleValue(iditer) * ks / sum, 1.0);
        if(Double.isNaN(plof) || Double.isInfinite(plof)) {
          plof = 1.0;
        }
        plofs.putDouble(iditer, plof);
        nplof += (plof - 1.0) * (plof - 1.0);
  
        LOG.incrementProcessed(progressPLOFs);
      }
      LOG.ensureCompleted(progressPLOFs);
  
      nplof = lambda * FastMath.sqrt(nplof / relation.size());
      if(LOG.isDebuggingFine()) {
        LOG.debugFine("nplof normalization factor is " + nplof);
      }
      return nplof > 0. ? nplof : 1.;
    }