class Table {
	boolean forkInUse[];
	Monitor forkMon = new Monitor();
	Semaphore forkSem = new Semaphore(1);
	
	public Table(int seats) {
		forkInUse = new boolean[seats];
		for (int i=0; i<forkInUse.length; i++)
			forkInUse[i] = false;
	} //constructor

	private int left(int i)	{
		return i;
	} //left()

	private int right(int i) {
		if (i+1 < forkInUse.length){
			return (i+1);
		} //if
		else {
			return 0;
		} //else
	} //right()
	
	public void useFork(int seat) {
		forkMon.enter();
		while( forkInUse[left(seat)]  || forkInUse[right(seat)] ) {
			System.out.println("Philosopher #"+Thread.currentThread().getName()+" is waiting for forks");
			forkMon.wait(forkSem); //wait();
		} //while
			forkInUse[left(seat)] = true;
			forkInUse[right(seat)] = true;
		forkMon.leave();
	} //useFork()
	
	public void releaseFork(int seat) {
		forkMon.enter();
		forkInUse[left(seat)] = false;
		forkInUse[right(seat)] = false;
		forkMon.notify(forkSem);  //notifyAll();
		forkMon.leave();
	} //relaeaseFork()
} //class Table
	
class Philosopher implements Runnable {
	Table myTable;
	int seat;
	
	public Philosopher(Table table, int seat) {
		myTable = table;
		this.seat = seat;
	} //constructor
	
	public void run() {
		while(true) {
			think(seat);
			myTable.useFork(seat);
			eat(seat);
			myTable.releaseFork(seat);
		} //loop endlessly
	} //run()
		
	void think(int seat) {
		System.out.println("Philosopher #"+Thread.currentThread().getName()+" is thinking");
		try {
			Thread.sleep( (int) (Math.random() * 20000) );
		} //try
		catch (InterruptedException ie) {
			System.out.println("An InterruptedException caught\n"+ie.getMessage());
			ie.printStackTrace();
			System.exit(1);
		} //catch()
	} //think()

	void eat(int seat) {
		System.out.println("Philosopher #"+Thread.currentThread().getName()+" is eating");
		try {
			Thread.sleep( (int) (Math.random() * 20000) );
		} //try
		catch (InterruptedException ie) {
			System.out.println("An InterruptedException caught\n"+ie.getMessage());
			ie.printStackTrace();
			System.exit(1);
		} //catch()
		System.out.println("Philosopher #"+Thread.currentThread().getName()+" finished eating");
	} //eat()
} //class Philosopher

public class HungryPhilosophers3 {
	public static void main(String argv[]) {
		int hungryPhilosophers = Integer.parseInt(argv[0]);
		Table table = new Table( hungryPhilosophers );
		for (int i=0; i<hungryPhilosophers; i++) 	{
			new Thread(new Philosopher(table, i),""+(i+1)).start();
		} //for
	} //main()
} //class HungryPhilosophers