private static Double compareGAFull(
      FormationDecision fd, FormationValue bestGA, FormationValue bestFull) {

    if (bestGA == null || bestFull == null) return null;

    FormationSolution fsGA = new FormationSolution();
    fsGA.setComponentSolutions(bestGA.getComponentSolutions());
    FormationSolution fsFull = new FormationSolution();
    fsFull.setComponentSolutions(bestFull.getComponentSolutions());
    List<FormationSolution> fs = new ArrayList<FormationSolution>();
    fs.add(fsGA);
    fs.add(fsFull);
    System.out.println("Comparing following solutions: GA:" + fsGA + ", fsFull:" + fsFull);

    AnalyticHierarchyProcess ahp =
        new AnalyticHierarchyProcess(createFormationDecisionAlternatives(fd, fs));
    EvaluationResult formationEvalResult = null;
    try {
      formationEvalResult =
          ahp.evaluateFull(createFormationEvaluations(fd.getAlternatives()), 15, true);
    } catch (Exception e) {
      e.printStackTrace();
    }

    SortedSet<FormationValue> results = createSortedFormationResults(formationEvalResult);
    FormationValue previous = null;
    double bestGAValue = 1D;
    double bestFullValue = 1D;
    for (FormationValue fv : results) {
      if (previous != null)
        System.out.println(
            "Comparison prev: diff: "
                + (previous.getFormationValue() - fv.getFormationValue())
                + ", ratio: "
                + (previous.getFormationValue() / fv.getFormationValue()));
      previous = fv;
      if (fv.equals(bestGA)) bestGAValue = fv.getFormationValue();
      if (fv.equals(bestFull)) bestFullValue = fv.getFormationValue();
    }
    System.out.println(
        "Comparison best: diff: "
            + (bestGAValue - bestFullValue)
            + ", ratio: "
            + (bestGAValue / bestFullValue));

    return bestGAValue / bestFullValue;
  }
    @Override
    public Void call() throws Exception {

      // only one Alternative at a time!
      // ahpFormation.getDecision().getAlternatives().clear();
      // ahpFormation.getDecision().addAlternative(formAlternative);

      try {
        ahpFormation.evaluateSingle(this.i);
      } catch (Exception e) {
        e.printStackTrace();
      }

      // System.out.println("thread " + Thread.currentThread() +
      // " has finished with " + result + ".");
      return null;
    }
  private static SortedSet<ComponentSolution> computeComponent(
      Component c, int numAMIs, int numServices) throws Exception {

    long startTimeAMIModel = new Date().getTime();
    List<AMI> amis = new ArrayList<AMI>();
    for (int i = 0; i < numAMIs; i++) {
      AMI ami = new AMI("ami-" + i);
      ami.getAttributes()
          .add(
              new Attribute<Double>(
                  EApplianceAttribute.COSTPERHOUR, new Double((Math.random() * 0.4 + 0.1))));
      ami.getAttributes()
          .add(new Attribute<Double>(EApplianceAttribute.POPULARITY, new Double(Math.random())));
      amis.add(ami);
    }

    // System.out.println("Generated AMIs: " + amis);

    ApplianceDecision ad = new ApplianceDecision();
    ad.setName("AMI Decision");

    Goal bestAppliance = new Goal("Best Appliance");
    bestAppliance.setGoalType(GoalType.POSITIVE);
    Criterion appliancePopularity = new Criterion("Appliance Popularity");
    appliancePopularity.setType(CriterionType.QUANTITATIVE);
    bestAppliance.addChild(appliancePopularity);

    Goal cheapestAppliance = new Goal("Cheapest Appliance");
    cheapestAppliance.setGoalType(GoalType.NEGATIVE);
    Criterion applianceCosts = new Criterion("Appliance Costs");
    applianceCosts.setType(CriterionType.QUANTITATIVE);
    cheapestAppliance.addChild(applianceCosts);

    ad.addGoal(bestAppliance);
    ad.addGoal(cheapestAppliance);

    List<ApplianceAlternative> applianceAlternatives = new ArrayList<ApplianceAlternative>();
    for (AMI a : amis) {
      ApplianceAlternative aa = new ApplianceAlternative(a, "alternative-" + a.getName());
      applianceAlternatives.add(aa);
      ad.addAlternative(aa);
    }

    // System.out.println("AMI Decision: " + ad);

    List<Evaluation> amiEvaluations = new ArrayList<Evaluation>();
    Evaluation evBest = new Evaluation();
    evBest
        .getEvaluations()
        .add(createAMIMatrix(applianceAlternatives, EApplianceAttribute.POPULARITY));
    Evaluation evCheapest = new Evaluation();
    evCheapest
        .getEvaluations()
        .add(createAMIMatrix(applianceAlternatives, EApplianceAttribute.COSTPERHOUR));
    amiEvaluations.add(evBest);
    amiEvaluations.add(evCheapest);

    long endTimeAMIModel = new Date().getTime();
    System.out.println("AMI Model Creation took " + (endTimeAMIModel - startTimeAMIModel) + " ms");

    long startTimeAMIEval = new Date().getTime();
    AnalyticHierarchyProcess ahpAMI = new AnalyticHierarchyProcess(ad);
    EvaluationResult amiEvalResult = ahpAMI.evaluateFull(amiEvaluations);
    long endTimeAMIEval = new Date().getTime();
    System.out.println("AMI Evaluation took " + (endTimeAMIEval - startTimeAMIEval) + " ms");

    long startTimeServiceModel = new Date().getTime();
    List<EC2Resource> services = new ArrayList<EC2Resource>();
    for (int i = 0; i < numServices; i++) {
      EC2Resource ec2 = new EC2Resource("ec2-" + i);
      ec2.getAttributes()
          .add(
              new Attribute<Double>(
                  EComputeServiceAttribute.COSTPERHOUR, new Double((Math.random() * 0.39 + 0.01))));
      ec2.getAttributes()
          .add(
              new Attribute<Double>(
                  EComputeServiceAttribute.CPUBENCHMARK, new Double(Math.random() * 1000)));
      ec2.getAttributes()
          .add(
              new Attribute<Double>(
                  EComputeServiceAttribute.RAMBENCHMARK, new Double(Math.random() * 1000)));
      ec2.getAttributes()
          .add(
              new Attribute<Double>(
                  EComputeServiceAttribute.DISKBENCHMARK, new Double(Math.random() * 1000)));
      ec2.getAttributes()
          .add(
              new Attribute<Double>(
                  EComputeServiceAttribute.MAXLATENCY, new Double(Math.random() * 450 + 50)));
      ec2.getAttributes()
          .add(
              new Attribute<Double>(
                  EComputeServiceAttribute.AVGLATENCY, new Double(Math.random() * 490 + 10)));
      ec2.getAttributes()
          .add(
              new Attribute<Double>(
                  EComputeServiceAttribute.SERVICEPOPULARITY, new Double(Math.random())));
      ec2.getAttributes()
          .add(
              new Attribute<Double>(
                  EComputeServiceAttribute.UPTIME, new Double(Math.random() * 0.1 + 0.9)));

      ec2.setProvider(providers.get(new Random().nextInt(NUM_PROVIDERS)));

      services.add(ec2);
    }

    ComputeDecision sd = new ComputeDecision();
    sd.setName("Service Decision");

    Goal bestService = new Goal("Best Service");
    bestService.setGoalType(GoalType.POSITIVE);
    Criterion servicePopularity = new Criterion("Service Popularity");
    servicePopularity.setType(CriterionType.QUANTITATIVE);
    Criterion serviceCPU = new Criterion("Service CPU");
    serviceCPU.setType(CriterionType.BENCHMARK);
    Criterion serviceRAM = new Criterion("Service RAM");
    serviceRAM.setType(CriterionType.BENCHMARK);
    Criterion serviceDisk = new Criterion("Service Disk");
    serviceDisk.setType(CriterionType.BENCHMARK);
    Criterion serviceUptime = new Criterion("Service Uptime");
    serviceUptime.setType(CriterionType.QUANTITATIVE);

    bestService.addChild(servicePopularity);
    bestService.addChild(serviceCPU);
    bestService.addChild(serviceRAM);
    bestService.addChild(serviceDisk);
    bestService.addChild(serviceUptime);

    Goal cheapestService = new Goal("Cheapest Service");
    cheapestService.setGoalType(GoalType.NEGATIVE);
    Criterion serviceCosts = new Criterion("Service Costs");
    serviceCosts.setType(CriterionType.QUANTITATIVE);
    cheapestService.addChild(serviceCosts);

    Goal latencyService = new Goal("Low Latency Service");
    latencyService.setGoalType(GoalType.NEGATIVE);
    Criterion serviceMaxLatency = new Criterion("Service Max Latency");
    serviceMaxLatency.setType(CriterionType.BENCHMARK);
    Criterion serviceAvgLatency = new Criterion("Service Avg Latency");
    serviceAvgLatency.setType(CriterionType.BENCHMARK);
    latencyService.addChild(serviceMaxLatency);
    latencyService.addChild(serviceAvgLatency);

    sd.addGoal(bestService);
    sd.addGoal(cheapestService);
    sd.addGoal(latencyService);

    List<ComputeServiceAlternative> ec2Alternatives = new ArrayList<ComputeServiceAlternative>();
    for (EC2Resource e : services) {
      ComputeServiceAlternative ea = new ComputeServiceAlternative(e, "alternative-" + e.getName());
      ec2Alternatives.add(ea);
      sd.addAlternative(ea);
    }

    List<Evaluation> serviceEvaluations = new ArrayList<Evaluation>();

    Evaluation evServicePopularity = new Evaluation();
    evServicePopularity
        .getEvaluations()
        .add(createServiceMatrix(ec2Alternatives, EComputeServiceAttribute.SERVICEPOPULARITY));
    evServicePopularity
        .getEvaluations()
        .add(createServiceMatrix(ec2Alternatives, EComputeServiceAttribute.CPUBENCHMARK));
    evServicePopularity
        .getEvaluations()
        .add(createServiceMatrix(ec2Alternatives, EComputeServiceAttribute.RAMBENCHMARK));
    evServicePopularity
        .getEvaluations()
        .add(createServiceMatrix(ec2Alternatives, EComputeServiceAttribute.DISKBENCHMARK));
    Evaluation evServiceCheapest = new Evaluation();
    evServiceCheapest
        .getEvaluations()
        .add(createServiceMatrix(ec2Alternatives, EComputeServiceAttribute.COSTPERHOUR));
    Evaluation evServiceLatency = new Evaluation();
    evServiceLatency
        .getEvaluations()
        .add(createServiceMatrix(ec2Alternatives, EComputeServiceAttribute.MAXLATENCY));
    evServiceLatency
        .getEvaluations()
        .add(createServiceMatrix(ec2Alternatives, EComputeServiceAttribute.AVGLATENCY));
    serviceEvaluations.add(evServicePopularity);
    serviceEvaluations.add(evServiceCheapest);
    serviceEvaluations.add(evServiceLatency);

    long endTimeServiceModel = new Date().getTime();
    System.out.println(
        "Service Model Creation took " + (endTimeServiceModel - startTimeServiceModel) + " ms");

    long startTimeServiceEval = new Date().getTime();
    AnalyticHierarchyProcess ahpService = new AnalyticHierarchyProcess(sd);
    EvaluationResult serviceEvalResult = ahpService.evaluateFull(serviceEvaluations);
    long endTimeServiceEval = new Date().getTime();
    System.out.println(
        "Service Evaluation took " + (endTimeServiceEval - startTimeServiceEval) + " ms");

    long startTimeCombined = new Date().getTime();
    List<CombinationValue> combinations = new ArrayList<CombinationValue>();
    for (ApplianceAlternative aa : applianceAlternatives)
      for (ComputeServiceAlternative csa : ec2Alternatives)
        combinations.add(
            new CombinationValue(
                aa.getAppl(),
                csa.getComputeService(),
                amiEvalResult.getResultMultiplicativeIndexMap().get(aa),
                serviceEvalResult.getResultMultiplicativeIndexMap().get(csa)));

    long endTimeCombined = new Date().getTime();
    System.out.println(
        "Combination Model took "
            + (endTimeCombined - startTimeCombined)
            + " ms (for "
            + combinations.size()
            + " combinations)");
    long startTimeCombinedEval = new Date().getTime();

    SortedSet<ComponentSolution> combinationResults = new TreeSet<ComponentSolution>();
    for (CombinationValue cv : combinations) {
      Double value = cv.getApplianceValue() + cv.getServiceValue();
      combinationResults.add(new ComponentSolution(c, new CombinationTotalValue(cv, value)));
    }
    long endTimeCombinedEval = new Date().getTime();
    System.out.println(
        "Combination Eval took " + (endTimeCombinedEval - startTimeCombinedEval) + " ms");

    System.out.println("Worst Combination: " + combinationResults.first());
    System.out.println("Best Combination: " + combinationResults.last());

    TIME_INTERMEDIATE_TOTAL =
        ((endTimeAMIModel - startTimeAMIModel)
            + (endTimeAMIEval - startTimeAMIEval)
            + (endTimeServiceModel - startTimeServiceModel)
            + (endTimeServiceEval - startTimeServiceEval)
            + (endTimeCombined - startTimeCombined)
            + (endTimeCombinedEval - startTimeCombinedEval));
    TIME_COMPONENTS_TOTAL += TIME_INTERMEDIATE_TOTAL;

    FileWriter fw = new FileWriter("out_components.txt", true);
    BufferedWriter out = new BufferedWriter(fw);
    out.write(
        ""
            + c.getName()
            + ","
            + NUM_COMPONENTS
            + ","
            + numAMIs
            + ","
            + numServices
            + ","
            + (endTimeAMIModel - startTimeAMIModel)
            + ","
            + (endTimeAMIEval - startTimeAMIEval)
            + ","
            + (endTimeServiceModel - startTimeServiceModel)
            + ","
            + (endTimeServiceEval - startTimeServiceEval)
            + ","
            + (endTimeCombined - startTimeCombined)
            + ","
            + (endTimeCombinedEval - startTimeCombinedEval)
            + ","
            + TIME_INTERMEDIATE_TOTAL
            + "\n");
    out.close();

    return combinationResults;
  }
  private static SortedSet<FormationValue> computeFormations(
      Map<Component, SortedSet<ComponentSolution>> componentSolutions,
      int numComps,
      int numAMIs,
      int numServices)
      throws Exception {

    // System.out.print("formation (size="
    // + fs.getComponentSolutionMap().size() + "): " + fs);
    long startTimeFormation = new Date().getTime();
    FormationDecision fd = new FormationDecision();
    fd.setName("Combined Decision");

    Goal valuableFormation = new Goal("Highest Sum Value of Components");
    valuableFormation.setGoalType(GoalType.POSITIVE);
    Criterion formationValue = new Criterion("Value");
    formationValue.setType(CriterionType.QUANTITATIVE);
    valuableFormation.addChild(formationValue);
    fd.addGoal(valuableFormation);

    Goal cheapestFormation = new Goal("Cheapest Inter-Connection Costs Formation");
    cheapestFormation.setGoalType(GoalType.NEGATIVE);
    Criterion formationCosts = new Criterion("Inter-Connection Traffic Costs");
    formationCosts.setType(CriterionType.QUANTITATIVE);
    cheapestFormation.addChild(formationCosts);
    fd.addGoal(cheapestFormation);

    List<FormationSolution> fs =
        new ArrayList<FormationSolution>(
            cartesianProduct(new ArrayList<Set<ComponentSolution>>(componentSolutions.values())));

    long endTimeFormation = new Date().getTime();

    long startTimeFormationEval = new Date().getTime();
    SortedSet<FormationValue> result = new TreeSet<FormationValue>();
    long endTimeFormationEval = new Date().getTime();

    FormationValue bestGA = null;
    FormationValue bestFull = null;

    if ((COMPARISON && fs.size() > 1)
        || 10 * GA_POPULATION_SIZE < fs.size()
        || Math.pow(16, 3) < fs.size()) {
      long endTimeFormationGA = new Date().getTime();
      System.out.println(
          "Formation Decision Model took " + (endTimeFormationGA - startTimeFormation) + " ms");

      startTimeFormationEval = new Date().getTime();
      result = computeGABestFormation(componentSolutions, fd);
      endTimeFormationEval = new Date().getTime();
      System.out.println(
          "Formation Solutions Eval took "
              + (endTimeFormationEval - startTimeFormationEval)
              + " ms");
      System.out.println("Worst Formation from GA Elite: " + result.first());
      System.out.println("Best Formation from GA Elite: " + result.last());
      bestGA = result.last();
    }
    if (Math.pow(16, 3) >= fs.size()) {
      long startTimeFormationFull = new Date().getTime();
      fd = createFormationDecisionAlternatives(fd, fs);

      List<FormationAlternative> formAlts = fd.getAlternatives();
      List<Evaluation> formationEvaluations = createFormationEvaluations(formAlts);

      long endTimeFormationFull = new Date().getTime();
      System.out.println(
          "Formation Solutions Model took "
              + (endTimeFormation
                  - startTimeFormation
                  + endTimeFormationFull
                  - startTimeFormationFull)
              + " ms (for "
              + fs.size()
              + " formation solutions)");
      endTimeFormation = endTimeFormation + (endTimeFormationFull - startTimeFormationFull);

      startTimeFormationEval = new Date().getTime();

      System.out.println("preparing AHP...");
      AnalyticHierarchyProcess ahpFormation = new AnalyticHierarchyProcess(fd);
      ahpFormation.calculateWeights();
      ahpFormation.calculateAlternativeValues(formationEvaluations);

      System.out.println("creating threads...");
      ExecutorService exec = Executors.newFixedThreadPool(16);
      List<Future<Void>> futures = new ArrayList<Future<Void>>();
      for (int i = 0; i < fd.getAlternatives().size(); i++) {
        Callable<Void> c = new EvaluationThread(ahpFormation, i);
        futures.add(exec.submit(c));
      }
      exec.shutdown();
      System.out.println("returning Voids...");
      for (Future<Void> f : futures) f.get();

      System.out.println("calculating indices...");
      result = createSortedFormationResults(ahpFormation.calculateIndices());

      endTimeFormationEval = new Date().getTime();
      System.out.println(
          "Formation Solutions Eval took "
              + (endTimeFormationEval - startTimeFormationEval)
              + " ms (for "
              + fs.size()
              + " formation solutions)");

      System.out.println("Worst Formation Solution: " + result.first());
      System.out.println("Best Formation Solution: " + result.last());
      bestFull = result.last();
    }

    TIME_TOTAL =
        TIME_COMPONENTS_TOTAL
            + (endTimeFormation - startTimeFormation)
            + (endTimeFormationEval - startTimeFormationEval);

    FileWriter fw = new FileWriter("out_formation.txt", true);
    BufferedWriter out = new BufferedWriter(fw);
    out.write(
        ""
            + numComps
            + ","
            + numAMIs
            + ","
            + numServices
            + ","
            + (endTimeFormation - startTimeFormation)
            + ","
            + (endTimeFormationEval - startTimeFormationEval)
            + ","
            + TIME_TOTAL
            + ","
            + compareGAFull(fd, bestGA, bestFull)
            + "\n");
    out.close();

    return result;
  }