package math.statistics;

/**
 * Enumerates all permutations on N elements.
 * @author Daniel
 *
 */
public class Permutation {
	
	/**
	 * Prints a pseudorandom permutation of the integers 0 through n.
	 * <pre>
	 * n = 6:
	 * 5 0 2 3 1 4 
	 * . * . . . .
	 * . . . . * .
	 * . . * . . .
	 * . . . * . .
	 * . . . . . *
	 * * . . . . .</pre>
	 * @param n
	 */
	public static void permutation(int n){
		int i, j;
		int[] a = new int[n];

		// insert integers 0..n-1
		for(i=0; i<n; i++)
			a[i] = i;

		// shuffle
		for(i=0; i<n; i++){
			int r = (int) (Math.random() * (i+1)); // int between 0 and i
			int swap = a[r];
			a[r] = a[i];
			a[i] = swap;
		}

		// print permutation
		for(i=0; i<n; i++)
			System.out.print(a[i] + " ");
		System.out.println("");

		// print checkerboard visualization
		for(i=0; i<n; i++){
			for(j=0; j<n; j++){
				if (a[j] == i) System.out.print("* ");
				else           System.out.print(". ");
			}
			System.out.println("");
		}
	}
	
	/**
	 * Read in a permutation and print out the inverse permutation.
	 * @param permutation
	 */
	public static void inversePerumation(int[] permutation){
		int i;
		int n = permutation.length; 
		// read in permutation
		int[] a = new int[n];
		for(i=0; i<n; i++)
			a[i] = permutation[i];

		// check if valid
		boolean[] exists = new boolean[n];
		for(i=0; i<n; i++){
			if(a[i]<0 || a[i]>=n || exists[a[i]])
				throw new RuntimeException("Input is not a permutation.");
			exists[a[i]] = true;
		}

		// invert
		int[] ainv = new int[n];
		for(i=0; i<n; i++)
			ainv[a[i]] = i;

		// print out
		for(i=0; i<n; i++)
			System.out.print(ainv[i] + " ");
		System.out.println();
	}

    // print N! permutation of the characters of the string s (in order)
    public  static void perm1(String s) { perm1("", s); }
    private static void perm1(String prefix, String s) {
        int N = s.length();
        if (N == 0) System.out.println(prefix);
        else {
            for (int i = 0; i < N; i++)
               perm1(prefix + s.charAt(i), s.substring(0, i) + s.substring(i+1, N));
        }

    }

    // print N! permutation of the elements of array a (not in order)
    public static void perm2(String s) {
       int N = s.length();
       char[] a = new char[N];
       for (int i = 0; i < N; i++)
           a[i] = s.charAt(i);
       perm2(a, N);
    }

    private static void perm2(char[] a, int n) {
        if (n == 1) {
            System.out.println(a);
            return;
        }
        for (int i = 0; i < n; i++) {
            swap(a, i, n-1);
            perm2(a, n-1);
            swap(a, i, n-1);
        }
    }  
    
    public static void choose(char[] a, int R) { enumerate(a, a.length, R); }

    // Enumerates all permutations of size k chosen from N elements.
    private static void enumerate(char[] a, int n, int r) {
        if (r == 0) {
            for (int i = n; i < a.length; i++)
                System.out.print(a[i]);
            System.out.println();
            return;
        }
        for (int i = 0; i < n; i++) {
            swap(a, i, n-1);
            enumerate(a, n-1, r-1);
            swap(a, i, n-1);
         }
    } 

    // swap the characters at indices i and j
    private static void swap(char[] a, int i, int j) {
        char c;
        c = a[i]; a[i] = a[j]; a[j] = c;
    }

	
	public static void main(String[] args) { 
		int n = 6;
		permutation(n);
		System.out.println();
		System.out.println("Inverse Permutation of 5 0 2 3 1 4 = ");
		inversePerumation(new int[]{5, 0, 2, 3, 1, 4});

		String alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
		String elements = alphabet.substring(0, n);
		perm1(elements);
		System.out.println();
		perm2(elements);
		
		int k = 2;
        char[] a = new char[n];
        for (int i = 0; i < n; i++)
            a[i] = alphabet.charAt(i);
        choose(a, k);
	}
}