/*
 * Java Genetic Algorithm Library (@__identifier__@).
 * Copyright (c) @__year__@ Franz Wilhelmstötter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author:
 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
 */
package io.jenetics.engine;

import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;

import io.jenetics.Chromosome;
import io.jenetics.Genotype;
import io.jenetics.NumericGene;
import io.jenetics.Phenotype;
import io.jenetics.stat.DoubleMomentStatistics;
import io.jenetics.stat.DoubleMoments;
import io.jenetics.util.ISeq;
import io.jenetics.util.Seq;

/**
 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
 * @version !__version__!
 * @since !__version__!
 */
final class GeneConvergenceLimit<
	N extends Number & Comparable<? super N>,
	G extends NumericGene<N, G>
>
	implements Predicate<EvolutionResult<G, ?>>
{

	private final Predicate<DoubleMoments> _convergence;
	private final double _convergenceRate;

	GeneConvergenceLimit(
		final Predicate<DoubleMoments> convergence,
		final double convergenceRate
	) {
		if (convergenceRate < 0.0 || convergenceRate > 1.0) {
			throw new IllegalArgumentException(format(
				"The given convergence rate is not in the range [0, 1]: %f",
				convergenceRate
			));
		}
		_convergence = requireNonNull(convergence);
		_convergenceRate = convergenceRate;
	}

	@Override
	public boolean test(final EvolutionResult<G, ?> result) {
		final ISeq<DoubleMoments> stat = statistics(result.getPopulation());

		return stat.stream()
			.filter(_convergence)
			.count() < _convergenceRate*stat.size();
	}

	private final ISeq<DoubleMoments>
	statistics(final Seq<? extends Phenotype<G, ?>> population) {
		final Map<Index, DoubleMomentStatistics> statistics = new HashMap<>();

		for (Phenotype<G, ?> pt : population) {
			final Genotype<G> gt = pt.getGenotype();

			for (int i = 0; i < gt.length(); ++i) {
				final Chromosome<G> ch = gt.getChromosome(i);

				for (int j = 0; j < ch.length(); ++j) {
					statistics
						.computeIfAbsent(new Index(i, j),
							k -> new DoubleMomentStatistics())
						.accept(ch.getGene(j).doubleValue());
				}
			}
		}

		return statistics.values().stream()
			.map(DoubleMomentStatistics::toDoubleMoments)
			.collect(ISeq.toISeq());
	}

	/**
	 * Index class representing the  index pair of chromosome index and gene
	 * index.
	 */
	private static final class Index {
		final int ci;
		final int gi;

		Index(final int ci, final int gi) {
			this.ci = ci;
			this.gi = gi;
		}

		@Override
		public int hashCode() {
			return ci^gi;
		}

		@Override
		public boolean equals(final Object obj) {
			return obj instanceof Index &&
				((Index)obj).ci == ci &&
				((Index)obj).gi == gi;
		}

		@Override
		public String toString() {
			return format("[%d, %d]", ci, gi);
		}

	}

}



@@ -2342,21 +2342,20 @@
 nal Map%3C
-Index
+Long
 , Double
@@ -2671,22 +2671,42 @@
 ent(
-new Index(i, j
+((long)i %3C%3C 32) %7C (j & 0xffffffffL
 ),%0A%09
@@ -2893,16 +2893,16 @@
 oments)%0A
+
 %09%09%09.coll
@@ -2929,548 +2929,8 @@
 %09%7D%0A%0A
-%09/**%0A%09 * Index class representing the  index pair of chromosome index and gene%0A%09 * index.%0A%09 */%0A%09private static final class Index %7B%0A%09%09final int ci;%0A%09%09final int gi;%0A%0A%09%09Index(final int ci, final int gi) %7B%0A%09%09%09this.ci = ci;%0A%09%09%09this.gi = gi;%0A%09%09%7D%0A%0A%09%09@Override%0A%09%09public int hashCode() %7B%0A%09%09%09return ci%5Egi;%0A%09%09%7D%0A%0A%09%09@Override%0A%09%09public boolean equals(final Object obj) %7B%0A%09%09%09return obj instanceof Index &&%0A%09%09%09%09((Index)obj).ci == ci &&%0A%09%09%09%09((Index)obj).gi == gi;%0A%09%09%7D%0A%0A%09%09@Override%0A%09%09public String toString() %7B%0A%09%09%09return format(%22%5B%25d, %25d%5D%22, ci, gi);%0A%09%09%7D%0A%0A%09%7D%0A%0A
 %7D%0A%0A%0A

Implement 'GeneConvergenceLimit' class.