// Path: src/main/java/org/arbeitspferde/groningen/common/EvaluatedSubject.java
// public class EvaluatedSubject implements Comparable<EvaluatedSubject> {
//   private static final long EXPERIMENT_ID_UNINITIALIZED = 0;
//   private final SubjectStateBridge bridge;
//   private final Clock clock;
//   private Instant timeStamp;
//   private final Fitness fitness = new Fitness();
// 
//   private long experimentId = EXPERIMENT_ID_UNINITIALIZED;
//   private boolean isDefault = false;
//   private String clusterName;
//   private String subjectGroupName;
//   private String userName;
//   private int subjectGroupIndex;
// 
// 
//   /** Constructors */
//   public EvaluatedSubject(final Clock clock, final SubjectStateBridge subject, final double score,
//       final long experimentId) {
//     this(clock, subject, score);
//     setExperimentId(experimentId);
//   }
// 
//   public EvaluatedSubject(final Clock clock, final SubjectStateBridge bridge, final double score) {
//     this.clock = clock;
//     this.bridge = bridge;
//     setFitness(score);
//     setTimeStamp(this.clock.now());
// 
//     if (bridge.getAssociatedSubject() != null) {
//       setClusterName(bridge.getAssociatedSubject().getGroup().getClusterName());
//       setSubjectGroupName(bridge.getAssociatedSubject().getGroup().getName());
//       setUserName(bridge.getAssociatedSubject().getGroup().getUserName());
//       setSubjectGroupIndex(bridge.getAssociatedSubject().getIndex());
//       setDefault(bridge.getAssociatedSubject().isDefault());
//     }
//   }
// 
//   public void setClusterName(String clusterName) {
//     this.clusterName = clusterName;
//   }
// 
//   public String getClusterName() {
//     return clusterName;
//   }
// 
//   public void setSubjectGroupName(String subjectGroupName) {
//     this.subjectGroupName = subjectGroupName;
//   }
// 
//   public String getSubjectGroupName() {
//     return subjectGroupName;
//   }
// 
//   public void setUserName(String userName) {
//     this.userName = userName;
//   }
// 
//   public String getUserName() {
//     return userName;
//   }
// 
//   public void setSubjectGroupIndex(int subjectGroupIndex) {
//     this.subjectGroupIndex = subjectGroupIndex;
//   }
// 
//   public int getSubjectGroupIndex() {
//     return subjectGroupIndex;
//   }
// 
//   /** Set and get methods */
//   public SubjectStateBridge getBridge() {
//     return bridge;
//   }
// 
//   public void setFitness(double score) {
//     this.fitness.setFitness(score);
//   }
// 
//   public double getFitness() {
//     return fitness.getFitness();
//   }
// 
//   public void setExperimentId(final long experimentId) throws IllegalArgumentException {
//     Preconditions.checkArgument(experimentId > EXPERIMENT_ID_UNINITIALIZED,
//         "Invalid experimentId: %s; should have been > %s.", experimentId,
//         EXPERIMENT_ID_UNINITIALIZED);
// 
//     this.experimentId = experimentId;
//   }
// 
//   public long getExperimentId() {
//     Preconditions.checkState(experimentId != EXPERIMENT_ID_UNINITIALIZED,
//         "experimentID is unset.");
//     return experimentId;
//   }
// 
//   public void setTimeStamp(Instant timeStamp) {
//     this.timeStamp = timeStamp;
//   }
// 
//   public Instant getTimeStamp() {
//     return timeStamp;
//   }
// 
//   /** Uses natural order to compare */
//   @Override
//   public int compareTo(EvaluatedSubject evaluatedSubject) {
//     return Double.compare(fitness.getFitness(), evaluatedSubject.getFitness());
//   }
// 
//   public boolean isDefault() {
//     return isDefault;
//   }
// 
//   public void setDefault(boolean trueOrFalse) {
//     isDefault = trueOrFalse;
//   }
// 
//   /**
//    * A class for Fitness, to be extended later.
//    */
//   private class Fitness {
//     private double fitnessScore;
// 
//     public double getFitness() {
//       return fitnessScore;
//     }
// 
//     public void setFitness(double fitness) {
//       this.fitnessScore = fitness;
//     }
//   }
// }


import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import org.arbeitspferde.groningen.common.EvaluatedSubject;
import org.arbeitspferde.groningen.utility.Clock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
package org.arbeitspferde.groningen.scorer;




/**
 * HistoricalBestPerformerScorer that uses the generation number as a major weight in scoring.
 *
 * The individual's score for this iteration will be weighted (multiplied) by the iteration
 * number.
 *
 * The best performer scoring will combine individuals both within an experiment if they are
 * duplicated AND will combine scores across iterations. Namely, within the same generation
 * individuals with the same command line (args and arg values) will be averaged.
 *
 * Across iterations, the individual's previous score will be combined with to its score in this
 * iteration - the individual will be represented by a single @{link EvaluatedSubject} with the
 * current iteration number and a score equal to the combination of the previous plus current
 * scores.
 */
public class GenerationNumberWeightedBestPerformerScorer implements HistoricalBestPerformerScorer {

  /**
   * To create new instances of {@link EvaluatedSubject EvaluatedSubjects} when merging existing
   * instance in the store.
   */
  private final Clock clock;

