/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: CycleCrossoverFavoringStrongParents.java
 * Written by Team 3: Christian Wittner
 * 
 * This code has been developed at the Karlsruhe Institute of Technology (KIT), Germany, 
 * as part of the course "Multicore Programming in Practice: Tools, Models, and Languages".
 * Contact instructor: Dr. Victor Pankratius (pankratius@ipd.uka.de)
 *
 * Copyright (c) 2010, Static Free Software. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.placement.genetic1.g1;

import com.sun.electric.tool.placement.genetic1.Chromosome;
import com.sun.electric.tool.placement.genetic1.Crossover;
import com.sun.electric.tool.placement.genetic1.Population;

import java.util.ArrayList;
import java.util.Random;

/**
 *         Implement Cycle Crossover.
 */
public class CycleCrossoverFavoringStrongParents implements Crossover {

	float crossOverRate;
	Random r;
	boolean isPositionUsed[];

	// mappings from genePosToNodeIndex
	int[] P1genePosToNodeIndex;
	int[] P2genePosToNodeIndex;

	/**
	 * 
	 * @param crossOverRate
	 */
	public CycleCrossoverFavoringStrongParents(float crossOverRate, Random r,
			int chromsomeSize) {
		this.crossOverRate = crossOverRate;
		this.r = r;
		isPositionUsed = new boolean[chromsomeSize];
		P1genePosToNodeIndex = new int[chromsomeSize];
		P2genePosToNodeIndex = new int[chromsomeSize];
	}

	public void crossover(Population population) {

		assert (r != null) : "set random seed value before first use";

		int indexOfProxy;
		int nbrOfCrossovers = Math.round(population.chromosomes.size() * crossOverRate);

		int nbrOfGenesPerChromsome = GeneticPlacement.nodeProxies.length;

		ArrayList<Chromosome> offsprings = new ArrayList<Chromosome>(
				nbrOfCrossovers);

		// chromosomes representing parents
		Chromosome[] P1 = new Chromosome[nbrOfCrossovers];
		Chromosome[] P2 = new Chromosome[nbrOfCrossovers];
		Chromosome swapChromosome, offspring;

		// select parents for offsprings favoring strong individuals
		double sumOfAllFitnessValues = 0.0;
		for (Chromosome c : population.chromosomes)
			sumOfAllFitnessValues += (1 / c.fitness.doubleValue());
		for (int i = 0; i < nbrOfCrossovers; i++) {

			// generate random value between 0 and sumOfAllFitnessValues
			double canidateSelector = r.nextDouble() * sumOfAllFitnessValues;

			// get P1
			double currentFitnessSum = 0.0;
			for (Chromosome c : population.chromosomes) {
				currentFitnessSum += (1 / c.fitness.doubleValue());
				if (currentFitnessSum >= canidateSelector) {
					P1[i] = c;
					break;
				}
			}

			// get P2
			canidateSelector = r.nextDouble() * sumOfAllFitnessValues;
			currentFitnessSum = 0.0;
			for (Chromosome c : population.chromosomes) {
				currentFitnessSum += (1 / c.fitness.doubleValue());
				if (currentFitnessSum >= canidateSelector) {
					// if we just picked P1 again get next
					if (P1[i] == c) {
						int indexOfC = population.chromosomes.indexOf(c);
						// if c is last in list get first element
						if (indexOfC == population.chromosomes.size() - 1) {
							P2[i] = population.chromosomes.get(0);
						} else {
							// just pick next in liste
							P2[i] = population.chromosomes.get(
									indexOfC + 1);
						}
					} else {
						P2[i] = c;
						break;
					}
				}
			}

		}

		int[] swapIndex;

		// while nbr of crossovers < crossover rate
		for (int i = 0; i < nbrOfCrossovers; i++) {

			assert (P1[i] != null);
			assert (P2[i] != null);
			assert (P1[i] != P2[i]);

			// create new chromsome to represent offspring
			// TODO:later get from object pool
			offspring = new Chromosome(nbrOfGenesPerChromsome);

			// calculate genePos2NodeIndexMap for P1 and P2
			// TODO: this proves that generateGenePos2IndexMapping method is not
			// well placed in GenePlacementLeftAlignedDrop
			// eventually we can computer this reverse mapping only if required
			// and
			// save it for further use.
			// computation vs memory resource!
			GenePlacementLeftRightAlignedDrop.generateGenePos2IndexMapping(
					P1[i].Index2GenePositionInChromosome, P1genePosToNodeIndex);
			GenePlacementLeftRightAlignedDrop.generateGenePos2IndexMapping(
					P2[i].Index2GenePositionInChromosome, P2genePosToNodeIndex);

			// fill list with all available indices
			for (int ind = 0; ind < isPositionUsed.length; ind++)
				isPositionUsed[ind] = false;

			int nbrOfPlacedNodes = 0;

			// for all nodes to place in offspring
			while (nbrOfPlacedNodes < isPositionUsed.length)
			// cycle crossover
			{
				// randomly select cell to be placed
				indexOfProxy = r.nextInt(isPositionUsed.length);
				while (isPositionUsed[indexOfProxy]) {
					indexOfProxy++;
					if (indexOfProxy == isPositionUsed.length)
						indexOfProxy = 0;
				}

				while (true) {
					// get gene position in P1
					// put it into P1's position in the offspring
					offspring.Index2GenePositionInChromosome[indexOfProxy] = P1[i].Index2GenePositionInChromosome[indexOfProxy];
					isPositionUsed[indexOfProxy] = true;
					nbrOfPlacedNodes++;

					offspring.GeneRotation[indexOfProxy] = P1[i].GeneRotation[indexOfProxy];
					offspring.GeneXPadding[indexOfProxy] = P1[i].GeneXPadding[indexOfProxy];
					offspring.GeneYPadding[indexOfProxy] = P1[i].GeneYPadding[indexOfProxy];

					// get node at this position from P2
					indexOfProxy = P2genePosToNodeIndex[P1[i].Index2GenePositionInChromosome[indexOfProxy]];

					// if it is already in the offspring the current cycle is
					// finished
					if (isPositionUsed[indexOfProxy])
						break;

				}// cycle ended

				// swap P1 and P2 to start the next cycle from the other
				// parent
				swapChromosome = P1[i];
				P1[i] = P2[i];
				P2[i] = swapChromosome;

				swapIndex = P1genePosToNodeIndex;
				P1genePosToNodeIndex = P2genePosToNodeIndex;
				P2genePosToNodeIndex = swapIndex;

			}

			assert (offspring.isIndex2GenePosValid());

			// create new chromosome from configuration calculated above
			offsprings.add(offspring);
		}

		for (Chromosome c : offsprings) {
			assert c.size() == GeneticPlacement.nodeProxies.length;
		}

		// add offsprings to the population
		population.chromosomes.addAll(offsprings);

	}
}
/*
 * every cell is in the same location one parent or the other
 */
