/**
  * Some useful debug information
  *
  * @param coveredBranches
  * @param coveredMethods
  * @param fitness
  */
 private void printStatusMessages(
     AbstractTestSuiteChromosome<? extends ExecutableChromosome> suite,
     int coveredBranches,
     double fitness) {
   if (coveredBranches > maxCoveredBranches) {
     maxCoveredBranches = coveredBranches;
     logger.info(
         "(Branches) Best individual covers "
             + coveredBranches
             + "/"
             + (totalBranches * 2)
             + " branches");
     logger.info(
         "Fitness: "
             + fitness
             + ", size: "
             + suite.size()
             + ", length: "
             + suite.totalLengthOfTestCases());
   }
   if (fitness < bestFitness) {
     logger.info(
         "(Fitness) Best individual covers "
             + coveredBranches
             + "/"
             + (totalBranches * 2)
             + " branches");
     bestFitness = fitness;
     logger.info(
         "Fitness: "
             + fitness
             + ", size: "
             + suite.size()
             + ", length: "
             + suite.totalLengthOfTestCases());
   }
 }
  /**
   * {@inheritDoc}
   *
   * <p>Execute all tests and count covered branches
   */
  @Override
  public double getFitness(AbstractTestSuiteChromosome<? extends ExecutableChromosome> suite) {
    logger.trace("Calculating branch fitness");
    double fitness = 0.0;

    List<ExecutionResult> results = runTestSuite(suite);
    Map<Integer, Double> trueDistance = new HashMap<Integer, Double>();
    Map<Integer, Double> falseDistance = new HashMap<Integer, Double>();
    Map<Integer, Integer> predicateCount = new HashMap<Integer, Integer>();

    // Collect stats in the traces
    boolean hasTimeoutOrTestException =
        analyzeTraces(suite, results, predicateCount, trueDistance, falseDistance);

    // Collect branch distances of covered branches
    int numCoveredBranches = 0;

    for (Integer key : predicateCount.keySet()) {

      double df = 0.0;
      double dt = 0.0;
      int numExecuted = predicateCount.get(key);

      if (removedBranchesT.contains(key)) numExecuted++;
      if (removedBranchesF.contains(key)) numExecuted++;

      if (trueDistance.containsKey(key)) {
        dt = trueDistance.get(key);
      }
      if (falseDistance.containsKey(key)) {
        df = falseDistance.get(key);
      }
      // If the branch predicate was only executed once, then add 1
      if (numExecuted == 1) {
        fitness += 1.0;
      } else {
        fitness += normalize(df) + normalize(dt);
      }

      if (falseDistance.containsKey(key) && (Double.compare(df, 0.0) == 0)) numCoveredBranches++;

      if (trueDistance.containsKey(key) && (Double.compare(dt, 0.0) == 0)) numCoveredBranches++;
    }

    // +1 for every branch that was not executed
    fitness += 2 * (totalBranches - predicateCount.size());

    printStatusMessages(suite, numCoveredBranches, fitness);

    // Calculate coverage
    int coverage = numCoveredBranches;

    coverage += removedBranchesF.size();
    coverage += removedBranchesT.size();

    if (totalGoals > 0) suite.setCoverage(this, (double) coverage / (double) totalGoals);
    else suite.setCoverage(this, 1);

    suite.setNumOfCoveredGoals(this, coverage);
    suite.setNumOfNotCoveredGoals(this, totalGoals - coverage);

    if (hasTimeoutOrTestException) {
      logger.info("Test suite has timed out, setting fitness to max value " + (totalBranches * 2));
      fitness = totalBranches * 2;
      // suite.setCoverage(0.0);
    }

    updateIndividual(this, suite, fitness);

    assert (coverage <= totalGoals) : "Covered " + coverage + " vs total goals " + totalGoals;
    assert (fitness >= 0.0);
    assert (fitness != 0.0 || coverage == totalGoals)
        : "Fitness: " + fitness + ", " + "coverage: " + coverage + "/" + totalGoals;
    assert (suite.getCoverage(this) <= 1.0) && (suite.getCoverage(this) >= 0.0)
        : "Wrong coverage value " + suite.getCoverage(this);
    return fitness;
  }
  /**
   * Iterate over all execution results and summarize statistics
   *
   * @param results
   * @param predicateCount
   * @param callCount
   * @param trueDistance
   * @param falseDistance
   * @return
   */
  private boolean analyzeTraces(
      AbstractTestSuiteChromosome<? extends ExecutableChromosome> suite,
      List<ExecutionResult> results,
      Map<Integer, Integer> predicateCount,
      Map<Integer, Double> trueDistance,
      Map<Integer, Double> falseDistance) {

    boolean hasTimeoutOrTestException = false;
    for (ExecutionResult result : results) {
      if (result.hasTimeout() || result.hasTestException()) {
        hasTimeoutOrTestException = true;
        continue;
      }

      for (Entry<Integer, Integer> entry :
          result.getTrace().getPredicateExecutionCount().entrySet()) {
        if (!branchesId.contains(entry.getKey())
            || (removedBranchesT.contains(entry.getKey())
                && removedBranchesF.contains(entry.getKey()))) continue;
        if (!predicateCount.containsKey(entry.getKey()))
          predicateCount.put(entry.getKey(), entry.getValue());
        else {
          predicateCount.put(entry.getKey(), predicateCount.get(entry.getKey()) + entry.getValue());
        }
      }
      for (Entry<Integer, Double> entry : result.getTrace().getTrueDistances().entrySet()) {
        if (!branchesId.contains(entry.getKey()) || removedBranchesT.contains(entry.getKey()))
          continue;
        if (!trueDistance.containsKey(entry.getKey()))
          trueDistance.put(entry.getKey(), entry.getValue());
        else {
          trueDistance.put(
              entry.getKey(), Math.min(trueDistance.get(entry.getKey()), entry.getValue()));
        }
        if ((Double.compare(entry.getValue(), 0.0) == 0)) {
          result.test.addCoveredGoal(branchCoverageTrueMap.get(entry.getKey()));
          if (Properties.TEST_ARCHIVE) {
            TestsArchive.instance.putTest(this, branchCoverageTrueMap.get(entry.getKey()), result);
            toRemoveBranchesT.add(entry.getKey());
            suite.isToBeUpdated(true);
          }
        }
      }
      for (Entry<Integer, Double> entry : result.getTrace().getFalseDistances().entrySet()) {
        if (!branchesId.contains(entry.getKey()) || removedBranchesF.contains(entry.getKey()))
          continue;
        if (!falseDistance.containsKey(entry.getKey()))
          falseDistance.put(entry.getKey(), entry.getValue());
        else {
          falseDistance.put(
              entry.getKey(), Math.min(falseDistance.get(entry.getKey()), entry.getValue()));
        }
        if ((Double.compare(entry.getValue(), 0.0) == 0)) {
          result.test.addCoveredGoal(branchCoverageFalseMap.get(entry.getKey()));
          if (Properties.TEST_ARCHIVE) {
            TestsArchive.instance.putTest(this, branchCoverageFalseMap.get(entry.getKey()), result);
            toRemoveBranchesF.add(entry.getKey());
            suite.isToBeUpdated(true);
          }
        }
      }
    }
    return hasTimeoutOrTestException;
  }