docs: public static Cluster repartition(final Cluster currentCluster,
                                        final List<StoreDefinition> currentStoreDefs,
                                        final Cluster interimCluster,
                                        final List<StoreDefinition> finalStoreDefs,
                                        final String outputDir,
                                        final int attempts,
                                        final boolean disableNodeBalancing,
                                        final boolean disableZoneBalancing,
                                        final boolean enableRandomSwaps,
                                        final int randomSwapAttempts,
                                        final int randomSwapSuccesses,
                                        final List<Integer> randomSwapZoneIds,
                                        final boolean enableGreedySwaps,
                                        final int greedySwapAttempts,
                                        final int greedySwapMaxPartitionsPerNode,
                                        final int greedySwapMaxPartitionsPerZone,
                                        final List<Integer> greedySwapZoneIds,
                                        final int maxContiguousPartitionsPerZone) {
          PartitionBalance partitionBalance = new PartitionBalance(currentCluster, currentStoreDefs);
          RebalanceUtils.dumpAnalysisToFile(outputDir,
                                            RebalanceUtils.currentClusterFileName,
                                            partitionBalance);
  
          Cluster minCluster = interimCluster;
  
          double minUtility = Double.MAX_VALUE;
  
          for(int attempt = 0; attempt < attempts; attempt++) {
              Cluster nextCandidateCluster = interimCluster;
  
              if(maxContiguousPartitionsPerZone > 0) {
                  nextCandidateCluster = repeatedlyBalanceContiguousPartitionsPerZone(nextCandidateCluster,
                                                                                      maxContiguousPartitionsPerZone);
              }
  
              if(!disableNodeBalancing) {
                  nextCandidateCluster = balancePrimaryPartitions(nextCandidateCluster,
                                                                  !disableZoneBalancing);
              }
  
              if(enableRandomSwaps) {
                  nextCandidateCluster = randomShufflePartitions(nextCandidateCluster,
                                                                 randomSwapAttempts,
                                                                 randomSwapSuccesses,
                                                                 randomSwapZoneIds,
                                                                 finalStoreDefs);
              }
              if(enableGreedySwaps) {
                  nextCandidateCluster = greedyShufflePartitions(nextCandidateCluster,
                                                                 greedySwapAttempts,
                                                                 greedySwapMaxPartitionsPerNode,
                                                                 greedySwapMaxPartitionsPerZone,
                                                                 greedySwapZoneIds,
                                                                 finalStoreDefs);
              }
              RebalanceUtils.validateCurrentFinalCluster(currentCluster, nextCandidateCluster);
  
              System.out.println("-------------------------\n");
              partitionBalance = new PartitionBalance(nextCandidateCluster, finalStoreDefs);
              double currentUtility = partitionBalance.getUtility();
              System.out.println("Optimization number " + attempt + ": " + currentUtility
                                 + " max/min ratio");
              System.out.println("-------------------------\n");
              System.out.println(PartitionBalanceUtils.analyzeInvalidMetadataRate(interimCluster,
                                                                                  finalStoreDefs,
                                                                                  nextCandidateCluster,
                                                                                  finalStoreDefs));
  
              if(currentUtility <= minUtility) {
                  minUtility = currentUtility;
                  minCluster = nextCandidateCluster;
  
                  RebalanceUtils.dumpClusterToFile(outputDir, RebalanceUtils.finalClusterFileName
                                                              + attempt, minCluster);
                  RebalanceUtils.dumpAnalysisToFile(outputDir, RebalanceUtils.finalClusterFileName
                                                               + attempt, partitionBalance);
              }
              System.out.println("-------------------------\n");
          }
  
          System.out.println("\n==========================");
          System.out.println("Final distribution");
          partitionBalance = new PartitionBalance(minCluster, finalStoreDefs);
          System.out.println(partitionBalance);
  
          RebalanceUtils.dumpClusterToFile(outputDir, RebalanceUtils.finalClusterFileName, minCluster);
          RebalanceUtils.dumpAnalysisToFile(outputDir,
                                            RebalanceUtils.finalClusterFileName,
                                            partitionBalance);
          return minCluster;
      }