/// <summary>
        /// This method only selects the newest tournament results as training data.
        /// </summary>
        /// <param name="data">All historical training data.</param>
        /// <returns>Only the training data where <see cref="TrainingDataWrapper.CurrentGeneration"/> == <see cref="GenomeTournamentResult.Generation"/>.</returns>
        public AggregatedTrainingDataWrapper AggregateTargets(TrainingDataWrapper data)
        {
            // we just want to train on the newest results
            // assumption: list of known results for genome g is ordered
            // we need to be able to handle duplicates, since genomes are not neccessarily unique
            var filteredObservations = data.Genomes.Select(g => data.TournamentResults[g].Where(t => t.Generation == data.CurrentGeneration).ToList())
                .ToArray();

            var relevantCount = filteredObservations.Sum(r => r.Count);
            var relevantIndices = new int[relevantCount];
            var relevantTargets = new double[relevantCount];

            var currentRelevantIndex = 0;
            for (var i = 0; i < filteredObservations.Length; i++)
            {
                // filteredObservations[i].Count can be 0.
                for (var repeat = 0; repeat < filteredObservations[i].Count; repeat++)
                {
                    relevantIndices[currentRelevantIndex] = i;
                    relevantTargets[currentRelevantIndex] = filteredObservations[i][repeat].TournamentRank;
                    currentRelevantIndex++;
                }
            }

            // this should match
            Debug.Assert(currentRelevantIndex == relevantCount, "Each feature column should have been handled.");
            var relevantConvertedGenomes = data.ConvertedGenomes.Rows(relevantIndices);

            var result = new AggregatedTrainingDataWrapper()
                             {
                                 RelevantConvertedGenomes = relevantConvertedGenomes,
                                 RelevantTargets = relevantTargets,
                             };
            return result;
        }
        /// <summary>
        /// Computes the average <see cref="GenomeTournamentResult.TournamentRank"/> over all <see cref="GenomeTournamentResult"/> for each <see cref="Genome"/> in <see cref="TrainingDataWrapper.Genomes"/>.
        /// </summary>
        /// <param name="data">
        /// The training data.
        /// </param>
        /// <returns>
        /// The aggregated training data.
        /// </returns>
        public AggregatedTrainingDataWrapper AggregateTargets(TrainingDataWrapper data)
        {
            var targets = data.Genomes.Select(g => data.TournamentResults[g].Average(r => r.TournamentRank)).ToArray();

            var result = new AggregatedTrainingDataWrapper()
            {
                RelevantConvertedGenomes = data.ConvertedGenomes, RelevantTargets = targets
            };

            return(result);
        }