public List<Cluster> performClustering(double[][] distances
			, String[] clusterNames
			, LinkingRule linkingRule
			, int k
			, Matrix features) {

		/* Argument checks */
		if (distances == null || distances.length == 0
		        || distances[0].length != distances.length) {
			throw new IllegalArgumentException("Invalid distance matrix");
		}
		if (distances.length != clusterNames.length) {
			throw new IllegalArgumentException("Invalid cluster name array");
		}
		if (linkingRule == null) {
			throw new IllegalArgumentException("Undefined linkage strategy");
		}

		/* Setup model */
		List<Cluster> clusters = createClusters(clusterNames);
		List<ClusterPair> linkages = createLinkages(distances, clusters);

		/* Process */
		HierarchyBuilder builder = 
				new HierarchyBuilder(clusters
								   , linkages
								   , k);
		int counter = 0;
		while (!builder.isTreeComplete()) {
			builder.agglomerate(linkingRule, counter, features);
			counter++;
		}

		return builder.getRootCluster();
	}