public void CreateHierarchicalEdges() { // Starting from level 2 denotes a serious mess on design, because lvl 1 is // used by the clusters. for (var level = 2; level <= MaxLevel; level++) { SetCurrentLevelForSearches(level - 1); int n = 1 << (level - 1); // Group clusters by their level. Each subsequent level doubles the amount of clusters in each group var clusterGroups = Clusters.GroupBy(cl => $"{cl.ClusterX / n}_{cl.ClusterY / n}"); foreach (var clusterGroup in clusterGroups) { var entrancesInClusterGroup = clusterGroup .SelectMany(cl => cl.EntrancePoints) .Where(entrance => GetEntrancePointLevel(entrance) >= level) .ToList(); var firstEntrance = entrancesInClusterGroup.FirstOrDefault(); if (firstEntrance == null) { continue; } var entrancePosition = AbstractGraph.GetNode(firstEntrance.AbstractNodeId).Info.Position; SetCurrentClusterByPositionAndLevel( entrancePosition, level); foreach (var entrance1 in entrancesInClusterGroup) { foreach (var entrance2 in entrancesInClusterGroup) { if (entrance1 == entrance2 || !IsValidAbstractNodeForLevel(entrance1.AbstractNodeId, level) || !IsValidAbstractNodeForLevel(entrance2.AbstractNodeId, level)) { continue; } AddEdgesBetweenAbstractNodes(entrance1.AbstractNodeId, entrance2.AbstractNodeId, level); } } } } }