private static Triple<ClusterViewNode, RowKey, RowKey> buildNewTree(
     final Map<DendrogramNode, Map<Pair<RowKey, RowKey>, Number>> m,
     final DendrogramNode root,
     final Map<RowKey, Pair<DataRow, Integer>> rows,
     ExecutionContext exec)
     throws CanceledExecutionException {
   if (root.isLeaf()) {
     final Pair<DataRow, Integer> leafRow = rows.get(RowKeyHelper.getKey(root));
     return Triple.apply(
         new ClusterViewNode(leafRow.getFirst().getKey()),
         leafRow.getFirst().getKey(),
         leafRow.getFirst().getKey());
   }
   final Triple<ClusterViewNode, RowKey, RowKey> firstTree =
       buildNewTree(m, root.getFirstSubnode(), rows, exec);
   exec.checkCanceled();
   final Triple<ClusterViewNode, RowKey, RowKey> secondTree =
       buildNewTree(m, root.getSecondSubnode(), rows, exec);
   final Map<Pair<RowKey, RowKey>, Number> map = m.get(root);
   Pair<RowKey, RowKey> pairNoChange = Pair.create(firstTree.getO3(), secondTree.getO2());
   if (!map.containsKey(pairNoChange)) {
     pairNoChange = flip(pairNoChange);
   }
   Pair<RowKey, RowKey> pairChange = Pair.create(secondTree.getO3(), firstTree.getO2());
   if (!map.containsKey(pairChange)) {
     pairChange = flip(pairChange);
   }
   assert map.containsKey(pairNoChange);
   assert map.containsKey(pairChange);
   final double noChangeValue = map.get(pairNoChange).doubleValue();
   final double changeValue = map.get(pairChange).doubleValue();
   if (noChangeValue > changeValue) {
     return Triple.apply(
         new ClusterViewNode(firstTree.getO1(), secondTree.getO1(), root.getDist()),
         firstTree.getO2(),
         secondTree.getO3());
   }
   return Triple.apply(
       new ClusterViewNode(secondTree.getO1(), firstTree.getO1(), root.getDist()),
       secondTree.getO2(),
       firstTree.getO3());
 }
 @Override
 protected PortObject[] execute(final PortObject[] inObjects, final ExecutionContext exec)
     throws Exception {
   // Initialising
   exec.setMessage("initialising");
   final ClusterTreeModel model = (ClusterTreeModel) inObjects[0];
   final BufferedDataTable data = (BufferedDataTable) inObjects[1];
   final int distanceColumnIdx =
       data.getDataTableSpec().findColumnIndex(distanceColumnModel.getColumnName());
   final Map<RowKey, DistanceVectorDataValue> distanceMatrix =
       new HashMap<RowKey, DistanceVectorDataValue>();
   for (final DataRow dataRow : data) {
     final DistanceVectorDataValue distanceVector =
         (DistanceVectorDataValue) dataRow.getCell(distanceColumnIdx);
     distanceMatrix.put(dataRow.getKey(), distanceVector);
   }
   exec.setMessage("computing");
   final DendrogramNode origRoot = model.getRoot();
   final Map<Triple<DendrogramNode, RowKey, RowKey>, Number> m =
       visit(
           origRoot,
           new HashMap<Triple<DendrogramNode, RowKey, RowKey>, Number>(),
           distanceMatrix,
           model.getClusterDistances().length + 1,
           exec.createSilentSubExecutionContext(.9));
   final Map<RowKey, Pair<DataRow, Integer>> rows = new HashMap<RowKey, Pair<DataRow, Integer>>();
   int idx = 0;
   for (final DataRow dataRow : data) {
     rows.put(dataRow.getKey(), Pair.create(dataRow, Integer.valueOf(idx++)));
   }
   exec.setMessage("creating final tree");
   final ClusterViewNode tree = buildNewTree(convertM(m), origRoot, rows, exec).getO1();
   final ArrayList<DistanceVectorDataValue>
       origList = new ArrayList<DistanceVectorDataValue>(model.getClusterDistances().length + 1),
       newList = new ArrayList<DistanceVectorDataValue>(model.getClusterDistances().length + 1);
   flatten(origRoot, origList, distanceMatrix);
   exec.checkCanceled();
   flatten(tree, newList, distanceMatrix);
   logger.info("Before:      " + sumDistance(origList));
   logger.info("After:       " + sumDistance(newList));
   final ClusterTreeModel clusterTreeModel =
       new ClusterTreeModel(
           (DataTableSpec) model.getSpec(),
           tree,
           model.getClusterDistances(),
           model.getClusterDistances().length + 1) {
         @Override
         public String getSummary() {
           return "Before: " + sumDistance(origList) + "\nAfter:  " + sumDistance(newList);
         }
       };
   return new PortObject[] {clusterTreeModel};
 }
 private Map<DendrogramNode, Map<Pair<RowKey, RowKey>, Number>> convertM(
     final Map<Triple<DendrogramNode, RowKey, RowKey>, Number> m) {
   final Map<DendrogramNode, Map<Pair<RowKey, RowKey>, Number>> ret =
       new HashMap<DendrogramNode, Map<Pair<RowKey, RowKey>, Number>>();
   for (final Entry<Triple<DendrogramNode, RowKey, RowKey>, Number> entry : m.entrySet()) {
     final Triple<DendrogramNode, RowKey, RowKey> entryKey = entry.getKey();
     final DendrogramNode key = entryKey.getO1();
     if (!ret.containsKey(key)) {
       ret.put(key, new HashMap<Pair<RowKey, RowKey>, Number>());
     }
     // ENH keep only the winner
     ret.get(key).put(Pair.create(entryKey.getO2(), entryKey.getO3()), entry.getValue());
   }
   return ret;
 }
 private static Pair<RowKey, RowKey> flip(Pair<RowKey, RowKey> pairNoChange) {
   return Pair.create(pairNoChange.getSecond(), pairNoChange.getFirst());
 }