/*
  Name: bfs.java
  Purpose: implementation of a breadth-first search
  Author: Leo Liberti
  Source: Java
  History: 28/8/11  work started
*/

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

class circularQueue {

    // the queue
    int[] q;

    // length of queue array
    int n;

    // index of head element
    int h;

    // index of tail element
    int t;

    // queue constructor
    public circularQueue(int qlen) {
	n = qlen;
	q = new int[n];
	h = 0;
	t = 0;
    }

    public boolean isEmpty() {
	if (h == t) {
	    return true;
	} else {
	    return false;
	}
    }

    public int first() {
	assert(!isEmpty()): "error: queue is empty, cannot read first element";
	return q[h];
    }

    public int popFront() {
	int p = this.first();
	h = (h + 1) % (n + 1);
	return p;
    }

    public int size() {
	int theSize = (t - h + n + 1) % (n + 1);
	return theSize;
    }

    public void pushBack(int d) {
	assert(this.size() < n) : 
	"error: queue is full: cannot push elements on its back";
	q[t] = d;
	t = (t+1) % (n+1);  
    }

}

class intPair implements Comparable {

    // essential elements of intPair (an ordered pair of integers)
    public int first;
    public int second;

    // the rest of the implementation makes this object a "Comparable" one,
    // which means to say that it can implement ordered set classes, such
    // as TreeSet
    public intPair() {  }
    public intPair(int a, int b) {
	first = a;
	second = b;
    }
    // this function compares this to o and returns -1,0,1 according to
    // this < o, this == o, this > o
    public int compareTo(Object o) throws ClassCastException {
	if (!(o instanceof intPair)) {
	    throw new ClassCastException("expecting an intPair object");
	}
	intPair p = (intPair) o;
	if (this.first < p.first || 
	    (this.first == p.first && this.second < p.second)) {
	    return -1;
	} else if (this.first == p.first && this.second == p.second) {
	    return 0;
	} else {
	    return 1;
	}
    }
}

public class bfs {

    // this is the set of elements V
    Set<Integer> V;

    // the queue
    circularQueue Q;
 
    // A stores the relation 
    Map<intPair, Boolean> A;

    // alpha maps elements to their distance from s
    Map<Integer,Integer> alpha;

    // source element
    int s;

    // target element
    int t;

    // size of V
    int n;

    // class constructor
    public bfs() {
	alpha = new HashMap<Integer, Integer>();
	V = new TreeSet<Integer>();
    }

    // initialize the queue
    public void initializeQueue() {
	n = V.size();
	Q = new circularQueue(n);
    }	
    
    // the BFS algorithm implementation: as in the slides; but
    // we don't actually need the set L, since we use alpha to 
    // determine whether we've already put a node in the queue or not
    public int breadthFirstSearch() {
	int c = 0;
	Q.pushBack(s);
	int u;
	int v;
	// initialize alpha to n+1
	Iterator<Integer> VIt = V.iterator();
	while(VIt.hasNext()) {
	    alpha.put(VIt.next(), n + 1);
	}
	// set alpha(s)=0
	VIt = V.iterator();
	alpha.put(VIt.next(),0);

	intPair p = new intPair();
	// outer loop
	while(!Q.isEmpty()) {

	    // get first element of the queue
	    u = Q.popFront();
	    System.out.println(u + " out of Q");
	    // let c = alpha(u) + 1
	    c = alpha.get(u) + 1;
	    p.first = u;
	    VIt = V.iterator();

	    // inner loop
	    while(VIt.hasNext()) {
		v = (int) VIt.next();
		p.second = v;

		// test whether we have to process v
		if (A.containsKey(p) && alpha.get(v) == n + 1) {

		    // let alpha(v) = c
		    alpha.put(v, c);
		    System.out.print("  alpha(" + v + ") = " + c);
		    if (v == t) {
			// target found 
			// (replace with test (v/100 == t) for fastest paths,
			//  since in that case we only know t to be a place,
			//  and we store places in the first digit of the
			//  element name)
			System.out.println("  reached " + t + " with c = "+c);
			return c;
		    } else {
			// v is not the target
			Q.pushBack(v);
			System.out.println(", " + v + " pushed on Q");
		    }
		}
	    }
	}
	return 0;
    }

    public static void main(String[] args)  {

	if (args.length < 3) {
	    System.err.println("bfs: syntax error: syntax is 'java bfs graph_file source target'");
	    System.exit(1);
	}
	
	// dealing with dynamic objects, create a dynamic bfs object first
	bfs theBFS =  new bfs();
	
	// read contain relation elements from file 
	// (first elements in even places, second in odd)
	LinkedList<Integer> relation = new LinkedList<Integer>();

	// read from text file
	try {
	    Scanner sc = 
		new Scanner(new BufferedReader(new FileReader(args[0])));

	    // continue until the file has content
	    int theItem;
	    while(sc.hasNext()) {
		
		// if next string is an integer,
		if(sc.hasNextInt()) {
		    
		    // read it in,
		    theItem = sc.nextInt();
		    
		    // add it to the relation list
		    relation.add(theItem);
		    
		    // and to the set V
		    theBFS.V.add(theItem);

		} else {
		    
		    // otherwise, simply skip to the next string
		    sc.next();
		}
	    }
	} catch (IOException e) {
	    System.err.println("bfs: file " + args[0] + " not found");
	    System.exit(1);
	}

	// read s and t from command line
	theBFS.s = Integer.parseInt(args[1]);
	theBFS.t = Integer.parseInt(args[2]);

	// since we store all relation elements successively, 
	// we need to divide by 2 to obtain size of relation
	int Rsize = relation.size() / 2;

	// initialize the relation storage
	theBFS.A = new TreeMap<intPair,Boolean>();

	// initialize the queue (can be called after V is filled)
	theBFS.initializeQueue();
	
	// sort out first (even) and second (odd) elements 
	// from relation and into A
	Iterator<Integer> relationIt = relation.iterator();
	int relationCounter = 0;
	intPair thePair = new intPair();
	while(relationIt.hasNext()) {
	    if (relationCounter % 2 == 0) {
		thePair.first = relationIt.next();
	    } else {
		thePair.second = relationIt.next();
		intPair p = new intPair(thePair.first, thePair.second);
		theBFS.A.put(p, true);
	    }
	    relationCounter++;
	}

	/* DEBUG
	for(Iterator<Integer> i = relation.iterator(); i.hasNext(); ) {
	    System.out.println("relation: " + i.next());
	}
	for(Iterator<intPair> i = theBFS.A.keySet().iterator(); i.hasNext(); ){
	    thePair = i.next();
	    System.out.println("A: " + thePair.first + ", " + thePair.second);
	}
	*/

	// call breadth-first search
	int itineraryCost = theBFS.breadthFirstSearch();

	// parse answer
	if (itineraryCost == 0) {
	    System.out.println("target element " + 
			       theBFS.t + " unreachable from " + theBFS.s);
	} else {
	    System.out.println("itinerary cost = " + itineraryCost);
	}
    }
    
}

