package ca.pfv.spmf.algorithms.sort;
/* This file is copyright (c) 2008-2013 Philippe Fournier-Viger
* 
* This file is part of the SPMF DATA MINING SOFTWARE
* (http://www.philippe-fournier-viger.com/spmf).
* 
* SPMF 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.
* 
* SPMF 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
* SPMF. If not, see <http://www.gnu.org/licenses/>.
*/

import java.util.Random;

/**
 * Implementation of a few selection algorithms (randomized select, etc.).
 * based on description from the book "Introduction to Algorithms" from MIT Press.
 * 
 * Please refer to that book for details about the algorithms and proofs that
 * they are correct.
 */
public class Select {
	private static Random random = new Random(System.currentTimeMillis());
	 
	
	//======================== FOR INTEGER VALUES ====================
	
	/**
	 * Method to select the ith smallest integer of a sub-array in average linear time.
	 * Based on the Randomized-Select algorithm in "Introduction to algorithms" book by Cormen et al. (2001).
	 * @param a   array of integers
	 * @param i  the i-th value
	 * @param p  the first position of the subarray
	 * @param r  the last position of the subarray
	 * @return the element of "a" that is larger than i elements.
	 */
	// ((see the book for details about the algorithm)
	public static int randomizedSelect(int[] a, int i, int p, int r) {
//		int p = 0;
//		int r = a.length-1;
		
		while(true){
			if(p == r){
				return a[p];
			}
			int q = randomizedPartition(a, p, r);  
			int k = q-p+1;

			if(i == k-1){
				return a[q];
			}
			else if(i <k){
				r = q -1;
			}else{
				i = i - k;
				p = q + 1;
			}
		}
  	}
	
	
	/**
	 * Method to select the ith smallest integer of an array in average linear time.
	 * Based on the Randomized-Select algorithm in "Introduction to algorithms" book by Cormen et al. (2001).
	 * @param a   array of integers
	 * @param i  the i-th value
	 * @return the element of "a" that is larger than i elements.
	 */
	// ((see the book for details about the algorithm)
	public static int randomizedSelect(int[] a, int i) {
		int p = 0;
		int r = a.length-1;
		
		while(true){
			if(p == r){
				return a[p];
			}
			int q = randomizedPartition(a, p, r);  
			int k = q-p+1;

			if(i == k-1){
				return a[q];
			}
			else if(i <k){
				r = q -1;
			}else{
				i = i - k;
				p = q + 1;
			}
		}
  	}
	
	// (see the book for details about the algorithm)
    private static int randomizedPartition(int[] a, int p, int r) {
		int i = 0;
		if(p < r){
			i =  p + random.nextInt(r-p);
		}else{
			i =  r + random.nextInt(p-r);
		}
		swap(a, r, i);
		return partition(a, p, r);  // call the partition method of quicksort.
	}
    
 // (see the book for details about the algorithm)
    private static int partition(int[] a, int p, int r) {
		int x = a[r];
		int i = p - 1;
		for(int j = p; j <= r-1; j++){
			if(a[j] <= x){
				i = i+1;
				swap(a, i, j);
			}
		}
		swap(a, i+1, r);
		return i+1;
	}

	/**
	 * Method to swap two elements in an array
	 * @param array the array
	 * @param i position of an element
	 * @param j position of another element
	 */
	private static void swap(int[] array, int i, int j) {
        int valueI = array[i];
        array[i] = array[j];
        array[j] = valueI;  
    }
	
	//========================= FOR DOUBLE VALUES ===================
	
	/**
	 * Method to select the ith smallest integer of an array in average linear time.
	 * Based on the Randomized-Select algorithm in "Introduction to algorithms" book by Cormen et al. (2001).
	 * @param a   array of integers
	 * @param i  the i-th value
	 * @return the element of "a" that is larger than i elements.
	 */
	// ((see the book for details about the algorithm)
	public static double randomizedSelect(double[] a, int i) {
		int p = 0;
		int r = a.length-1;
		
		while(true){
			if(p == r){
				return a[p];
			}
			int q = randomizedPartition(a, p, r);  
			int k = q-p+1;

			if(i == k-1){
				return a[q];
			}
			else if(i <k){
				r = q -1;
			}else{
				i = i - k;
				p = q + 1;
			}
		}
  	}
	
	// (see the book for details about the algorithm)
    private static int randomizedPartition(double[] a, int p, int r) {
		int i = 0;
		if(p < r){
			i =  p + random.nextInt(r-p);
		}else{
			i =  r + random.nextInt(p-r);
		}
		swap(a, r, i);
		return partition(a, p, r);  // call the partition method of quicksort.
	}
    
 // (see the book for details about the algorithm)
    private static int partition(double[] a, int p, int r) {
    	double x = a[r];
		int i = p - 1;
		for(int j = p; j <= r-1; j++){
			if(a[j] <= x){
				i = i+1;
				swap(a, i, j);
			}
		}
		swap(a, i+1, r);
		return i+1;
	}

	/**
	 * Method to swap two elements in an array
	 * @param array the array
	 * @param i position of an element
	 * @param j position of another element
	 */
	private static void swap(double[] array, int i, int j) {
		double valueI = array[i];
        array[i] = array[j];
        array[j] = valueI;  
    }
}