/*
 * Created on 11/12/2006
 */

/**
 * @author Carlos Andres Sierra - Cod. 257188
 *         Jefferson Velasquez - Cod. 257196
 *         Juan Albeiro Caballero - Cod. 257200  			
 */

import java.util.Random;
import java.text.DecimalFormat;

public class Test_Chips {

	// Inicialización de las variables privadas globales de la clase
	private byte Chips[] = null;
	private byte Estado[] = null;
	private byte Daño[] = null;
	private int nChips = 0, nEjecuciones = 0, nActivos = 0, tamaño = 0;
	private double nMalos = 0, nDaño = 0, Comparaciones = 0.0;
	
	// Inicialización de las variables públicas globales de la clase
	public double Probability[][] = null;
	public String Salida = "";
	
	// Constructor de la clase
	public Test_Chips(int numChips, int numEjecuciones, double numProporcionMalos, double numProbabilidadDaño) 
	{
		nChips = nActivos = numChips;
		nEjecuciones = numEjecuciones;
		nMalos = numProporcionMalos;
		nDaño = numProbabilidadDaño;
		
		initAplication();
	}
	
	private void initAplication()
	{
		DecimalFormat df = new DecimalFormat("0.00000");
		
		initChips();
		initDaño();
		initProbability();
		
		for(int i = 0; i < nEjecuciones; i++)
		{
			while(nActivos != 1)
				Test_Pair();
			
			Register_Probability();
			Comparaciones = 0.0;
			initEstado();
			nActivos = nChips;
			RandomVector(Chips);
		}
		
		for(int i = 0; i < tamaño; i++)
		{
			Probability[i][1] /= nEjecuciones;
			Salida += "  P(" + (int)Probability[i][0] + "): " + df.format(Probability[i][1]) + "\n";
		}
	}
	
	/*
	 * Inicialización del vector que almacena los chips y los distribuye aleatoriamente
	 * de acuerdo a la proporción de chips buenos y malos
	 */
	private void initChips()
	{
		int temp1 = 0;
		double temp2 = 0.0;
		Chips = new byte[nChips];
		
		if(nMalos >= 49.0)	
			nMalos = 49.0;
			
		temp2 = nChips * (nMalos / 100);
		if((nMalos % 1) <= 0.5)
			temp1 = (int)temp2;
		else
			temp1 = ((int)temp2) + 1;
		
		// Mantener la precondicion de que |G| >= |B| + 2
		if((nChips - temp1) < (temp1 + 2))
			temp1--;
		
		// 0:Chip Malo			1:Chip Bueno
		for(int i = 0; i < temp1; i++)
			Chips[i] = 0;
		for(int i = temp1; i < nChips; i++)
			Chips[i] = 1;
		
		RandomVector(Chips);
		initEstado();
	}
	
	/* 
	 * Manejo de la probabilidad de daño de los chips, i.e., cuando dan respuestas
	 * correctas, mediante un vector distribuido de acuerdo al valor de daño ingresado  
	 */ 
	private void initDaño()
	{
		// 0:Incorrecto			1:Correcto
		Daño = new byte[100];
		
		int temp = 0;
		if((nDaño % 1) <= 0.5)
			temp = (int)nDaño;
		else
			temp = ((int)nDaño) + 1;
			
		for(int i = 0; i < temp; i++)
			Daño[i] = 0;
		for(int i = temp; i < 100; i++)
			Daño[i] = 1;
		
		RandomVector(Daño);
	}
	
	/*
	 * Inicialización del vector que muestra el estado de la recursión (eliminado o activo)
	 * para los distintos chips que componen el test 
	 */
	private void initEstado()
	{
		Estado = new byte[nChips];
		// 0:Eliminado			1:Activo
		for(int i = 0; i < nChips; i++)
			Estado[i] = 1;
	}
	
	/*
	 * Inicialización de la matriz que maneja las distintas probabilidades del número
	 * de comparaciones obtenidas en cada una de las distintas ejecuciones
	 */
	private void initProbability()
	{
		Probability = new double[60][2];
		for(int i = 0; i < 60; i++)
		{
			Probability[i][0] = (-1.0);
			Probability[i][1] = 0.0;
		}
	}
	
	
	
