gt:           if(c2 == c1) {
context: /*
   * This file is part of ELKI:
   * Environment for Developing KDD-Applications Supported by Index-Structures
   *
   * Copyright (C) 2019
   * ELKI Development Team
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Affero General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   * GNU Affero General Public License for more details.
   *
   * You should have received a copy of the GNU Affero General Public License
   * along with this program. If not, see <http://www.gnu.org/licenses/>.
   */
  package elki.algorithm.statistics;
  
  import java.util.*;
  
  import elki.Algorithm;
  import elki.clustering.trivial.ByLabelOrAllInOneClustering;
  import elki.data.Cluster;
  import elki.data.model.Model;
  import elki.data.type.TypeInformation;
  import elki.data.type.TypeUtil;
  import elki.database.Database;
  import elki.database.ids.*;
  import elki.database.query.QueryBuilder;
  import elki.database.query.distance.DistanceQuery;
  import elki.database.relation.Relation;
  import elki.distance.Distance;
  import elki.distance.minkowski.EuclideanDistance;
  import elki.logging.Logging;
  import elki.logging.progress.FiniteProgress;
  import elki.logging.progress.StepProgress;
  import elki.math.DoubleMinMax;
  import elki.math.MeanVariance;
  import elki.result.HistogramResult;
  import elki.result.Metadata;
  import elki.utilities.datastructures.histogram.AbstractObjDynamicHistogram;
  import elki.utilities.datastructures.histogram.ObjHistogram;
  import elki.utilities.documentation.Description;
  import elki.utilities.documentation.Title;
  import elki.utilities.exceptions.EmptyDataException;
  import elki.utilities.optionhandling.OptionID;
  import elki.utilities.optionhandling.Parameterizer;
  import elki.utilities.optionhandling.constraints.CommonConstraints;
  import elki.utilities.optionhandling.parameterization.Parameterization;
  import elki.utilities.optionhandling.parameters.Flag;
  import elki.utilities.optionhandling.parameters.IntParameter;
  import elki.utilities.optionhandling.parameters.ObjectParameter;
  
  import net.jafama.FastMath;
  
  /**
   * Algorithm to gather statistics over the distance distribution in the data
   * set.
   *
   * @author Erich Schubert
   * @since 0.2
   *
   * @param <O> Object type
   */
  @Title("Distance Histogram")
  @Description("Computes a histogram over the distances occurring in the data set.")
  public class DistanceStatisticsWithClasses<O> implements Algorithm {
    /**
     * The logger for this class.
     */
    private static final Logging LOG = Logging.getLogger(DistanceStatisticsWithClasses.class);
  
    /**
     * Distance function used.
     */
    protected Distance<? super O> distance;
  
    /**
     * Number of bins to use in sampling.
     */
    protected int numbin;
  
    /**
     * Sampling flag.
     */
    protected boolean sampling = false;
  
    /**
     * Compute exactly (slower).
     */
    protected boolean exact = false;
  
    /**
     * Constructor.
     *
     * @param distance Distance function to use
     * @param numbins Number of bins
     * @param exact Exactness flag
     * @param sampling Sampling flag
     */
    public DistanceStatisticsWithClasses(Distance<? super O> distance, int numbins, boolean exact, boolean sampling) {
      super();
      this.distance = distance;
      this.numbin = numbins;
      this.exact = exact;
      this.sampling = sampling;
    }
  
    @Override
    public TypeInformation[] getInputTypeRestriction() {
      return TypeUtil.array(distance.getInputTypeRestriction());
    }
  
    public HistogramResult run(Database database, Relation<O> relation) {
      DistanceQuery<O> dq = new QueryBuilder<>(relation, distance).distanceQuery();
      StepProgress stepprog = LOG.isVerbose() ? new StepProgress("Distance statistics", 2) : null;
  
      // determine binning ranges.
      DoubleMinMax gminmax = new DoubleMinMax();
  
      // Cluster by labels
      Collection<Cluster<Model>> split = (new ByLabelOrAllInOneClustering()).autorun(database).getAllClusters();
  
      // global in-cluster min/max, global other-cluster min/max
      DoubleMinMax giminmax = new DoubleMinMax(), gominmax = new DoubleMinMax();
      // in-cluster distances
      MeanVariance mimin = new MeanVariance(), mimax = new MeanVariance();
      MeanVariance midif = new MeanVariance();
      // other-cluster distances
      MeanVariance momin = new MeanVariance(), momax = new MeanVariance();
      MeanVariance modif = new MeanVariance();
      // Histogram
      LOG.beginStep(stepprog, 1, "Prepare histogram.");
      if(exact) {
        gminmax = exactMinMax(relation, dq);
      }
      else if(sampling) {
        gminmax = sampleMinMax(relation, dq);
      }
      ObjHistogram<long[]> histogram;
      if(gminmax.isValid()) {
        histogram = new ObjHistogram<long[]>(numbin, gminmax.getMin(), gminmax.getMax(), () -> {
          return new long[2];
        });
      }
      else {
        histogram = new AbstractObjDynamicHistogram<long[]>(numbin) {
          @Override
          protected long[] downsample(Object[] data, int start, int end, int size) {
            long[] ret = new long[2];
            for(int i = start; i < end; i++) {
              long[] existing = (long[]) data[i];
              if(existing != null) {
                for(int c = 0; c < 2; c++) {
                  ret[c] += existing[c];
                }
              }
            }
            return ret;
          }
  
          @Override
          protected long[] aggregate(long[] first, long[] second) {
            for(int c = 0; c < 2; c++) {
              first[c] += second[c];
            }
            return first;
          }
  
          @Override
          protected long[] cloneForCache(long[] data) {
            return data.clone();
          }
  
          @Override
          protected long[] makeObject() {
            return new long[2];
          }
        };
      }
  
      LOG.beginStep(stepprog, 2, "Build histogram.");
      final FiniteProgress progress = LOG.isVerbose() ? new FiniteProgress("Distance computations", relation.size(), LOG) : null;
      for(Cluster<?> c1 : split) {
        for(DBIDIter id1 = c1.getIDs().iter(); id1.valid(); id1.advance()) {
          // in-cluster distances
          DoubleMinMax iminmax = new DoubleMinMax();
          for(DBIDIter iter2 = c1.getIDs().iter(); iter2.valid(); iter2.advance()) {
            // skip the point itself.
            if(DBIDUtil.equal(id1, iter2)) {
              continue;
            }
            double d = dq.distance(id1, iter2);
            histogram.get(d)[0]++;
            iminmax.put(d);
          }
          // aggregate
          mimin.put(iminmax.getMin());
          mimax.put(iminmax.getMax());
          midif.put(iminmax.getDiff());
          // min/max
          giminmax.put(iminmax.getMin());
          giminmax.put(iminmax.getMax());
  
          // other-cluster distances
          DoubleMinMax ominmax = new DoubleMinMax();
          for(Cluster<?> c2 : split) {
  