/*
  Name: sorting.java
  Purpose: implementation of sorting algorithms
  Author: Leo Liberti
  Source: Java
  History: 1/9/11  work started
*/

import java.io.*;
import java.util.*;
import java.lang.*;

class intPair {
    int first;
    int second;
}

class sorting {

    // print an array
    public static void printArray(int[] x) {
	printArray(x, 0, x.length);
	System.out.println();
    }

    // print a subarray (x[first] ... x[last])
    public static void printArray(int[] x, int first, int last) {
	printArrayPivot(x, first, last, -1);
    }

    // print an array and emphasize a pivot element
    public static void printArrayPivot(int[] x, int pivot) {
	printArrayPivot(x, 0, x.length, pivot);
	System.out.println();
    }

    // print a subarray and emphasize a pivot element
    public static void printArrayPivot(int[] x, int first, int last, int pivot){
	for(int i = first; i < last; i++) {
	    if (x[i] == pivot) {
		System.out.print("[" + x[i] + "] ");
	    } else {
		System.out.print(x[i] + " ");
	    }
	}
    }

    // swap x[i] and x[j]
    public static void swap(int[] x, int i, int j) {
	assert(i >= 0 && i < x.length && j >= 0 && j < x.length) : 
	"index out of range";
	if (i != j) {
	    int t = x[i];
	    x[i] = x[j];
	    x[j] = t;
	}
    }

    // merge using arrays (but not "in place"): 
    //   sub-arrays x[i1...i2] and x[j1...j2] are sorted, 
    //   sort x[i1...i2,j1...j2]
    // ASSUME that i1 <= i2 < j1 <= j2
    public static void merge(int[] x, int i1, int i2, int j1, int j2) {
	assert(i1 <= i2 && i2 < j1 && j1 <= j2) : "error calling merge()";

	// arrays are indexed from 0 to length-1, so upper range limits
	// should be increased
	i2++;
	j2++;

	// number of components of interest
	int n = i2-i1 + j2-j1;

	// make some temporary space
	int[] y = new int [n];

	// counter for x[i1...i2]: initialize to i1
	int i = i1;

	// counter for x[j1...j2]: initialize to j1
	int j = j1;

	// counter for temporary storage y
	int k = 0;	

	while(i < i2 || j < j2) {
	    // continue until there is work to do

	    if (i < i2 && j < j2) {
		// both i and j are still in ranges of interest
		if (x[i] <= x[j]) {
		    // smallest component of range i1...i2 is smaller:
		    // copy to temporary storage array y
		    y[k] = x[i]; 

		    // increase counter for range i1...i2
		    i++;
		} else {
		    // smallest component of range j1...j2 is smaller:
		    // copy to temporary storage array y
		    y[k] = x[j];

		    // increase counter for range j1...j2
		    j++;
		}
	    } else if (i == i2 && j < j2) {
		// range i1...i2 is done, but j1...j2 still has components,
		// copy them to y and increase counter
		y[k] = x[j];
		j++;
	    } else if (i < i2 && j == j2) {
		// range i1...i2 still has components and j1...j2 is done,
		// copy comps. from i1...i2 to y and increase counter
		y[k] = x[i]; 
		i++;
	    }

	    // don't forget to increase counter for temp. storage y
	    k++;
	} 
	
	// now prepare to copy from (sorted) temporary storage y back to x
	k = 0;
	// the counter h will index x over the ranges i1...i2 and j1...j2
	int h = i1;
	while(k < n) {
	    x[h] = y[k];
	    k++;
	    h++;
	    if (h == i2) {
		h = j1;
	    }
	}
    }

    // mergesort x from first to last
    //   space is used for prettyprinting x as we go along
    public static void mergeSort(int[] x, int first, int last, String space) {
	if (first < last) {
	    // recursive case
	    System.out.println(space + 
			     "mergeSort(" + first + "," + last + ")");

	    // get midpoint index for x
	    int m = (first + last) / 2;
	    String morespace = space + "  ";

	    // sort subsequences to midpoint
	    mergeSort(x, first, m, morespace);
	    // ...and from midpoint to end
	    mergeSort(x, m+1, last, morespace);
	    // merge the sorted subsequences
	    merge(x, first, m, m+1, last);

	    System.out.print(morespace); 
	    printArray(x);
	    System.out.println(space + "return");
	} else {
	    // base case
	    System.out.println(space + 
			       "mergeSort(" + first + "," + last + "): return");
	}
    }