	/*
	 * Test_Pair() realiza las pruebas por parejas de chips, haciendo todas las
	 * posibles parejas y llamando al método Comparar(int Par1, int Par2)
	 */
	private void Test_Pair()
	{
		int i = 0, j = nChips - 1, temp = 0;
		
		System.out.println();
		while((i < nChips) && (nActivos > 1))
		{
			temp = 0;
			// Verificación de que el primer chip a probar no ha sido eliminado ya
			if(Estado[i] == 1)
				// Busca un chip de atras hacia adelante para ser un candidato del test
				while((j > i) && (temp == 0))
				{
					// Verificación de que el segundo chip a probar no ha sido eliminado ya
					if(Estado[j] == 1)
			 		{
			 			Comparar(i,j);
			 			temp = 1;
			 		}
					j--;
				}
			i++;
		}
	}
	
	/*
	 * Comparar(int Par1, int Par2) es utilizado para realizar la comparación entre dos chips,
	 * los cuales son el Par1-ésimo y el Par2-ésimo, y dependiendo de las respuestas que den
	 * cada uno acerca del otro se realizara la respectiva eliminación 
	 */
	private void Comparar(int Par1, int Par2)
	{
		int temp1 = 0, temp2 = 0;
		
		// Obtienen las respuesta que cada chip da sobre el otro mediante el método getAnswer
		temp1 = getAnswer(Par1, Par2);
		temp2 = getAnswer(Par2, Par1);
		
		if((temp1 == 0) && (temp2 == 0))
		{
			/*
			 *  Si las respuestas dadas por cada uno de los chips son "Malo", 
			 *  se descartan ambos
			 */
			Estado[Par1] = Estado[Par2] = 0;
			nActivos -= 2;
		}
		else
			if((temp1 == 1) && (temp2 == 1))
			{
				/*
				 *  Si las respuestas dadas por cada uno de los chips son "Bueno", 
				 *  se descarta uno de los dos
				 */
				Estado[Par2] = 0;
				nActivos -= 1;
			}
			else
			{
				/*
				 * Si las respuestas dadas por cada uno de los chips son diferentes, se 
				 * descartan ambos
				 */ 
				Estado[Par1] = Estado[Par2] = 0;
				nActivos -= 2;
			}
		
		// Se aumenta en dos el número de comparaciones realizadas en toda la ejecución
		Comparaciones += 2.0;
	}
	
	/*
	 * getAnswer(int Par1, int Par2) determina la respuesta que da el Chip1 (Par1) acerca
	 * del tipo del Chip2 (Par2), el cual es bueno(return 1) o malo(return  0)
	 */
	private int getAnswer(int Par1, int Par2)
	{
		Random random = new Random();
		int aux = 0;
		// Validación para determinar si el Chip1 es malo o no
		if(Chips[Par1] == 0)
		{
			aux = (int)random.nextInt(100);
			// Si Daño[aux] es igual a 1, dara una respuesta correcta sobre el Chip2
			if(Daño[aux] == 1)
				return Chips[Par2];
			else
				// Como el Chip1 dara una respuesta incorrecta, se invierte el tipo del Chip2
				if(Chips[Par2] == 0)
					return 1;
				else
					return 0;
		}
		else
			return Chips[Par2];
	}
	
	/* 
	 * 
	 */
	private void Register_Probability()
	{
		// Aumento de ocurrencia para un determinado numero de comparaciones
		for(int i = 0; i <= tamaño ; i++)
		{
			//System.out.println("Indice: " + Probability[i][0] + "    Comparaciones: " + Comparaciones);
			if(Probability[i][0] == Comparaciones)
			{
				Probability[i][1] += 1.0;
				i = 750;
			}
			else
				if(Probability[i][0] > Comparaciones)
				{
					for(int l = tamaño; l > i; l--)
					{
						Probability[l][0] = Probability[l - 1][0];
						Probability[l][1] = Probability[l - 1][1];
					}
					Probability[i][0] = Comparaciones;
					Probability[i][1] = 1.0;
					tamaño++;
					i = 750;
				}
				else
					if(Probability[i][0] == (-1))
					{
						Probability[i][0] = Comparaciones;
						Probability[i][1] = 1.0;
						tamaño++;
						i = 750;
					}	
	
		}
	}
		
	
	/*
	 * Este metodo es utilizado para colocar de forma aleatoria los elementos dentro de un
	 * arreglo que inicialmente esta ordenado secuencialmente
	 */	
	private void RandomVector(byte Par[])
	{
		Random random = new Random();
		int pos = 0; 
		byte swat = 0;
		
		for(int i = 0; i < Par.length - 1; i++)
		{
			pos = (int)random.nextInt(Par.length);
			swat = Par[i];
			Par[i] = Par[pos];
			Par[pos] = swat;
		}
	}

}
