@Override
  public SuitableOptions clone() {

    SuitableOptions cloned = new SuitableOptions();

    // Clone moduleName
    for (String moduleName : moduleNames) {
      cloned.moduleNames.add(moduleName);
    }

    // Clone suitableOptionsNames
    for (List<String> l : suitableOptionsNames) {

      List<String> clonedList = (List<String>) new ArrayList<String>();

      for (String option : l) {
        clonedList.add(option);
      }
      cloned.suitableOptionsNames.add(clonedList);
    }

    // Clone suitableOptionsCharacteristics
    for (List<CloudOffer> l : suitableOptionsCharacteristics) {

      List<CloudOffer> clonedList2 = (List<CloudOffer>) new ArrayList<CloudOffer>();

      for (CloudOffer option : l) {
        clonedList2.add(option.clone());
      }
      cloned.suitableOptionsCharacteristics.add(clonedList2);
    }

    cloned.setLatencyInternetMillis(latencyInternetMillis);
    cloned.setLatencyDatacenterMillis(latencyDatacenterMillis);

    return cloned;
  }
  public boolean existsAlternativeCloudProviderForModuleWithLowerAvailability(
      String modulename, String cloudOffer) {
    List<CloudOffer> offers = getCloudOffersForModule(modulename);
    CloudOffer currentOffer = getCloudCharacteristicsFromList(offers, cloudOffer);

    for (CloudOffer offer : offers) {
      // series of conditions in AND that I prefer to nest for visibility
      if (offer.getAvailability() < currentOffer.getAvailability()) {
        if (!(offer.getProviderName().equals(currentOffer.getProviderName()))) {
          return true;
        }
      }
    }
    return false;
  }
  /**
   * @param modulename
   * @param cloudOffer
   * @return whether exists a worse offer in terms of performance of the same provider than
   *     cloudOffer.
   */
  public boolean existsOfferWithWorsePerformanceOfSameProvider(
      String modulename, String cloudOffer) {
    List<CloudOffer> offers = getCloudOffersForModule(modulename);
    CloudOffer currentOffer = getCloudCharacteristicsFromList(offers, cloudOffer);

    for (CloudOffer offer : offers) {
      // series of conditions in AND that I prefer to nest for visibility
      if (offer.getPerformance() < currentOffer.getPerformance()) {
        if (offer.getProviderName().equals(currentOffer.getProviderName())) {
          return true;
        }
      }
    }
    return false;
  }
  /**
   * @param modulename
   * @param currentCloudOffer
   * @return The offer in terms of performance of the same provider that is immediately worse than
   *     cloudOffer. It is assumed that ArraysOfcloudOffer are ordered by
   *     "CloudOptionReversePerformanceComparator"
   */
  public CloudOffer getOfferImmediateLowerPerformanceOfSameProvider(
      String modulename, String cloudOffer) {
    List<CloudOffer> offers = getCloudOffersForModule(modulename);
    CloudOffer currentOffer = getCloudCharacteristicsFromList(offers, cloudOffer);

    for (CloudOffer offer : offers) { // assumed that are ordered in reverse
      // order
      if (offer.getPerformance() < currentOffer.getPerformance()) {
        if (offer.getProviderName().equals(currentOffer.getProviderName())) {
          return offer; // The first one in an ordered traverse must be the
          // chosen one
        }
      }
    }

    return null;
  }
  /**
   * @param modulename
   * @param currentCloudOffer
   * @return The offer in terms of performance of the same provider that is immediately better than
   *     cloudOffer. It is assumed that ArraysOfcloudOffer are ordered by
   *     "CloudOptionReversePerformanceComparator"
   */
  public CloudOffer getOfferImmediateHigherPerformanceOfSameProvider(
      String modulename, String cloudOffer) {
    List<CloudOffer> offers = getCloudOffersForModule(modulename);
    CloudOffer currentOffer = getCloudCharacteristicsFromList(offers, cloudOffer);

    CloudOffer potentialBetter = null;

    for (CloudOffer offer : offers) { // assumed that are ordered in reverse
      // order
      if (offer.getPerformance() > currentOffer.getPerformance()) {
        if (offer.getProviderName().equals(currentOffer.getProviderName())) {
          potentialBetter = offer;
        }
      } else { // not better. So the rest are not better either.
        return potentialBetter;
      }
    }

    return potentialBetter;
  }
  public CloudOffer getOfferImmediateLowerAvailabilityOfSameProviderSimilarPerformance(
      String modulename, String cloudOffer) {
    List<CloudOffer> offers = getCloudOffersForModule(modulename);
    CloudOffer currentOffer = getCloudCharacteristicsFromList(offers, cloudOffer);

    CloudOffer potentialWorse = null;

    for (CloudOffer offer : offers) { // assumed that are ordered in reverse
      // order
      if (offer.getAvailability() < currentOffer.getAvailability()) {
        if (!(offer.getProviderName().equals(currentOffer.getProviderName()))) {
          if (potentialWorse == null) { // there was not found any yet
            potentialWorse = offer;
          } else { // An alternative offer was already found, now check by
            // its performance (the less difference)
            if (Math.abs(currentOffer.getPerformance() - offer.getPerformance())
                < (Math.abs(currentOffer.getPerformance() - potentialWorse.getPerformance()))) {
              // Closest difference
              potentialWorse = offer;
            }
          }
        }
      }
    }

    return potentialWorse;
  }
 @Override
 public int compare(CloudOffer o1, CloudOffer o2) {
   return (int)
       ((o2.getPerformance() * COMPARATOR_LIMIT) - (o1.getPerformance() * COMPARATOR_LIMIT));
 }