/// <summary>
        ///
        /// </summary>
        /// <param name="population"></param>
        /// <param name="crossover"></param>
        /// <param name="crossoverProbability"></param>
        /// <param name="allParents"></param>
        /// <returns></returns>
        protected virtual IList <IChromosome> PerformCrossPopulation(ISpeciedPopulation population, ICrossover crossover, float crossoverProbability, IList <IChromosome> allParents)
        {
            // (PUZZLE) How to create lookup when fitness is how equality is determined?
            var parentsLookup = allParents.ToLookup(
                (parent) => population.Species.FirstOrDefault(
                    (specie) => specie.CurrentGeneration.Chromosomes.Any(
                        (chrom) => Object.ReferenceEquals(chrom, parent))));

            var toReturn       = new List <IChromosome>();
            var newSpecieSizes = CalcNewSpecieSizes(population);

            foreach (var kv in newSpecieSizes)
            {
                Specie specie      = kv.Key;
                int    numChildren = kv.Value;
                int    nCross      = (int)Math.Round(numChildren * crossoverProbability);
                var    curParents  = parentsLookup[specie].ToList();
                // Copy the best chromosome directly
                if (numChildren > 0)
                {
                    toReturn.Add(specie.BestChromosome);
                }
                // Normal crossover for the specie
                toReturn.AddRange(PerformCrossSpecie(population, specie, crossover, nCross, allParents, parentsLookup));
                // For the rest, copy over random parents
                // Include -1 to leave space for BestChromosome to be directly copied
                toReturn.AddRange(GetRandomParents(curParents, numChildren - nCross - 1));
            }

            return(toReturn);
        }
        /// <summary>
        /// Enumerates through the offspring created from the specie.
        /// </summary>
        /// <param name="population"></param>
        /// <param name="specie"></param>
        /// <param name="crossover"></param>
        /// <param name="childrenCount"></param>
        /// <param name="allParents"></param>
        /// <param name="parentsLookup"></param>
        /// <returns></returns>
        protected virtual IEnumerable <IChromosome> PerformCrossSpecie(
            ISpeciedPopulation population,
            Specie specie,
            ICrossover crossover,
            int childrenCount,
            IList <IChromosome> allParents,
            ILookup <Specie, IChromosome> parentsLookup)
        {
            if (parentsLookup[specie].Count() < crossover.ParentsNumber)
            {
                throw new InvalidOperationException("Not enough specie parents available for this crossover.");
            }

            if (childrenCount <= 0)
            {
                yield break;
            }

            var curParents = new List <IChromosome>(crossover.ParentsNumber);
            int pInd       = 0;

            for (int i = 0; i < childrenCount; i += crossover.ChildrenNumber)
            {
                IList <IChromosome> kids;

                // Interspecie crossover
                if (RandomizationProvider.Current.GetDouble() <= InterspecieMatingRate)
                {
                    kids = PerformInterspecieCross(crossover, allParents);
                }
                // Crossover within specie
                else
                {
                    List <IChromosome> specieParents = parentsLookup[specie].ToList();
                    // Choose parents, place into curParents list
                    curParents.Clear();
                    while (curParents.Count < crossover.ParentsNumber)
                    {
                        curParents.Add(specieParents[pInd++]);
                        // Wrap around and re-shuffle
                        if (pInd >= specieParents.Count)
                        {
                            pInd          = 0;
                            specieParents = specieParents.Shuffle(RandomizationProvider.Current).ToList();
                        }
                    }
                    kids = crossover.Cross(curParents);
                }

                // Return children
                for (int k = 0; k < kids.Count; k++)
                {
                    if (i + k < childrenCount)
                    {
                        yield return(kids[k]);
                    }
                }
            }
        }
        /// <summary>
        /// Returns a dictionary mapping each specie to its number of offspring.
        /// </summary>
        /// <param name="population"></param>
        /// <returns></returns>
        protected virtual Dictionary <Specie, int> CalcNewSpecieSizes(ISpeciedPopulation population)
        {
            var    species     = population.Species;
            var    toReturn    = new Dictionary <Specie, int>(species.Count);
            var    avgFits     = species.Select(s => GetAverageFitness(s)).ToList();
            double totalAvgFit = avgFits.Sum();

            int totalChildren = 0; // Keep track for later

            for (int i = 0; i < species.Count; i++)
            {
                int newSize = GetNewSpecieSize(species[i], avgFits[i], totalAvgFit, population);
                toReturn.Add(species[i], newSize);
                totalChildren += newSize;
            }

            if (totalChildren == 0)
            {
                return(new Dictionary <Specie, int>());
            }

            // Expand to minsize
            for (; totalChildren < population.MinSize; totalChildren++)
            {
                var minkv = GetMinNonZeroValue(toReturn);
                if (minkv.Value == int.MaxValue)
                {
                    throw new InvalidOperationException("All species were set to size zero.");
                }
                toReturn[minkv.Key]++;
            }

            // Trim to maxsize
            for (; totalChildren > population.MaxSize; totalChildren--)
            {
                var maxkv = GetMaxValue(toReturn);
                if (maxkv.Value <= population.MinSpecieSize)
                {
                    throw new InvalidOperationException("MinSpecieSize is larger than expected.");
                }
                toReturn[maxkv.Key]--;
            }

            return(toReturn);
        }
        /// <summary>
        /// Calculates the new size of a specie given its average fitness, the
        /// sum of all average fitnesses, and the population.
        /// </summary>
        private int GetNewSpecieSize(Specie specie, double avgFit, double totalAvgFit, ISpeciedPopulation population)
        {
            if (!SpecieShouldReproduce(specie))
            {
                return(0);
            }
            double ratio   = avgFit / totalAvgFit;
            int    newSize = (int)Math.Ceiling(ratio * population.MinSize);

            return((newSize < population.MinSpecieSize) ? 0 : newSize);
        }