/*
 * Created on 11/12/2006
 */

/**
 * @author Carlos Andres Sierra - Cod. 257188
 *         Jefferson Velasquez - Cod. 257196
 *         Juan Albeiro Caballero - Cod. 257200  			
 */

import java.text.DecimalFormat;
import java.util.Random;

public class RQS_Me10 {

	// Se inicializan las variables globales de la clase
	private double Probability[][] = null; 
	private int Array[] = null, Aux[] = null;
	private int nTamaño = 0, nComparaciones = 0, nProbability = 0;
	private double ValorEsperado = 0;
	
	public int nPermutations = 0, nExcedidos = 0;
	
	
	/**
	 * Constructor de la clase
	 */
	public RQS_Me10(int n) 
	{
		nTamaño = n;
		initApplication();
	}

	/*
	 * Este método es el que hace el llamado a los principales métodos de la clase
	 */
	private void initApplication()
	{
		nPermutations = Factorial(nTamaño);
		initProbability();
		initArray();
		Permutations(Array,0,nTamaño);
	}
	
	/*
	 * Inicialización de la matriz donde se almacena la probabilidad
	 */	
	private void initProbability()
	{
		Probability = new double[100][2];
		for(int i = 0; i < 100; i++)
		{
			Probability[i][0] = (-1.0);
			Probability[i][1] = 0.0;
		}
	}
	
	/*
	 * Inicialización del arreglo donde se van a almacenar las diferentes permutaciones 
	 */
	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;
	}
	
	
	
	/*
	 * 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;
	}
	
	
	/*
	 * Este método se utiliza para almacenar en la matriz de probabilidad 
	 * la cantidad hallada de comparaciones en la permutación evaluada
	 */
	private void Register_Probability()
	{
		// Aumento de ocurrencia para un determinado numero de comparaciones
		for(int i = 0; i <= nProbability ; i++)
		{
			if(Probability[i][0] == nComparaciones)
			{
				Probability[i][1] += 1.0;
				i = 150;
			}
			else
				if(Probability[i][0] > nComparaciones)
				{
					for(int l = nProbability; l > i; l--)
					{
						Probability[l][0] = Probability[l - 1][0];
						Probability[l][1] = Probability[l - 1][1];
					}
					Probability[i][0] = nComparaciones;
					Probability[i][1] = 1.0;
					nProbability++;
					i = 150;
				}
				else
					if(Probability[i][0] == (-1))
					{
						Probability[i][0] = nComparaciones;
						Probability[i][1] = 1.0;
						nProbability++;
						i = 150;
					}	
	
		}
	}
	
	
	
	/*
	 * Este metodo es utilizado para tomar las permutaciones y luego hacer el llamado al
	 * método que ejecuta el RQS. Aqui mediante un ciclo se generan todas las posibles 
	 * permutaciones para un determinado n, y en cada una se calculan los valores 
	 * necesarios para obtener los resultados finales.
	 */	
	private void Permutations (int Par1[], int Par2, int Par3) 
	{
		if (Par2 == Par3) 
		{
			for(int i = 0; i < Par3; i++)
				Aux[i] = Array[i];
			
			nComparaciones = 0;			
			Randomized_QuickSort(Aux,0,Par3 - 1);
		
			// Se llama al método para registrar el número de comparaciones 
			Register_Probability();
		} 
		else
		{ 
			/*
			 * Con este ciclo se crean todas las posibles permutaciones para un 
			 * determinado n  
			 */
			for (int j = Par2; j < Par3; j++) 
			{
				Swap(Par1,Par2,j);
				Permutations(Par1,Par2 + 1,Par3);
				Swap(Par1,Par2,j);
			}
		}	
	}
	
	
	/*
	 * Este método estima el valor esperado de las posibles permutaciones, en este caso 
	 * se toman los valores de comparaciones y se multiplican por la probabilidad asignada
	 */
	public String Valor_Esperado()
	{
		DecimalFormat dc = new DecimalFormat("0.000000");
		
		for(int i = 0; i < nProbability; i++)
			ValorEsperado += (Probability[i][0] * (Probability[i][1] / nPermutations));
		
		return dc.format(ValorEsperado);
	}
	
	
	public int Count_Excedidos()
	{
		double temp = ValorEsperado * 1.5;
		
		for(int i = 0; i < nProbability; i++)
			if(Probability[i][0] > temp)
				nExcedidos += Probability[i][1];
		
		return nExcedidos;
	}
	
	
	
	/*
	 * Estos son los tres métodos con los cuales se ejecuta el RQS 
	 */
	private int Partition(int Par1[], int Par2, int Par3)
	{
		int x = Par1[Par3], i = Par2 - 1;
		
		for(int j = Par2; j < Par3; j++)
		{
			if(Par1[j] <= x)
			{
				i++;
				Swap(Par1,i,j);
			}
			nComparaciones++;
		}
		Swap(Par1,i + 1,Par3);
		
		return i + 1;
	}
	
    // Método que hace aleatorio al Quick Sort
	private int Randomized_Partition(int Par1[], int Par2, int Par3)
	{
		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);
		
		return Partition(Par1,Par2,Par3);
	}
	
	// Método principal del RQS
	private void Randomized_QuickSort(int Par1[], int Par2, int Par3)
	{
		if(Par2 < Par3)
		{
			int q = Randomized_Partition(Par1,Par2,Par3);
			Randomized_QuickSort(Par1,Par2,q - 1);
			Randomized_QuickSort(Par1,q + 1,Par3);
		}
	}
}