import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.NoSuchElementException;
import java.util.Scanner;
/**
* Implementation for homework assignment 10.
*
* @author Cheng Yu
* @assignment ICS211 TA10
* @date Apr 14, 2010
* @bugs None
*/
public class ChengJade10 {
/**
* 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.
if (args.length != 1) {
System.err.println("Usage: java ChengJade10 <path>");
System.exit(1);
return;
}
// Create the priority queue of processes.
final PriorityQueue<Process> queue = new PriorityQueue<Process>();
long time = 0;
// Check for any errors while processing the file.
try {
// Open the file for reading, and read until the end of the file.
final Scanner scanner = new Scanner(new FileReader(args[0]));
while (scanner.hasNext()) {
// Get the next command from the file.
final String command = scanner.next();
// Check to schedule a command.
if (command.equalsIgnoreCase("schedule")) {
schedule(queue, time, scanner);
continue;
}
// Check to run a command.
if (command.equalsIgnoreCase("run")) {
time = run(queue, time, scanner);
continue;
}
// Otherwise, the file is illegal because the command is unknown.
System.out.flush();
try { java.lang.Thread.sleep(1000); } catch (Exception e2) { }
System.err.println("Unknown command in file: " + command);
System.err.println("Entries should start with 'schedule' or 'run'.");
System.exit(1);
}
} catch (final FileNotFoundException e) {
System.out.flush();
try { java.lang.Thread.sleep(1000); } catch (Exception e2) { }
System.err.println(e.getMessage());
System.exit(1);
} catch (final InputMismatchException e) {
System.out.flush();
try { java.lang.Thread.sleep(1000); } catch (Exception e2) { }
System.err.println(e.getMessage());
System.exit(1);
} catch (final NoSuchElementException e) {
System.out.flush();
try { java.lang.Thread.sleep(1000); } catch (Exception e2) { }
System.err.println("Unexpected end of file.");
System.exit(1);
}
System.exit(0);
}
/**
* Returns a positive long value from the scanner.
*
* @param scanner The scanner.
* @return The value.
* @throws InputMismatchException if the value is negative.
*/
private static long nextPositiveLong(final Scanner scanner) {
final long value;
try {
value = scanner.nextLong();
} catch (final InputMismatchException e) {
throw new InputMismatchException("Invalid integer value.");
}
if (value < 0)
throw new InputMismatchException("Unexpected negative value.");
return value;
}
/**
* Runs a command.
*
* @param queue The priority queue of processes.
* @param time The time before executing the command.
* @param scanner The scanner reading the command.
* @return The new time.
*/
private static long run(final PriorityQueue<Process> queue, final long time,
final Scanner scanner) {
assert null != queue;
assert null != scanner;
// Read the new time from the scanner. This method will execute until no
// processes remain in the queue or this time is reached.
final long newTime = nextPositiveLong(scanner);
if (newTime < time)
throw new InputMismatchException("Run time is in the past: " + newTime);
// Loop while there are processes to perform and there is remaining time.
long currentTime = time;
while (currentTime < newTime && !queue.isEmpty()) {
// Remove and display the next process from the queue.
final Process process = queue.poll();
final String activity = process.activity();
final long duration = process.duration();
final long due = process.due();
System.out.print(currentTime + ": performing " + activity);
System.out.print(" with deadline " + due);
System.out.println(" and duration " + duration + '\n');
// If the current process cannot be finished, calculate the remaining time
// and reinsert the process into the queue. Return zero to indicate there
// is no time left to execute more processes.
if (currentTime + duration > newTime) {
final long newDuration = duration - (newTime - currentTime);
schedule(queue, currentTime, new Process(activity, due, newDuration));
return newTime;
}
// Adjust the current time and execute the next process if there is one.
currentTime += duration;
System.out.print(currentTime + ": done performing " + activity);
System.out.println(currentTime <= due ? "\n" : " (late)\n");
}
// Return the new time.
return newTime;
}
/**
* Schedules a new process to run.
*
* @param queue The priority queue.
* @param time The current time.
* @param process The process.
*/
private static void schedule(final PriorityQueue<Process> queue,
final long time, final Process process) {
assert null != queue;
assert null != process;
queue.add(process);
System.out.print(time + ": adding " + process.activity());
System.out.print(" with deadline " + process.due());
System.out.println(" and duration " + process.duration() + '\n');
}
/**
* Schedules a new process to run. The process is read from a scanner.
*
* @param queue The priority queue.
* @param time The current time.
* @param scanner The scanner.
*/
private static void schedule(final PriorityQueue<Process> queue,
final long time, final Scanner scanner) {
assert null != queue;
assert null != scanner;
// Read the name, due time, and duration for the process.
final String activity = scanner.next();
final long due = nextPositiveLong(scanner);
final long duration = nextPositiveLong(scanner);
// Initialize a new process, and add it to the queue.
schedule(queue, time, new Process(activity, due, duration));
}
}
/**
* A generic minimization heap.
*/
class MinHeap<T extends Comparable<T>> {
/**
* The maximum size of the heap.
*/
public final static int MAX_SIZE = 1000;
private final ArrayList<T> heap = new ArrayList<T>();
/**
* Adds an item to the collection. This is an iterative method.
*
* @param value The value to add to the collection.
* @return True indicates the item was added, false otherwise.
*/
public boolean add(final T value) {
if (value == null)
throw new NullPointerException("value");
// Return false if the heap is full.
if (heap.size() == MAX_SIZE)
return false;
// Measure the time to add the item.
final long startTime = System.nanoTime();
// Add the item to the end of the heap.
heap.add(value);
// Get the index of the last item in the heap.
int index = heap.size() - 1;
// Count the number of levels traversed in the tree.
int levelsTraversed = 0;
// Loop from the bottom level to the top level.
while (index > 0) {
// Get the index of the parent item in the heap, and break out of the
// loop if the parent value is smaller than the current value.
final int parentIndex = (index - 1) / 2;
if (compare(parentIndex, index) < 0)
break;
// Swap this item and the parent, and then loop again one level higher.
swap(index, parentIndex);
index = parentIndex;
levelsTraversed++;
}
// Display information about the time to add the item.
final long timeConsumption = System.nanoTime() - startTime;
System.out.println("Levels traversed: " + levelsTraversed);
System.out.println("Time consumed (ns): " + timeConsumption);
// Return true to indicate the item was added successfully.
return true;
}
/**
* Compares the heap value at the specified indexes. This is used to make the
* code more readable.
*
* @param left The left index.
* @param right The right index.
* @return The comparison result.
*/
private int compare(final int left, final int right) {
assert left < heap.size();
assert right < heap.size();
return heap.get(left).compareTo(heap.get(right));
}
/**
* Returns true if the collection is empty.
*
* @return True if the collection is empty, false otherwise.
*/
public boolean isEmpty() {
return heap.isEmpty();
}
/**
* This method recursively reorders the heap.
*
* @param index The index of the item being ordered.
* @return The number of levels traversed.
*/
private int reheapify(final int index) {
assert index < heap.size();
// Get the index of this value's left and right child in the heap tree.
final int leftIndex = index * 2 + 1;
final int rightIndex = index * 2 + 2;
// Return immediately if there are no children for this value.
if (leftIndex >= heap.size())
return 0;
// Check if there is no right child or if the left child has a smaller value
// than the right child.
if (heap.size() == rightIndex || compare(leftIndex, rightIndex) < 0) {
// If the left child is smaller than this value, swap this value and the
// child, and continue recursively.
if (compare(leftIndex, index) < 0) {
swap(leftIndex, index);
return 1 + reheapify(leftIndex);
}
// If the right child is smaller than this value, swap this value and the
// child, and continue recursively.
} else if (compare(rightIndex, index) < 0) {
swap(rightIndex, index);
return 1 + reheapify(rightIndex);
}
// Arriving here means the children are smaller than the current value, so
// stop the recursion.
return 0;
}
/**
* Removes an item from the collection. This method is implemented
* recursively.
*
* @return The item at the top of the heap.
* @throws NoSuchElementException if the heap is empty.
*/
public T remove() {
// Throw an exception if the heap is empty.
if (heap.isEmpty())
throw new NoSuchElementException("The heap is empty.");
// Measure the time to remove the item.
final long startTime = System.nanoTime();
// Get the value at the top of the heap as the result.
final T result = heap.get(0);
// Move the value from the last index of the heap to the top of the heap.
final int lastIndex = heap.size() - 1;
heap.set(0, heap.get(lastIndex));
// Remove the last value in the collection because it is no longer used.
heap.remove(lastIndex);
// Recursively order the heap if there are items in the heap.
final int levelsTraversed = heap.isEmpty() ? 0 : reheapify(0);
// Display information about the time to add the item.
final long timeConsumption = System.nanoTime() - startTime;
System.out.println("Levels traversed: " + levelsTraversed);
System.out.println("Time consumed (ns): " + timeConsumption);
// Return the item that was originally at the top of the heap
return result;
}
/**
* Swaps two values in the heap.
*
* @param index1 The first index.
* @param index2 The second index.
*/
private void swap(final int index1, final int index2) {
assert index1 < heap.size();
assert index2 < heap.size();
final T temp = heap.get(index1);
heap.set(index1, heap.get(index2));
heap.set(index2, temp);
}
}
/**
* A generic priority queue implemented with a heap.
*/
class PriorityQueue<T extends Comparable<T>> {
/** The queue that contains the generic values. */
private final MinHeap<T> myQueue = new MinHeap<T>();
/**
* Adds an item to the collection.
*
* @param value The item to add to the collection.
* @return True if the item is added.
* @throws NullPointerException if the value is null.
*/
public boolean add(final T value) {
if (value == null)
throw new NullPointerException("value");
return myQueue.add(value);
}
/**
* Returns true if the collection is empty.
*
* @return True if the collection is empty, otherwise false.
*/
public boolean isEmpty() {
return myQueue.isEmpty();
}
/**
* Returns the next item from the queue. This method returns null if there are
* no more items in the queue.
*
* @return The next item from the queue or null if there are no more items.
*/
public T poll() {
if (myQueue.isEmpty())
return null;
return myQueue.remove();
}
/**
* Returns the string representation of the class.
*
* @return The string representation of the class.
*/
@Override
public String toString() {
return myQueue.toString();
}
}
/**
* A process class. This class contains an activity string, a due time, and a
* duration.
*/
class Process implements Comparable<Process> {
/** The name of the activity. */
private final String myActivity;
/** The due time for the process. */
private final long myDue;
/** The duration of the process. */
private final long myDuration;
/**
* Initializes a new instance of the Process class.
*
* @param activity The activity.
* @param due The due time.
* @param duration The duration.
*/
public Process(final String activity, final long due, final long duration) {
if (activity == null)
throw new NullPointerException("activity");
if (duration < 0)
throw new IllegalArgumentException("duration");
myActivity = activity;
myDue = due;
myDuration = duration;
}
/**
* Returns the activity.
*
* @return The activity.
*/
public String activity() {
return myActivity;
}
/**
* Compares this instance to another instance.
*
* @param process The other process.
*
* @return The comparison value.
*/
public int compareTo(final Process process) {
if (process == null)
return 1;
final long due = process.due();
return myDue > due ? 1 : myDue < due ? -1 : 0;
}
/**
* Returns the due time.
*
* @return The due time.
*/
public long due() {
return myDue;
}
/**
* Returns the duration.
*
* @return The duration.
*/
public long duration() {
return myDuration;
}
}