  /** Stores the all-time unique and merged evaluated subjects with greatest to least ordering. */
  @VisibleForTesting
  final
// Path: src/main/java/org/arbeitspferde/groningen/common/EvaluatedSubject.java
// public class EvaluatedSubject implements Comparable<EvaluatedSubject> {
//   private static final long EXPERIMENT_ID_UNINITIALIZED = 0;
//   private final SubjectStateBridge bridge;
//   private final Clock clock;
//   private Instant timeStamp;
//   private final Fitness fitness = new Fitness();
// 
//   private long experimentId = EXPERIMENT_ID_UNINITIALIZED;
//   private boolean isDefault = false;
//   private String clusterName;
//   private String subjectGroupName;
//   private String userName;
//   private int subjectGroupIndex;
// 
// 
//   /** Constructors */
//   public EvaluatedSubject(final Clock clock, final SubjectStateBridge subject, final double score,
//       final long experimentId) {
//     this(clock, subject, score);
//     setExperimentId(experimentId);
//   }
// 
//   public EvaluatedSubject(final Clock clock, final SubjectStateBridge bridge, final double score) {
//     this.clock = clock;
//     this.bridge = bridge;
//     setFitness(score);
//     setTimeStamp(this.clock.now());
// 
//     if (bridge.getAssociatedSubject() != null) {
//       setClusterName(bridge.getAssociatedSubject().getGroup().getClusterName());
//       setSubjectGroupName(bridge.getAssociatedSubject().getGroup().getName());
//       setUserName(bridge.getAssociatedSubject().getGroup().getUserName());
//       setSubjectGroupIndex(bridge.getAssociatedSubject().getIndex());
//       setDefault(bridge.getAssociatedSubject().isDefault());
//     }
//   }
// 
//   public void setClusterName(String clusterName) {
//     this.clusterName = clusterName;
//   }
// 
//   public String getClusterName() {
//     return clusterName;
//   }
// 
//   public void setSubjectGroupName(String subjectGroupName) {
//     this.subjectGroupName = subjectGroupName;
//   }
// 
//   public String getSubjectGroupName() {
//     return subjectGroupName;
//   }
// 
//   public void setUserName(String userName) {
//     this.userName = userName;
//   }
// 
//   public String getUserName() {
//     return userName;
//   }
// 
//   public void setSubjectGroupIndex(int subjectGroupIndex) {
//     this.subjectGroupIndex = subjectGroupIndex;
//   }
// 
//   public int getSubjectGroupIndex() {
//     return subjectGroupIndex;
//   }
// 
//   /** Set and get methods */
//   public SubjectStateBridge getBridge() {
//     return bridge;
//   }
// 
//   public void setFitness(double score) {
//     this.fitness.setFitness(score);
//   }
// 
//   public double getFitness() {
//     return fitness.getFitness();
//   }
// 
//   public void setExperimentId(final long experimentId) throws IllegalArgumentException {
//     Preconditions.checkArgument(experimentId > EXPERIMENT_ID_UNINITIALIZED,
//         "Invalid experimentId: %s; should have been > %s.", experimentId,
//         EXPERIMENT_ID_UNINITIALIZED);
// 
//     this.experimentId = experimentId;
//   }
// 
//   public long getExperimentId() {
//     Preconditions.checkState(experimentId != EXPERIMENT_ID_UNINITIALIZED,
//         "experimentID is unset.");
//     return experimentId;
//   }
// 
//   public void setTimeStamp(Instant timeStamp) {
//     this.timeStamp = timeStamp;
//   }
// 
//   public Instant getTimeStamp() {
//     return timeStamp;
//   }
// 
//   /** Uses natural order to compare */
//   @Override
//   public int compareTo(EvaluatedSubject evaluatedSubject) {
//     return Double.compare(fitness.getFitness(), evaluatedSubject.getFitness());
//   }
// 
//   public boolean isDefault() {
//     return isDefault;
//   }
// 
//   public void setDefault(boolean trueOrFalse) {
//     isDefault = trueOrFalse;
//   }
// 
//   /**
//    * A class for Fitness, to be extended later.
//    */
//   private class Fitness {
//     private double fitnessScore;
// 
//     public double getFitness() {
//       return fitnessScore;
//     }
// 
//     public void setFitness(double fitness) {
//       this.fitnessScore = fitness;
//     }
//   }
// }


// Path: src/main/java/org/arbeitspferde/groningen/scorer/GenerationNumberWeightedBestPerformerScorer.java
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import org.arbeitspferde.groningen.common.EvaluatedSubject;
import org.arbeitspferde.groningen.utility.Clock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

package org.arbeitspferde.groningen.scorer;




/**
 * HistoricalBestPerformerScorer that uses the generation number as a major weight in scoring.
 *
 * The individual's score for this iteration will be weighted (multiplied) by the iteration
 * number.
 *
 * The best performer scoring will combine individuals both within an experiment if they are
 * duplicated AND will combine scores across iterations. Namely, within the same generation
 * individuals with the same command line (args and arg values) will be averaged.
 *
 * Across iterations, the individual's previous score will be combined with to its score in this
 * iteration - the individual will be represented by a single @{link EvaluatedSubject} with the
 * current iteration number and a score equal to the combination of the previous plus current
 * scores.
 */
public class GenerationNumberWeightedBestPerformerScorer implements HistoricalBestPerformerScorer {

  /**
   * To create new instances of {@link EvaluatedSubject EvaluatedSubjects} when merging existing
   * instance in the store.
   */
  private final Clock clock;

  /** Stores the all-time unique and merged evaluated subjects with greatest to least ordering. */
  @VisibleForTesting
  final
  List<EvaluatedSubject> alltimeEvaluatedSubjects = new ArrayList<>();