docs: private void buildHierarchy(Relation<V> database, Clustering<SubspaceModel> clustering, List<Cluster<SubspaceModel>> clusters, int dimensionality) {
      StringBuilder msg = LOG.isDebugging() ? new StringBuilder() : null;
      final int db_dim = RelationUtil.dimensionality(database);
      Hierarchy<Cluster<SubspaceModel>> hier = clustering.getClusterHierarchy();
  
      for(int i = 0; i < clusters.size() - 1; i++) {
        Cluster<SubspaceModel> c_i = clusters.get(i);
        final Subspace s_i = c_i.getModel().getSubspace();
        int subspaceDim_i = dimensionality - s_i.dimensionality();
        NumberVector ci_centroid = ProjectedCentroid.make(s_i.getDimensions(), database, c_i.getIDs());
        long[] pv1 = s_i.getDimensions();
  
        for(int j = i + 1; j < clusters.size(); j++) {
          Cluster<SubspaceModel> c_j = clusters.get(j);
          final Subspace s_j = c_j.getModel().getSubspace();
          int subspaceDim_j = dimensionality - s_j.dimensionality();
  
          if(subspaceDim_i < subspaceDim_j) {
            if(msg != null) {
              msg.append("\n l_i=").append(subspaceDim_i).append(" pv_i=[").append(BitsUtil.toStringLow(s_i.getDimensions(), db_dim)).append(']') //
                  .append("\n l_j=").append(subspaceDim_j).append(" pv_j=[").append(BitsUtil.toStringLow(s_j.getDimensions(), db_dim)).append(']');
            }
  
            // noise level reached
            if(s_j.dimensionality() == 0) {
              // no parents exists -> parent is noise
              if(hier.numParents(c_i) == 0) {
                clustering.addChildCluster(c_j, c_i);
                if(msg != null) {
                  msg.append("\n [").append(BitsUtil.toStringLow(s_j.getDimensions(), db_dim)) //
                      .append("] is parent of [").append(BitsUtil.toStringLow(s_i.getDimensions(), db_dim)).append(']');
                }
              }
            }
            else {
              NumberVector cj_centroid = ProjectedCentroid.make(c_j.getModel().getDimensions(), database, c_j.getIDs());
              long[] pv2 = s_j.getDimensions();
              long[] commonPreferenceVector = BitsUtil.andCMin(pv1, pv2);
              int subspaceDim = subspaceDimensionality(ci_centroid, cj_centroid, pv1, pv2, commonPreferenceVector);
  
              double d = weightedDistance(ci_centroid, cj_centroid, commonPreferenceVector);
              if(msg != null) {
                msg.append("\n dist = ").append(subspaceDim);
              }
  
              if(subspaceDim == subspaceDim_j) {
                if(msg != null) {
                  msg.append("\n d = ").append(d);
                }
                if(d <= 2 * epsilon) {
                  // no parent exists or c_j is not a parent of the already
                  // existing parents
                  if(hier.numParents(c_i) == 0 || !isParent(database, c_j, hier.iterParents(c_i), db_dim)) {
                    clustering.addChildCluster(c_j, c_i);
                    if(msg != null) {
                      msg.append("\n [").append(BitsUtil.toStringLow(s_j.getDimensions(), db_dim)) //
                          .append("] is parent of [") //
                          .append(BitsUtil.toStringLow(s_i.getDimensions(), db_dim)).append(']');
                    }
                  }
                }
                else {
                  throw new RuntimeException("Should never happen: d = " + d);
                }
              }
            }
          }
        }
      }
      if(msg != null) {
        LOG.debug(msg.toString());
      }
    }