docs: public static Cluster swapGreedyRandomPartitions(final Cluster nextCandidateCluster,
                                                       final List<Integer> nodeIds,
                                                       final int greedySwapMaxPartitionsPerNode,
                                                       final int greedySwapMaxPartitionsPerZone,
                                                       List<StoreDefinition> storeDefs) {
  
          System.out.println("GreedyRandom : nodeIds:" + nodeIds);
          Cluster returnCluster = Cluster.cloneCluster(nextCandidateCluster);
          double currentUtility = new PartitionBalance(returnCluster, storeDefs).getUtility();
          int nodeIdA = -1;
          int nodeIdB = -1;
          int partitionIdA = -1;
          int partitionIdB = -1;
  
          for(int nodeIdAPrime: nodeIds) {
              System.out.println("GreedyRandom : processing nodeId:" + nodeIdAPrime);
              List<Integer> partitionIdsAPrime = new ArrayList<Integer>();
              partitionIdsAPrime.addAll(returnCluster.getNodeById(nodeIdAPrime).getPartitionIds());
              Collections.shuffle(partitionIdsAPrime);
  
              int maxPartitionsInAPrime = Math.min(greedySwapMaxPartitionsPerNode,
                                                   partitionIdsAPrime.size());
  
              for(int offsetAPrime = 0; offsetAPrime < maxPartitionsInAPrime; offsetAPrime++) {
                  Integer partitionIdAPrime = partitionIdsAPrime.get(offsetAPrime);
                  List<Pair<Integer, Integer>> partitionIdsZone = new ArrayList<Pair<Integer, Integer>>();
                  for(int nodeIdBPrime: nodeIds) {
                      if(nodeIdBPrime == nodeIdAPrime)
                          continue;
                      for(Integer partitionIdBPrime: returnCluster.getNodeById(nodeIdBPrime)
                                                                  .getPartitionIds()) {
                          partitionIdsZone.add(new Pair<Integer, Integer>(nodeIdBPrime,
                                                                          partitionIdBPrime));
                      }
                  }
  
                  Collections.shuffle(partitionIdsZone);
                  int maxPartitionsInZone = Math.min(greedySwapMaxPartitionsPerZone,
                                                     partitionIdsZone.size());
                  for(int offsetZone = 0; offsetZone < maxPartitionsInZone; offsetZone++) {
                      Integer nodeIdBPrime = partitionIdsZone.get(offsetZone).getFirst();
                      Integer partitionIdBPrime = partitionIdsZone.get(offsetZone).getSecond();
                      Cluster swapResult = swapPartitions(returnCluster,
                                                          nodeIdAPrime,
                                                          partitionIdAPrime,
                                                          nodeIdBPrime,
                                                          partitionIdBPrime);
                      double swapUtility = new PartitionBalance(swapResult, storeDefs).getUtility();
                      if(swapUtility < currentUtility) {
                          currentUtility = swapUtility;
                          System.out.println(" -> " + currentUtility);
                          nodeIdA = nodeIdAPrime;
                          partitionIdA = partitionIdAPrime;
                          nodeIdB = nodeIdBPrime;
                          partitionIdB = partitionIdBPrime;
                      }
                  }
              }
          }
  
          if(nodeIdA == -1) {
              return returnCluster;
          }
          return swapPartitions(returnCluster, nodeIdA, partitionIdA, nodeIdB, partitionIdB);
      }