docs: public OutlierResult run(Database database, Relation<?> relation) {
      WritableDoubleDataStore scores = DataStoreUtil.makeDoubleStorage(relation.getDBIDs(), DataStoreFactory.HINT_STATIC);
  
      DoubleMinMax minmax = new DoubleMinMax();
  
      try (FileInputStream fis = new FileInputStream(file); //
          InputStream in = FileUtil.tryGzipInput(fis); //
          TokenizedReader reader = CSVReaderFormat.DEFAULT_FORMAT.makeReader()) {
        Tokenizer tokenizer = reader.getTokenizer();
        CharSequence buf = reader.getBuffer();
        Matcher mi = idpattern.matcher(buf), ms = scorepattern.matcher(buf);
        reader.reset(in);
        while(reader.nextLineExceptComments()) {
          Integer id = null;
          double score = Double.NaN;
          for(/* initialized by nextLineExceptComments */; tokenizer.valid(); tokenizer.advance()) {
            mi.region(tokenizer.getStart(), tokenizer.getEnd());
            ms.region(tokenizer.getStart(), tokenizer.getEnd());
            final boolean mif = mi.find(), msf = ms.find();
            if(mif && msf) {
              throw new AbortException("ID pattern and score pattern both match value: " + tokenizer.getSubstring());
            }
            if(mif) {
              if(id != null) {
                throw new AbortException("ID pattern matched twice: previous value " + id + " second value: " + tokenizer.getSubstring());
              }
              id = ParseUtil.parseIntBase10(buf, mi.end(), tokenizer.getEnd());
            }
            if(msf) {
              if(!Double.isNaN(score)) {
                throw new AbortException("Score pattern matched twice: previous value " + score + " second value: " + tokenizer.getSubstring());
              }
              score = ParseUtil.parseDouble(buf, ms.end(), tokenizer.getEnd());
            }
          }
          if(id != null && !Double.isNaN(score)) {
            scores.putDouble(DBIDUtil.importInteger(id), score);
            minmax.put(score);
          }
          else if(id == null && Double.isNaN(score)) {
            LOG.warning("Line did not match either ID nor score nor comment: " + reader.getLineNumber());
          }
          else {
            throw new AbortException("Line matched only ID or only SCORE patterns: " + reader.getLineNumber());
          }
        }
      }
      catch(IOException e) {
        throw new AbortException("Could not load outlier scores: " + e.getMessage() + " when loading " + file, e);
      }
  
      OutlierScoreMeta meta = inverted //
          ? new InvertedOutlierScoreMeta(minmax.getMin(), minmax.getMax()) //
          : new BasicOutlierScoreMeta(minmax.getMin(), minmax.getMax());
      DoubleRelation scoresult = new MaterializedDoubleRelation("External Outlier", "external-outlier", scores, relation.getDBIDs());
      OutlierResult or = new OutlierResult(meta, scoresult);
  
      // Apply scaling
      if(scaling instanceof OutlierScaling) {
        ((OutlierScaling) scaling).prepare(or);
      }
      DoubleMinMax mm = new DoubleMinMax();
      for(DBIDIter iditer = relation.iterDBIDs(); iditer.valid(); iditer.advance()) {
        double val = scoresult.doubleValue(iditer);
        val = scaling.getScaled(val);
        scores.putDouble(iditer, val);
        mm.put(val);
      }
      meta = new BasicOutlierScoreMeta(mm.getMin(), mm.getMax());
      return new OutlierResult(meta, scoresult);
    }