private void createOrUpdateIdealStateForFullAuto(String dcName, Map<String, Set<String>> partitionsToInstancesInDc,
      TreeMap<Integer, Set<String>> resourceIdToInstances) {
    // The remaining partition would form a list of instance group. Each group would be added to the new partitions
    int remainingPartition = partitionsToInstancesInDc.size();
    List<Set<String>> instanceGroups = groupInstancesBasedOnSharedPartition(partitionsToInstancesInDc);
    Map<String, Set<String>> instanceToPartitions = generateInstanceToPartitionsMap(partitionsToInstancesInDc);
    info("[{}] Found {} groups of instances for cluster {}", dcName.toUpperCase(), instanceGroups.size(), clusterName);
    Iterator<Set<String>> instanceGroupIterator = instanceGroups.iterator();
    int startResourceId = FULL_AUTO_COMPATIBLE_RESOURCE_NAME_START_NUMBER;
    if (!resourceIdToInstances.isEmpty()) {
      Map.Entry<Integer, Set<String>> lastEntry = resourceIdToInstances.lastEntry();
      startResourceId = lastEntry.getKey().intValue() + 1;
    }
    // Break these smaller instance groups into bigger new groups
    List<Set<String>> newGroups = new ArrayList<>();
    Set<String> currentInstances = new HashSet<>();
    while (instanceGroupIterator.hasNext()) {
      Set<String> group = instanceGroupIterator.next();
      if (currentInstances.size() + group.size() > maxInstancesInOneResourceForFullAuto) {
        newGroups.add(new HashSet<>(currentInstances));
        currentInstances.clear();
      }
      currentInstances.addAll(group);
    }
    if (currentInstances.size() != 0) {
      newGroups.add(currentInstances);
    }

    for (Set<String> instances : newGroups) {
      Map<Integer, Set<String>> partitionGroups = new HashMap<>();
      // Get all the partitions that belong to these instances, but categorize them by the number of replicas.
      for (String instance : instances) {
        for (String partition : instanceToPartitions.get(instance)) {
          int numReplica = partitionsToInstancesInDc.get(partition).size();
          partitionGroups.computeIfAbsent(numReplica, k -> new HashSet<>()).add(partition);
        }
      }
      // Partitions that have more replicas will be created first
      List<Integer> numReplicas = new ArrayList<>(partitionGroups.keySet());
      Collections.sort(numReplicas, Collections.reverseOrder());
      for (int numReplica : numReplicas) {
        String resourceName = Integer.toString(startResourceId);
        List<Map.Entry<String, Set<String>>> partitionsUnderResource = partitionGroups.get(numReplica)
            .stream()
            .map(p -> new AbstractMap.SimpleEntry<>(p, partitionsToInstancesInDc.get(p)))
            .collect(Collectors.toList());
        buildAndCreateIdealState(dcName, resourceName, partitionsUnderResource);
        startResourceId++;
      }
    }
    info("[{}] Successfully added {} partitions to cluster {}", dcName.toUpperCase(), remainingPartition, clusterName);
  }
Create or update ideal state for given partition layout when the resources and static layout are both FULL_AUTO
compatible. When calling this method, make sure that given partitions' instances are not assigned to any existing
resources, so they are all new partitions in new instances.
This method would first find all the instance group and partitions that belong to each group. It will then try to
merge those groups if the size of the group is less than the max number of instances in one resource. Also this
method would try to merge with last existing resource if the size allows.
@param dcName The name of the datacenter
@param partitionsToInstancesInDc The map of partition layout.
@param resourceIdToInstances The map from resource id to a list of instance names that are assigned to this resource
ambry-clustermap/src/main/java/com/github/ambry/clustermap/HelixBootstrapUpgradeUtil.java