    // partition, version for debugging and tests
    public static intPair partitionSimple(int[] x, 
					  int first, int last, int pivot) {
	int i = first + 1;
	int j = last;
	do {
	    while(i < x.length && x[i] < pivot) {
		i++;
	    }
	    while(j >= 0 && x[j] > pivot) {
		j--;
	    }
	    if (i <= j) {
		swap(x, i, j);
		//System.out.print("swap(x," + i + "," + j + "): ");
		//printArray(x);
		i++;
		j--;
	    }
	} while (i <= j);
	int k = first + 1;
	while(k < x.length && x[k] < pivot) {
	    swap(x, k, k-1);
	    //System.out.print("bubbleswap(x," + k + "," + (k-1) + "): ");
	    //printArray(x);
	    k++;
	}
	intPair p = new intPair();
	p.first = j-1;
	p.second = i;
	return p;
    }

    // partition "in place"
    //   return a pair (a,b) of integers: x[first...a] and x[b...last]
    //   contain subarrays such that the first only contains < pivot,
    //   and the second >= pivot
    public static intPair partition(int[] x, int first, int last, int pivot) {
	int i = first;
	int j = last;
	do {
	    // repeat while i <= j
	    while(x[i] < pivot) {
		// get to first i s.t. x[i] >= pivot
		i++;
	    }
	    while(x[j] > pivot) {
		// get to last j s.t. x[j] <= pivot
		j--;
	    }
	    if (i <= j) {
		// they're out of place, swap them
		swap(x, i, j);

		// advance from the beginning
		i++;

		// retract from the end
		j--;
	    }
	} while (i <= j);
	intPair p = new intPair();
	p.first = j;
	p.second = i;
	return p;
    }

    // quicksort x[first...last]
    public static void quickSort(int[] x, int first, int last, String space) {
	if (first < last) {
	    // recursive case

	    // pivot is first element
	    int pivot = x[first];

	    // print stuff
	    System.out.println(space + "quickSort(" + first + "," + 
			       last + "), pivot " + pivot);
	    String morespace = space + "  ";
	    System.out.print(morespace + "partition: "); 
	    printArrayPivot(x, 0, x.length, pivot);
	    System.out.print("--> ");

	    // partition x: x[first..thePair.first] contains < pivot
	    //              x[thePair.second..last] contains >= pivot
	    intPair thePair = partition(x, first, last, pivot);
	    printArrayPivot(x, pivot);

	    // recursively sort the subarrays
	    quickSort(x, first, thePair.first, morespace);
	    quickSort(x, thePair.second, last, morespace);

	    System.out.println(space + "return");
	} else {
	    // base case
	    System.out.println(space + "quickSort(" + first + "," 
			       + last + "): return");
	}
    }

    public static void twoWaySort(int[] x) {
	// employ counter from beginning
	int low = 0;

	// and counter from end
	int high = x.length - 1;

	while(low <= high) {
	    // until beginning <= end counter

	    if (x[low] == 0) {
		// find the first 1 which is out of place, from beginning
		low++;

	    } else if (x[high] == 1) {
		// find the first 0 which is out of place, from end
		high--;

	    } else {
		// found out of place: swap
		swap(x, low, high);
		System.out.print("swap(x," + low + "," + high + "): ");
		printArray(x);

		// increase counters
                low++;
		high--;
	    }
	}
    }

    public static void main(String[] args) {
	int[] t = {5, 3, 6, 2, 1, 9, 4, 3};
	//int[] t = {5,4,3,2,1};

	System.out.print("original sequence: ");
	printArray(t);

	int[] x = (int[]) t.clone();
	System.out.print("sorting with mergeSort: ");
	printArray(x);
	mergeSort(x, 0, x.length-1, "");
	System.out.print("mergeSort-ed: ");
	printArray(x);

	x = (int[]) t.clone();
	System.out.print("sorting with quickSort: ");
	printArray(x);
	quickSort(x, 0, x.length-1, "");
	System.out.print("quickSort-ed: ");
	printArray(x);

	int[] b = {1, 0, 0, 1, 1, 0, 0, 0, 1, 1};
	System.out.print("original binary sequence: ");
	printArray(b);

	x = (int[]) b.clone();
	System.out.print("sorting with 2-way sorting: ");
	printArray(x);
	twoWaySort(x);
	System.out.print("2-way-sorted: ");
	printArray(x);
    }
}

