// Stephen Fulwider
//	A sample program to show working examples of Depth First Search (DFS) and Breadth First Search (BFS)

// Edited by Arup Guha to read in the BHCSI 2010 class graph.

import java.util.*;
import java.io.*;	

public class DFS_BFS_class
{

	public static void main(String[] args) throws Exception
	{
		new DFS_BFS_class(); // I like doing things this way so I don't have to use static objects
	}
	
	int N; // number of vertices in the graph
	boolean[][] G; // the graph as an adjacency matrix
					// G[i][j] is true if there is an edge from i to j
	
	TreeMap<String,Integer> people;
	ArrayList<String> namelist;
			
	DFS_BFS_class() throws Exception
	{
		setupGraph();
				
		System.out.println("------------------------------");
		System.out.println();
		
		// perform a DFS on the graph
		DFS();
		
		System.out.println();
		System.out.println("------------------------------");
		System.out.println();
		
		
		// perform a BFS on the graph
		BFS();
		
		System.out.println();
		System.out.println("------------------------------");
		System.out.println();
		System.out.println("All done - have a good day!");
	}
	
	// initial setup of the graph
	void setupGraph() throws Exception
	{
		Scanner fin = new Scanner(new File("classgraph.txt"));
		
		int numEdges = fin.nextInt();
		N = numEdges+1;
		
		// I am cheating. I know I can do this because our graph is a tree.
		G=new boolean[N][N];
		namelist = new ArrayList<String>();
		people = new TreeMap<String,Integer>();
		
		// read in edges
		for (int i=0; i<numEdges; i++) {
			String student1 = fin.next();
			String student2 = fin.next();
			String dummy = fin.next();
		
			// Get the index of the parent in our graph.
			Integer index1;
			if (people.containsKey(student1))
				index1 = (Integer)people.get(student1);
			
			// If it's not there, add it to our list of nodes.
			else {
				index1 = people.size();
				namelist.add(student1);
				System.out.println("Added "+student1+" as "+index1);
				people.put(student1, new Integer(index1));
			}			
			
			// Get the index of the parent in our graph.
			Integer index2;
			if (people.containsKey(student2))
				index2 = (Integer)people.get(student2);
			
			// If it's not there, add it to our list of nodes.
			else {
				index2 = people.size();
				namelist.add(student2);
				System.out.println("Added "+student2+" as "+index2);
				people.put(student2, new Integer(index2));
			}						
			
			G[index1][index2] = true;
			G[index2][index1] = true;
		}
		
		
	}
	
	// perform a DFS on the graph G
	void DFS()
	{
		boolean[] V=new boolean[N]; // a visited array to mark which vertices have been visited while doing the DFS
		
		int numComponets=0; // the number of components in the graph
		
		// do the DFS from each node not already visited
		for (int i=0; i<N; ++i)
			if (!V[i])
			{
				++numComponets;
				System.out.printf("Starting a DFS for component %d starting at node %s%n",numComponets,namelist.get(i));
				
				DFS(i,V);
			}
		
		System.out.println();
		System.out.printf("Finished with DFS - found %d components.%n", numComponets);
	}
	
	// perform a DFS starting at node at (works recursively)
	void DFS(int at, boolean[] V)
	{
		System.out.printf("At node %s in the DFS%n",namelist.get(at));
		
		// mark that we are visiting this node
		V[at]=true;
		
		// recursively visit every node connected to this that we have not already visited
		for (int i=0; i<N; ++i)
			if (G[at][i] && !V[i])
			{
				System.out.printf("Going to node %s...",namelist.get(i));
				DFS(i,V);
			}
		
		System.out.printf("Done processing node %s%n", namelist.get(at));
	}
	
	// perform a BFS on the graph G 
	void BFS()
	{
		boolean[] V=new boolean[N]; // a visited array to mark which vertices have been visited while doing the BFS
		
		int numComponets=0; // the number of components in the graph
		
		// do the BFS from each node not already visited
		for (int i=0; i<N; ++i)
			if (!V[i])
			{
				++numComponets;
				System.out.printf("Starting a BFS for component %d starting at node %s%n",numComponets,namelist.get(i));
				
				BFS(i,V);
			}
		
		System.out.println();
		System.out.printf("Finished with BFS - found %d components.%n", numComponets);
	}
	
	// perform a BFS starting at node start
	void BFS(int start, boolean[] V)
	{
		Queue<Integer> Q=new LinkedList<Integer>(); // A queue to process nodes
		
		// start with only the start node in the queue and mark it as visited
		Q.offer(start);
		V[start]=true;
				
		// continue searching the graph while still nodes in the queue
		while (!Q.isEmpty())
		{
			int at=Q.poll(); // get the head of the queue
			System.out.printf("At node %s in the BFS%n",namelist.get(at));
			
			// add any unseen nodes to the queue to process, then mark them as visited so they don't get re-added
			for (int i=0; i<N; ++i)
				if (G[at][i] && !V[i])
				{
					Q.offer(i);
					V[i]=true;
					
					System.out.printf("Adding node %s to the queue in the BFS%n", namelist.get(i));
				}
			
			System.out.printf("Done processing node %s%n", namelist.get(at));
		}
		
		System.out.printf("Finished with the BFS from start node %s%n", namelist.get(start));
	}

}
