docs: public OutlierResult run(Relation<O> relation) {
      DistanceQuery<O> dq = relation.getDistanceQuery(getDistanceFunction());
      final double logPerp = FastMath.log(perplexity);
  
      ModifiableDoubleDBIDList dlist = DBIDUtil.newDistanceDBIDList(relation.size() - 1);
      DoubleDBIDListMIter di = dlist.iter();
      double[] p = new double[relation.size() - 1];
      FiniteProgress prog = LOG.isVerbose() ? new FiniteProgress("SOS scores", relation.size(), LOG) : null;
      WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_HOT | DataStoreFactory.HINT_DB, 1.);
      for(DBIDIter it = relation.iterDBIDs(); it.valid(); it.advance()) {
        // Build sorted neighbors list.
        dlist.clear();
        for(DBIDIter i2 = relation.iterDBIDs(); i2.valid(); i2.advance()) {
          if(DBIDUtil.equal(it, i2)) {
            continue;
          }
          dlist.add(dq.distance(it, i2), i2);
        }
        dlist.sort();
        // Compute affinities
        computePi(it, di, p, perplexity, logPerp);
        // Normalization factor:
        double s = sumOfProbabilities(it, di, p);
        if(s > 0) {
          nominateNeighbors(it, di, p, 1. / s, scores);
        }
        LOG.incrementProcessed(prog);
      }
      LOG.ensureCompleted(prog);
      // Find minimum and maximum.
      DoubleMinMax minmax = new DoubleMinMax();
      for(DBIDIter it2 = relation.iterDBIDs(); it2.valid(); it2.advance()) {
        minmax.put(scores.doubleValue(it2));
      }
      DoubleRelation scoreres = new MaterializedDoubleRelation("Stoachastic Outlier Selection", "sos-outlier", scores, relation.getDBIDs());
      OutlierScoreMeta meta = new ProbabilisticOutlierScore(minmax.getMin(), minmax.getMax(), 0.);
      return new OutlierResult(meta, scoreres);
    }