import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.nio.BufferOverflowException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.NoSuchElementException;
import java.util.Scanner;
/**
* This is an implementation of ICS 311 Project 1.
*
* @author Jade Cheng
* @assignment ICS 311 Project 1
* @date Jan 30, 2009
* @bugs None
*/
public class JadeChengP1 {
/**
* A class thrown by the main method when it detects an error.
*/
@SuppressWarnings("serial")
private static class ProgramException extends RuntimeException {
/**
* Initializes a new instance of the MyException class.
*
* @param message The message.
*/
public ProgramException(final String message) {
super(message);
}
}
/**
* The main entry point of the program.
*
* @param args The command line arguments.
*/
public static void main(final String[] args) {
// Check the command line arguments for usage errors.
if (args.length != 3) {
System.out.println(
"Usage: java JadeChengP1 filePath sortMethod iterations\n"
+ "\n"
+ " filePath The path to a text file containing integers\n"
+ " sortMethod The algorithm used to sort the numbers\n"
+ " iterations The number of times to sort the values\n"
+ "\n"
+ "The 'sortMethod' must be one of the following values:\n"
+ "\n"
+ " 'bubble' --> uses Bubble Sort\n"
+ " 'insertion' --> uses Insertion Sort\n"
+ " 'merge' --> uses Merge Sort\n"
+ " 'selection' --> uses Selection Sort\n"
+ " 'heap' --> uses Heap Sort\n"
+ "\n"
+ "When the program executes, it loops 'iterations' number of times.\n"
+ "For each iteration i, the program sorts i number of values from\n"
+ "the specified file. After each iteration, the program writes to\n"
+ "standard output the number of items sorted and the number of\n"
+ "operations performed by the specified sorting method. The output\n"
+ "is tab-delimited.\n"
+ "\n"
+ "When the program terminates, it saves the sorted output to the\n"
+ "text file 'sorted.txt'. Each integer is placed on a separate\n"
+ "line.");
return;
}
final String filePath = args[0];
final String sortMethod = args[1];
final String iterationsText = args[2];
try {
// Read the number of iterations and check for errors.
final int iterations;
try {
iterations = Integer.parseInt(iterationsText);
if (iterations <= 0)
throw new ProgramException("'iterations' must be a positive number.");
} catch (final NumberFormatException e) {
throw new ProgramException("'iterations' must be a positive number.");
}
// Determine the method used to sort the numbers and check for errors.
final Sorter<Integer> sorter;
if (0 == sortMethod.compareToIgnoreCase("bubble"))
sorter = new BubbleSort<Integer>();
else if (0 == sortMethod.compareToIgnoreCase("insertion"))
sorter = new InsertionSort<Integer>();
else if (0 == sortMethod.compareToIgnoreCase("merge"))
sorter = new MergeSort<Integer>();
else if (0 == sortMethod.compareToIgnoreCase("selection"))
sorter = new SelectionSort<Integer>();
else if (0 == sortMethod.compareToIgnoreCase("heap"))
sorter = new HeapSort<Integer>();
else
throw new ProgramException("'sortMethod' is invalid.");
// Open the file for reading and check for errors.
final Scanner scanner;
try {
final File file = new File(filePath);
scanner = new Scanner(file);
} catch (final FileNotFoundException e) {
throw new ProgramException("'" + filePath + "' is not found.");
}
// Read the maximum number of values from the file.
final Integer[] unsorted = new Integer[iterations];
for (int i = 0; i < iterations; i++) {
try {
unsorted[i] = Integer.valueOf(scanner.nextInt());
} catch (final InputMismatchException e) {
throw new ProgramException("Non-integer value detected in file.");
} catch (final NoSuchElementException e) {
throw new ProgramException("Not enough integers present in file.");
}
}
// Loop the number of iterations specified on the command line. For each
// iteration, copy the unsorted values, sort them, and display the
// results. Also, as a sanity check, ensure the array is sorted after
// each iteration.
for (int i = 1; i <= iterations; i++) {
final Integer[] copy = Arrays.copyOfRange(unsorted, 0, i);
System.out.println(i + "\t" + sorter.sort(copy));
verifySortedArray(copy);
}
// Finally, save the sorted output to the file 'sorted.txt'.
try {
final PrintStream stream = new PrintStream("sorted.txt");
sorter.sort(unsorted);
for (final Integer i : unsorted)
stream.println(i);
} catch (final FileNotFoundException e) {
throw new ProgramException("Cannot write to file 'sorted.txt'");
}
} catch (final ProgramException e) {
// Display errors to standard error and return a failure status.
System.err.println(e.getMessage());
System.exit(1);
}
}
/**
* Ensures an array of integers is sorted.
*
* @param array The array of integers.
*/
private static void verifySortedArray(final Integer[] array) {
for (int i = 1; i < array.length; i++)
if (array[i].compareTo(array[i - 1]) < 0)
throw new ProgramException("Sorted algorithm failed.");
}
}
/**
* This generic class performs a bubble sort.
*/
class BubbleSort<T extends Comparable<T>> implements Sorter<T> {
/**
* Sorts an array using bubble sort.
*
* @param array The array.
*
* @return The number of operations performed.
*/
public int sort(final T[] array) {
if (array == null)
throw new NullPointerException("array");
int operations = 0;
boolean isSwapped;
do {
isSwapped = false;
for (int i = 0; i < array.length - 1; i++) {
operations++;
if (array[i].compareTo(array[i + 1]) > 0) {
this.swap(array, i, i + 1);
isSwapped = true;
}
}
} while (isSwapped);
return operations;
}
/**
* This method swaps two values in an array.
*
* @param array The array.
* @param n The first index.
* @param m The second index.
*/
private void swap(final T[] array, final int n, final int m) {
assert n >= 0;
assert m >= 0;
assert n < array.length;
assert m < array.length;
final T temp = array[n];
array[n] = array[m];
array[m] = temp;
}
}
/**
* This class implements a generic heap. The maximum size of the heap is
* MAX_ELEMENTS.
*/
class Heap<T extends Comparable<T>> {
/** The maximum number of elements that can exist in the heap. */
public static final int MAX_ELEMENTS = 10000;
/** An array that implements the heap buffer. */
private final ArrayList<T> myArray = new ArrayList<T>(Heap.MAX_ELEMENTS);
/** The number of elements used in the array. */
private int mySize = 0;
/**
* Initializes a new instance of the Heap class.
*/
public Heap() {
// Initialize the array of elements all to null.
for (int i = 0; i < Heap.MAX_ELEMENTS; i++)
this.myArray.add(null);
}
/**
* This method adds an item to the heap. This method does not allow null
* values to be added to the collection, but it does allow duplicates.
*
* @param o element whose presence in this collection is to be ensured.
*
* @return The number of operations performed.
*
* @throws BufferOverflowException Thrown if the item cannot be added.
*/
public int add(final T o) throws BufferOverflowException {
if (o == null)
throw new NullPointerException();
int operations = 0;
if (this.mySize == Heap.MAX_ELEMENTS)
throw new BufferOverflowException();
this.set(this.mySize++, o);
int n = this.mySize - 1;
while (n > 0) {
operations++;
final int nParent = (n - 1) / 2;
if (o.compareTo(this.get(nParent)) >= 0)
break;
this.swap(n, nParent);
n = nParent;
}
return operations;
}
/**
* Compares to elements at the specified index values.
*
* @param left The index of the left value.
* @param right The index of the right value.
*
* @return The result of the compareTo method.
*/
private int compare(final int left, final int right) {
assert left >= 0;
assert left < this.mySize;
assert right >= 0;
assert right < this.mySize;
assert this.mySize <= this.myArray.size();
return this.get(left).compareTo(this.get(right));
}
/**
* This method returns an element at the specified index.
*
* @param index The index.
* @return The element at the specified index.
*/
private T get(final int index) {
assert index >= 0;
assert index < this.mySize;
assert this.mySize <= this.myArray.size();
return this.myArray.get(index);
}
/**
* Retrieves and removes the smallest value in the heap. This method differs
* from the poll method in that it throws an exception if this heap is empty.
*
* @return The smallest value in the heap and the number of operations
* performed.
*
* @throws NoSuchElementException if this queue is empty.
*/
public Pair<T> remove() throws NoSuchElementException {
if (this.mySize == 0)
throw new NoSuchElementException();
int operations = 0;
final T result = this.get(0);
this.set(0, this.get(this.mySize - 1));
this.set(this.mySize - 1, null);
this.mySize--;
int n = 0;
while (true) {
final int nLeft = n * 2 + 1;
final int nRight = n * 2 + 2;
operations++;
if (nLeft >= this.mySize)
break;
if (nRight >= this.mySize || this.compare(nLeft, nRight) < 0) {
if (this.compare(nLeft, n) >= 0)
break;
this.swap(nLeft, n);
n = nLeft;
continue;
}
if (this.compare(nRight, n) >= 0)
break;
this.swap(nRight, n);
n = nRight;
}
return new Pair<T>(result, operations);
}
/**
* This method sets a value in the heap array.
*
* @param index The index of the value to set.
* @param value The value to set.
*/
private void set(final int index, final T value) {
assert index >= 0;
assert index < this.mySize;
assert this.mySize <= this.myArray.size();
this.myArray.set(index, value);
}
/**
* This method swaps two values.
*
* @param n The first index.
* @param m The second index.
*/
private void swap(final int n, final int m) {
assert n >= 0;
assert m >= 0;
assert n < this.mySize;
assert m < this.mySize;
assert this.mySize <= this.myArray.size();
final T temp = this.get(n);
this.set(n, this.get(m));
this.set(m, temp);
}
}
/**
* This class performs a heap sort.
*/
class HeapSort<T extends Comparable<T>> implements Sorter<T> {
/**
* Sorts an array using heap sort.
*
* @param array The array.
*
* @return The number of operations performed.
*/
public int sort(final T[] array) {
if (array == null)
throw new NullPointerException("array");
int operations = 0;
final Heap<T> heap = new Heap<T>();
for (final T element : array)
operations += heap.add(element);
for (int j = 0; j < array.length; j++) {
final Pair<T> pair = heap.remove();
array[j] = pair.value;
operations += pair.operations;
}
return operations;
}
}
/**
* This generic class performs an insertion sort.
*/
class InsertionSort<T extends Comparable<T>> implements Sorter<T> {
/**
* Sorts an array using insertion sort.
*
* @param array The array.
*
* @return The number of operations performed.
*/
public int sort(final T[] array) {
if (array == null)
throw new NullPointerException("array");
int operations = 0;
for (int i = 0; i < array.length; i++)
for (int j = 0; j < i; j++) {
operations++;
if (array[j].compareTo(array[i]) > 0) {
final T temp = array[i];
for (int k = i; k > j; k--)
array[k] = array[k - 1];
array[j] = temp;
}
}
return operations;
}
}
/**
* This class performs a merge sort.
*/
class MergeSort<T extends Comparable<T>> implements Sorter<T> {
/** A temporary array used while merging the sorted elements. */
private T[] myBuffer;
/**
* This method merges two ranges of values in the array, each sorted. The
* largest index of the first range is smaller than the smallest index of the
* second range.
*
* @param array The array.
* @param start The first index (inclusive) for the first range.
* @param mid The first index (inclusive) for the second range.
* @param end The last index (exclusive) for the second range.
*
* @return The number of operations performed.
*/
private int merge(final T[] array, final int start, final int mid,
final int end) {
assert array != null;
assert start >= 0;
assert start < mid;
assert mid < end;
assert end <= array.length;
int operations = 0;
int i = 0;
int i1 = start;
int i2 = mid;
while (i1 < mid && i2 < end) {
operations++;
if (array[i1].compareTo(array[i2]) < 0)
this.myBuffer[i++] = array[i1++];
else
this.myBuffer[i++] = array[i2++];
}
if (i1 < mid) {
operations += mid - i1;
System.arraycopy(array, i1, this.myBuffer, i, mid - i1);
}
if (i2 < end) {
operations += end - i2;
System.arraycopy(array, i2, this.myBuffer, i, end - i2);
}
operations += end - start;
System.arraycopy(this.myBuffer, 0, array, start, end - start);
return operations;
}
/**
* This method performs the merge sort recursively.
*
* @param array The array.
* @param start The start index (inclusive).
* @param end The end index (exclusive).
*
* @return The number of operations performed.
*/
private int mergeSort(final T[] array, final int start, final int end) {
assert array != null;
assert start >= 0;
assert start < end;
assert end <= array.length;
if (start + 1 == end)
return 1;
int operations = 0;
final int mid = (start + end) / 2;
operations += this.mergeSort(array, start, mid);
operations += this.mergeSort(array, mid, end);
operations += this.merge(array, start, mid, end);
return operations;
}
/**
* Sorts an array using merge sort.
*
* @param array The array.
*
* @return The number of operations performed.
*/
public int sort(final T[] array) {
if (array == null)
throw new NullPointerException("array");
// Handle a special case: when the array has no items in it.
if (array.length == 0)
return 0;
// Make a copy of the array. It's used as a temporary array while merging
// the sorted elements.
this.myBuffer = array.clone();
try {
return this.mergeSort(array, 0, array.length);
} finally {
this.myBuffer = null;
}
}
}
/**
* A generic class that contains a generic value and a recorded number of
* operations.
*/
class Pair<T> {
/** The number of operations performed. */
public final int operations;
/** A value. */
public final T value;
/**
* Initializes a new instance of the Pair class.
*
* @param value A value.
* @param operations The number of operations performed.
*/
public Pair(final T value, final int operations) {
this.value = value;
this.operations = operations;
}
}
/**
* This class performs a selection sort.
*/
class SelectionSort<T extends Comparable<T>> implements Sorter<T> {
/**
* Sorts an array using selection sort.
*
* @param array The array.
*
* @return The number of operations performed.
*/
public int sort(final T[] array) {
if (array == null)
throw new NullPointerException("array");
int operations = 0;
for (int i = 0; i < array.length; i++) {
T smallest = array[i];
int smallestIndex = i;
for (int j = i; j < array.length; j++) {
operations++;
if (array[j].compareTo(smallest) < 0) {
smallest = array[j];
smallestIndex = j;
}
}
for (int k = smallestIndex; k > i; k--) {
operations++;
array[k] = array[k - 1];
}
array[i] = smallest;
}
return operations;
}
}
/**
* The interface to an object that sorts an array of values.
*/
interface Sorter<T extends Comparable<T>> {
/**
* Sorts an array.
*
* @param array The array to sort.
*
* @return The number of operations performed.
*/
int sort(final T[] array);
}