/*
 * Created on 14/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 Probability_PermuteBySorting {

	// Declaración de las variables globales de la clase
	private double Probability[][] = null;
	private int Array[] = null;
	private int Aux[] = null;
	private int nTamaño = 0, indice = 0;
	
	public int nPermutations = 0;
	public String nProbability = "";
	
	
	/**
	 * Constructor de la clase
	 */
	public Probability_PermuteBySorting(int tamaño) 
	{
		nTamaño = tamaño;
		initApplication();
	}

	/*
	 * Este método se encarga de llamar a los métodos fundamentales de la aplicación
	 */
	private void initApplication()
	{
		nPermutations = Factorial(nTamaño);
		initArray();
		initProbability();
		Permutations(Array,0,nTamaño);
		getProbability();
	}

	/*
	 * Inicialización del arreglo que servira de base para hallar los calculos requeridos 
	 */
	private void initArray()
	{
		Array = new int[nTamaño];
		Aux = new int[nTamaño];
	
		for(int i = 0; i < nTamaño; i++)
			Array[i] = i + 1;
	}
	
	/*
	 * Inicialización de la matriz donde se almacena la probabilidad
	 */	
	private void initProbability()
	{
		Probability = new double[nPermutations][nTamaño + 1];
		
		/*
		 * Este llamado es para almacenar todas las posibles permutaciones en la matriz
		 * de probabilidad
		 */ 
		PermutationsPro(Array,0,nTamaño);
		
		initArray();
	}
	
	
	
	/*
	 * Este metodo halla el factorial de un numero mediante recursión
	 */
	private int Factorial(int Par)
	{
		if(Par == 1)
			return 1;
		else
			return Par * Factorial(Par - 1);
	}
	
	
	/*
	 * Este metodo es usado para intercambiar dos posiciones de un mismo arreglo
	 */
	private void Swap (int Par1[],int Par2,int Par3) 
	{
		int	temp = 0;

		temp = Par1[Par2];
		Par1[Par2] = Par1[Par3];
		Par1[Par3] = temp;
	}
	
	
	/*
	 * Metodo utilizado para determinar todas las posibles permutaciones para un arreglo 
	 * de tamaño n
	 */
	private void PermutationsPro (int Par1[], int Par2, int Par3) 
	{
		int j = 0;	
		if (Par2 == Par3) 
		{
			for(int i = 0; i < nTamaño; i++)
				Probability[indice][i] = Par1[i];
			
			indice++;
		} 
		else
		{ 
			/*
			 * Con este ciclo se crean todas las posibles permutaciones para un 
			 * determinado n  
			 */
			for (j = Par2; j < Par3; j++) 
			{
				Swap(Par1,Par2,j);
				PermutationsPro(Par1,Par2 + 1,Par3);
				Swap(Par1,Par2,j);
			}
		}	
	}
	
	/*
	 * Metodo utilizado para determinar todas las posibles permutaciones para un arreglo 
	 * de tamaño n  y ejecutar para una el algoritmo de aleatorización
	 */
	private void Permutations (int Par1[], int Par2, int Par3) 
	{
		int j = 0;	
		if (Par2 == Par3) 
		{
			Ejecutions(Par1);
		} 
		else
		{ 
			/*
			 * Con este ciclo se crean todas las posibles permutaciones para un 
			 * determinado n  
			 */
			for (j = Par2; j < Par3; j++) 
			{
				Swap(Par1,Par2,j);
				Permutations(Par1,Par2 + 1,Par3);
				Swap(Par1,Par2,j);
			}
		}	
	}
	
	
	/*
	 * Este método se utiliza para almacenar en la matriz de probabilidad el numero de
	 * ocurrencias que se tienen para determinada permutación
	 */
	private void Register_Probability(int Par[])
	{
		int j = 0;
		for(int i = 0; i < nPermutations; i++)
		{
			for(j = 0; j < nTamaño; j++)
				if(Probability[i][j] != Par[j])
					j = 10;

			if(j == nTamaño)
			{
				Probability[i][nTamaño] += 1.0;
				i = nPermutations;
			}
		}
	}
	
	/*
	 * Este método se utiliza para conseguir un String que contiene todas las posibles
	 * permutaciones junto con su respectiva probabilidad
	 */
	private void getProbability()
	{
		DecimalFormat df = new DecimalFormat("0.00000");
		for(int i = 0; i < nPermutations; i++)
		{
			if(Probability[i][nTamaño] != 0)
			{
				nProbability += "P(" + (int)Probability[i][0];
				for(int j = 1; j < nTamaño; j++)
					nProbability += "," + (int)Probability[i][j];
				
				nProbability += "): " + df.format(Probability[i][nTamaño] / (100 * nPermutations));
				
				nProbability += "\n"; 
			}
		}
	}

	
	/*
	 * Este método es el que maneja el número de ejecuciones y los pasos a seguir en cada una 
	 */
	private void Ejecutions(int Par[])
	{
		for(int i = 0; i < 100; i++)
		{
			for(int j = 0; j < nTamaño; j++)
				Aux[j] = Array[j];
			
			// Se ejecuta el método que realiza la permutación aleatoria al arreglo
			Permute_By_Sorting(Aux);
			// Se registra el valor obtenido en la matriz de probabilidad
			Register_Probability(Aux);
		}
	}
	
	private void Permute_By_Sorting(int Par[])
	{
		int temp[] = new int[nTamaño];
		Random random = new Random();
		
		for(int i = 0; i < nTamaño; i++)
			// Asignación de valores para determinar el orden de la aleatorización
			temp[i] = (int)random.nextInt(nPermutations * nPermutations * nPermutations);
		
		Randomized_QuickSort(temp,0,nTamaño - 1,Par);
	}
	
	/*
	 * El método de ordenamiento usado sera el de RQS debido a su optimabilidad 
	 */
	private int Partition(int Par1[], int Par2, int Par3, int Par4[])
	{
		int x = Par1[Par3], i = Par2 - 1;
		
		for(int j = Par2; j < Par3; j++)
		{
			if(Par1[j] <= x)
			{
				i++;
				Swap(Par1,i,j);
				Swap(Par4,i,j);
			}
		}
		Swap(Par1,i + 1,Par3);
		Swap(Par4,i + 1,Par3);
		
		return i + 1;
	}
	
    // Método que hace aleatorio al Quick Sort
	private int Randomized_Partition(int Par1[], int Par2, int Par3, int Par4[])
	{
		Random random = new Random();
		int temp = -1;
		
		// Se verifica que el valor aleatorio este entre Par2 (p) y Par3 (r)
		while(temp < Par2)
			temp = (int)random.nextInt(Par3);
		
		Swap(Par1,temp,Par3);
		Swap(Par4,temp,Par3);
		
		return Partition(Par1,Par2,Par3,Par4);
	}
	
	// Método principal del RQS
	private void Randomized_QuickSort(int Par1[], int Par2, int Par3, int Par4[])
	{
		if(Par2 < Par3)
		{
			int q = Randomized_Partition(Par1,Par2,Par3,Par4);
			Randomized_QuickSort(Par1,Par2,q - 1,Par4);
			Randomized_QuickSort(Par1,q + 1,Par3,Par4);
		}
	}
}
