docs: public OutlierResult run(Relation<V> relation) {
      // Use an array list of object IDs for fast random access by an offset
      ArrayDBIDs objids = DBIDUtil.ensureArray(relation.getDBIDs());
      // Build entire covariance matrix:
      CovarianceMatrix builder = CovarianceMatrix.make(relation, objids);
  
      // Anomalous objects
      HashSetModifiableDBIDs anomalous = DBIDUtil.newHashSet();
      // resulting scores
      WritableDoubleDataStore oscores = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_TEMP | DataStoreFactory.HINT_HOT);
      // compute loglikelihood
      double logLike = objids.size() * logml + loglikelihoodNormal(objids, anomalous, builder, relation);
  
      DoubleMinMax minmax = new DoubleMinMax();
      for(int loop = 0; loop < MAX_ITER; ++loop) {
        boolean changed = false;
        for(DBIDIter iter = objids.iter(); iter.valid(); iter.advance()) {
          // Change mask to make the current Fobject anomalous
          boolean wasadded = anomalous.add(iter) || !anomalous.remove(iter);
          final V vec = relation.get(iter);
          builder.put(vec, wasadded ? -1 : +1); // Remove
          // Compute new likelihoods
          double currentLogLike = (objids.size() - anomalous.size()) * logml + loglikelihoodNormal(objids, anomalous, builder, relation) //
              + anomalous.size() * logl + loglikelihoodAnomalous(anomalous);
  
          // if the loglike increases more than a threshold, the object remains in
          // the assigned state:
          final double loglikeGain = currentLogLike - logLike;
          oscores.putDouble(iter, loglikeGain);
          minmax.put(loglikeGain);
  
          if(loglikeGain > c) {
            logLike = currentLogLike;
            changed = true;
          }
          else {
            // remove from anomalous again
            wasadded = wasadded ? anomalous.remove(iter) : !anomalous.add(iter);
            builder.put(vec, wasadded ? +1 : -1);
          }
        }
        assert anomalous.size() == Math.round(objids.size() - builder.getWeight());
        if(!changed) {
          break;
        }
      }
  
      OutlierScoreMeta meta = new BasicOutlierScoreMeta(minmax.getMin(), minmax.getMax(), Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 0.0);
  
      DoubleRelation res = new MaterializedDoubleRelation("Gaussian Mixture Outlier Score", "gaussian-mixture-outlier", oscores, relation.getDBIDs());
      return new OutlierResult(meta, res);
    }