/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Evolver.java
 * Written by Team 4: Benedikt Mueller, Richard Fallert
 * 
 * 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.genetic2;


import com.sun.electric.tool.placement.PlacementFrame.PlacementNetwork;

import java.util.List;
import java.util.Random;



/**
 * Class for running a genetic algorithm in a separate thread.
 * Each Evolver iteratively picks individuals from the population, locks them
 * and uses them for evolution.
 * Different selection methods are implemented here.
 */
public class Evolver implements Runnable
{
	List<PlacementNetwork> allNetworks;
	
	private Individual[] indis;
	private Random rand;
	private long evolutionStepTime;
	double p;
	long steps;
	
	/**
	 * Constructor of an Evolver.
	 * @param indis All the individuals in the whole population.
	 * @param evolutionStepTime How many milliseconds a single step runs.
	 * @param evolutionSteps How many steps the evolve-function should iterate.
	 * @param rand An exclusive random number generator, not to be used by other threads.
	 */
	Evolver(Individual[] indis, long evolutionStepTime,  Random rand)
	{
		this.indis = indis;
		this.evolutionStepTime = evolutionStepTime;
		this.rand = rand;
	}
	
	/**
	 * Updates the information of how much of the runtime is already over.
	 */
	public void setProgress(double p)
	{
		this.p = p;		
	}
	
//	/**
//	 * Local steady-state evolution with diversity bonus.
//	 */
//	private void evoDiverseLocal()
//	{		
//		int comparison;
//		comparison = rand.nextInt(indis.length);
//		while(!indis[comparison].rwLock.writeLock().tryLock())
//		{
//			comparison = rand.nextInt(indis.length);
//		}
//		
//		int[] positions = new int[3];
//		positions[0] = rand.nextInt(indis.length);
//		while(!indis[positions[0]].rwLock.writeLock().tryLock())
//		{
//			positions[0] = rand.nextInt(indis.length);
//		}
//		
//		for(int i = 1; i < positions.length; i++)
//		{
//			positions[i] = (positions[0]+(int)(20.0*rand.nextGaussian()))%indis.length;
//			if(positions[i] < 0) positions[i] += indis.length;
//			while(!indis[positions[i]].rwLock.writeLock().tryLock())
//			{
//				positions[i] = (positions[0]+(int)(20.0*rand.nextGaussian()))%indis.length;
//				if(positions[i] < 0) positions[i] += indis.length;
//			}
//		}
//
//		//double d0 = indis[positions[0]].distance();
//		//double d1 = indis[positions[1]].distance();
//		//double d2 = indis[positions[2]].distance();
//		
//		// diversity bonus:
//		double d0 = indis[positions[0]].distance(indis[comparison]);
//		double d1 = indis[positions[1]].distance(indis[comparison]);
//		double d2 = indis[positions[2]].distance(indis[comparison]);
//		
//		/*
//		if(rand.nextInt(100000) == 0)
//		{
//			System.out.println("d0: " + d0 + " d1: " + d1 + " d2: " + d2);
//		}
//		*/
//		
//		// overwrite the loser with the crossover of the best two
//		if(indis[positions[0]].getBadness()-d0 >= indis[positions[1]].getBadness()-d1 &&
//				indis[positions[0]].getBadness()-d0 >= indis[positions[2]].getBadness()-d2)
//		{
//			indis[positions[0]].deriveFrom(indis[positions[1]], indis[positions[2]], rand);
//		}
//		else if(indis[positions[1]].getBadness()-d1 >= indis[positions[0]].getBadness()-d0 &&
//				indis[positions[1]].getBadness()-d1 >= indis[positions[2]].getBadness()-d2)
//		{
//			indis[positions[1]].deriveFrom(indis[positions[0]], indis[positions[2]], rand);
//		}	
//		else
//		{
//			indis[positions[2]].deriveFrom(indis[positions[0]], indis[positions[1]], rand);
//		}	
//		
//		for(int i = 0; i < positions.length; i++)
//		{
//			indis[positions[i]].rwLock.writeLock().unlock();
//		}
//		indis[comparison].rwLock.writeLock().unlock();
//	}
	
	/**
	 * Local steady-state evolution.
	 */
	private void evoLocal()
	{		
		int[] positions = new int[3];
		positions[0] = rand.nextInt(indis.length);
		while(!indis[positions[0]].rwLock.writeLock().tryLock())
		{
			positions[0] = rand.nextInt(indis.length);
		}
		
		for(int i = 1; i < positions.length; i++)
		{
			positions[i] = (positions[0]+(int)(20.0*rand.nextGaussian()))%indis.length;
			if(positions[i] < 0) positions[i] += indis.length;
			while(!indis[positions[i]].rwLock.writeLock().tryLock())
			{
				positions[i] = (positions[0]+(int)(20.0*rand.nextGaussian()))%indis.length;
				if(positions[i] < 0) positions[i] += indis.length;
			}
		}

		
		// overwrite the loser with the crossover of the best two
		if(indis[positions[0]].getBadness() >= indis[positions[1]].getBadness() &&
				indis[positions[0]].getBadness() >= indis[positions[2]].getBadness())
		{
			indis[positions[0]].deriveFrom(indis[positions[1]], indis[positions[2]], rand);
		}
		else if(indis[positions[1]].getBadness() >= indis[positions[0]].getBadness() &&
				indis[positions[1]].getBadness() >= indis[positions[2]].getBadness())
		{
			indis[positions[1]].deriveFrom(indis[positions[0]], indis[positions[2]], rand);
		}	
		else
		{
			indis[positions[2]].deriveFrom(indis[positions[0]], indis[positions[1]], rand);
		}	
		
		for(int i = 0; i < positions.length; i++)
		{
			indis[positions[i]].rwLock.writeLock().unlock();
		}
	}
	
	/**
	 * @return The number of evolution steps (generations) the evolver has iterated
	 */
	public long getSteps()
	{
		return steps;
	}
	
	public void run()
	{
		long t = System.currentTimeMillis();		

		while((System.currentTimeMillis()-t) < evolutionStepTime)
		{	
			//evoDiverseLocal();
			evoLocal();
			steps++;
		}
	}

}
