@Override
  public String toString() {
    double[] stats;
    ArrayList<Double> sizes = new ArrayList<Double>(size());
    ArrayList<Double> similarities = new ArrayList<Double>(size() * (size() - 1));

    for (ConicProgramPartition p : partitions) {
      sizes.add(Double.valueOf(p.getCutConstraints().size()));
    }

    stats = meanAndStdDev(sizes);
    double sizeMean = stats[0];
    double sizeStdDev = stats[1];

    Set<LinearConstraint> cutSet1, cutSet2, intersection, union;

    for (int i = 0; i < partitions.size(); i++) {
      for (int j = i + 1; j < partitions.size(); j++) {
        cutSet1 = partitions.get(i).getCutConstraints();
        cutSet2 = partitions.get(j).getCutConstraints();

        intersection = new HashSet<LinearConstraint>(cutSet1);
        intersection.retainAll(cutSet2);
        union = new HashSet<LinearConstraint>(cutSet1);
        union.addAll(cutSet2);

        similarities.add((double) intersection.size() / union.size());
      }
    }

    stats = meanAndStdDev(similarities);

    double simMean = stats[0];
    double simStdDev = stats[1];
    String toReturn =
        "Complete Partition\n"
            + "Size: "
            + size()
            + "\n"
            + "Mean cut set size: "
            + sizeMean
            + "\n"
            + "Std. dev.: "
            + sizeStdDev
            + "\n"
            + "Mean pairwise similarity: "
            + simMean
            + "\n"
            + "Std. dev.: "
            + simStdDev;

    for (ConicProgramPartition p : partitions) {
      toReturn = toReturn + "\n" + p.getCutConstraints().size();
    }

    return toReturn;
  }
  protected void reorderPartitions() {
    if (partitions.size() > 1) {
      Vector<ConicProgramPartition> newOrdering = new Vector<ConicProgramPartition>();
      Set<ConicProgramPartition> remainingPartitions =
          new HashSet<ConicProgramPartition>(partitions);

      Set<LinearConstraint> cutSet1, cutSet2, intersection, union;
      ConicProgramPartition bestPartition1 = null;
      ConicProgramPartition bestPartition2 = null;
      double similarity;
      double lowestSimilarity = 1.0;

      for (int i = 0; i < partitions.size(); i++) {
        for (int j = i + 1; j < partitions.size(); j++) {
          cutSet1 = partitions.get(i).getCutConstraints();
          cutSet2 = partitions.get(j).getCutConstraints();

          intersection = new HashSet<LinearConstraint>(cutSet1);
          intersection.retainAll(cutSet2);
          union = new HashSet<LinearConstraint>(cutSet1);
          union.addAll(cutSet2);
          similarity = (double) intersection.size() / union.size();

          if (bestPartition1 == null || similarity < lowestSimilarity) {
            bestPartition1 = partitions.get(i);
            bestPartition2 = partitions.get(j);
            lowestSimilarity = similarity;
          }
        }
      }

      newOrdering.add(bestPartition1);
      newOrdering.add(bestPartition2);
      remainingPartitions.remove(bestPartition1);
      remainingPartitions.remove(bestPartition2);

      while (!remainingPartitions.isEmpty()) {
        bestPartition1 = null;
        lowestSimilarity = 1.0;
        cutSet1 = newOrdering.lastElement().getCutConstraints();

        for (ConicProgramPartition p : remainingPartitions) {
          cutSet2 = p.getCutConstraints();

          intersection = new HashSet<LinearConstraint>(cutSet1);
          intersection.retainAll(cutSet2);
          union = new HashSet<LinearConstraint>(cutSet1);
          union.addAll(cutSet2);
          similarity = (double) intersection.size() / union.size();

          if (bestPartition1 == null || similarity < lowestSimilarity) {
            bestPartition1 = p;
            lowestSimilarity = similarity;
          }
        }

        newOrdering.add(bestPartition1);
        remainingPartitions.remove(bestPartition1);
      }

      partitions = newOrdering;
    }
  }
 @Override
 public void checkInAllMatrices() {
   for (ConicProgramPartition p : partitions) p.checkInMatrices();
 }