docs: protected double cluster(DataSet data, boolean doInit, int[] medioids, int[] assignments, List<Double> cacheAccel, boolean parallel)
      {
          DoubleAdder totalDistance =new DoubleAdder();
          LongAdder changes = new LongAdder();
          Arrays.fill(assignments, -1);//-1, invalid category!
          
          int[] bestMedCand = new int[medioids.length];
          double[] bestMedCandDist = new double[medioids.length];
          List<Vec> X = data.getDataVectors();
          final List<Double> accel;
          
          if(doInit)
          {
              TrainableDistanceMetric.trainIfNeeded(dm, data);
              accel = dm.getAccelerationCache(X);
              selectIntialPoints(data, medioids, dm, accel, rand, seedSelection);
          }
          else
              accel = cacheAccel;
  
          int iter = 0;
          do
          {
              changes.reset();
              totalDistance.reset();
              
              ParallelUtils.run(parallel, data.size(), (start, end)->
              {
                  for(int i = start; i < end; i++)
                  {
                      int assignment = 0;
                      double minDist = dm.dist(medioids[0], i, X, accel);
  
                      for (int k = 1; k < medioids.length; k++)
                      {
                          double dist = dm.dist(medioids[k], i, X, accel);
                          if (dist < minDist)
                          {
                              minDist = dist;
                              assignment = k;
                          }
                      }
  
                      //Update which cluster it is in
                      if (assignments[i] != assignment)
                      {
                          changes.increment();
                          assignments[i] = assignment;
                      }
                      totalDistance.add(minDist * minDist);
                  }
              });
              
              //TODO this update may be faster by using more memory, and actually moiving people about in the assignment loop above
              //Update the medoids
              Arrays.fill(bestMedCandDist, Double.MAX_VALUE);
              for(int i = 0; i < data.size(); i++)
              {
                  double thisCandidateDistance;
                  final int clusterID = assignments[i];
                  final int medCandadate = i;
                  
                  final int ii = i;
                  thisCandidateDistance = ParallelUtils.range(data.size(), parallel)
                          .filter(j -> j != ii && assignments[j] == clusterID)
                          .mapToDouble(j -> Math.pow(dm.dist(medCandadate, j, X, accel), 2))
                          .sum();
                  
                  if(thisCandidateDistance < bestMedCandDist[clusterID])
                  {
                      bestMedCand[clusterID] = i;
                      bestMedCandDist[clusterID] = thisCandidateDistance;
                  }
              }
              System.arraycopy(bestMedCand, 0, medioids, 0, medioids.length);
          }
          while( changes.sum() > 0 && iter++ < iterLimit);
          
          return totalDistance.sum();
      }