import java.util.Scanner;
import java.util.Random;

/**
  * Graph objects can be used to work with undirected graphs.
  * <br>Graphs are internally represented using adjacency matrices.
  * <br>This class provides facilities to record visits of vertices and edges.
  * <BR>ONLY MODIFY METHODS DFSVISIT AND ISCONNECTED IN THIS CLASS
  * @author Sophie Quigley
  * 
  */
public class Graph {
    
    /**
     * Used to generate edges randomly
     */
    static Random rand = new Random(999);   

    //-----------------------------------------------------------------------
    // The following instance variables are private and immutable
    
    /**
     * Total number of vertices in graph
     */
    private int totalV = 0;
          
    /**
     * Total number of edges in graph
     */
    private int totalE = 0;

    /**
     * Adjacency matrix of graph.
     * <br>edges[x][y] is the number of edges from vertex x to vertex y.
     */
    private int[][] edges = null;
       
    /**
     * Degree of each vertex.
     */
    private int[] degrees = null;
    
    //-----------------------------------------------------------------------
    // The following instance variables are public and can be used directly
    // by external graph visitors, or indirectly using methods provided.

    /**
     * Used by graph visitors to keep track of visited vertices.
     */
    public boolean[] visitedV = null;
    
    /**
     * Used by graph visitors to keep track of the degree of each vertex
     * if unvisited edges are counted.
     */
    public int[] unvisitedVDegree = null;
    
    /**
     * Used by graph visitors to keep track of number of visited edges
     * as an alternative to using unvisitedE.
     */
    public int[][] visitedE = null;
    
    /**
     * Used by graph visitors to keep track of number of unvisited edges
     * as an alternative to using visitedE.
     */
    public int[][] unvisitedE = null;
 
    /**
     * 
     * Creates a new undirected Graph whose content will be read from the Scanner.
     * <br>Input format consists of non-negative integers separated by white space as follows:
     * <ul>
     * <li>First non-negative integer specifies the number of vertices n
     * <li>Next nxn integers specify the edges, listed in adjacency matrix order
     * </ul>
     * @throws InstantiationException if incorrect data is read
     * @param in Scanner used to read graph description
     */
    public Graph(Scanner in) throws InstantiationException {

        // Read number of totalV and handle empty graph
        totalV = in.nextInt();
       
        if (totalV < 0) {
            throw new InstantiationException("Number of vertices must be positive");
        }
        if (totalV == 0) return;
        
        // Read adjacency matrix        
        // If mistakes are found in edges, the entire matrix is read
        // before the exception is thrown,
        int i, j;
        boolean inputMistake = false;
        edges = new int[totalV][totalV];
        for (i=0; i<totalV; i++) {
            for (j=0; j<totalV; j++) {
                edges[i][j]=in.nextInt();
                if (edges[i][j] <0) {
                    inputMistake = true;            
                }
            }
        }
        if (inputMistake) throw new InstantiationException("Number of edges cannot be negative"); 
        
        // Verify that adjacency matrix is symmetric
        for (i=0; i<totalV; i++)
            for (j=i+1; j<totalV; j++)
                if (edges[i][j] != edges[j][i]) {
                    throw new InstantiationException("Adjacency matrix is not symmetric");
                }
                
        // Initialize remainder of new Graph fields
        initializeGraphFields();
    }
    
    /**
     * Creates a randomly generated Graph according to specifications.
     * @param vertices Number of vertices in graph - defaults to 0
     * @param maxParallelEdges Maximum number of parallel edges between any two vertices - defaults to 0
     */
     public Graph( int vertices, int maxParallelEdges ) {

        // set totalV and handle empty graph
        totalV = (vertices < 0) ? 0 : vertices;
        if (totalV == 0) return;

        // Add edges to graph randomly
        edges = new int[totalV][totalV];
        if (maxParallelEdges >0)  {
            int randmax = maxParallelEdges+1;
            for (int i=0; i<totalV; i++)
                for (int j=i; j<totalV; j++) {
                    edges[i][j] = rand.nextInt(randmax); 
                    edges[j][i] = edges[i][j];
            }
        }
                
        // Initialize remainder of new Graph fields
        initializeGraphFields();
 }
    
    /**
     * Once vertices and edges are known, initialize remainder of Graph fields.
     */
    private void initializeGraphFields () {

        // Record degrees of vertices in degrees array and count total edges
        degrees = new int[totalV];
        for (int i=0; i<totalV; i++) {
            degrees[i] = 0;
            for (int j=0; j<totalV; j++) {
                degrees[i] += edges[i][j];
            }
            //  loops count twice
            degrees[i] += edges[i][i];
            totalE += degrees[i];                
        }
        // Number of edges = graph degree/2
        totalE = totalE / 2;
        
        // Prepare visitation status arrays
        unvisitedVDegree = new int[totalV];
        visitedV = new boolean[totalV];
        visitedE = new int[totalV][totalV];
        unvisitedE = new int[totalV][totalV];   
        resetVisitation();
        }
    
   /**
    * Returns a String representation of the graph:
    * a 2-D representation of the adjacency matrix of that graph.
    * @return The 2-D representation of the adjacency matrix of that graph.
    * 
    */    
    @Override
    public String toString() {
        return matrixtoString(edges);
    }

    /**
     * Returns a String representation of 2 dimensional matrix
 of size totalV x totalV.  
     * This can be used to visualize edges, visitedE, and unvisitedE
     * @param matrix matrix to be represented
     * @return 2D string representation of matrix
     */
    private String matrixtoString(int[][] matrix) {
        String result = "";
        for (int i=0; i<totalV; i++) {
            for (int j=0; j<totalV; j++) {
                result += matrix[i][j];
                result += " ";
            }
            result += "\n";
        }
        return result;
         
    }

    /**
     * Verifies whether graph is empty (with no vertices).
     * @return True iff graph is empty
     */
    public boolean isEmpty() {
        return (totalV == 0);        
    }
    
    
    /**
    * Gets the number of vertices in the graph.
    * @return The number of vertices in the graph
    *
    */  
    public int getTotalVertices() {
        return totalV;
    }
    
    /**
    * Gets the degree of a specific vertex.
    * @param vertex the vertex whose degree is sought
    * @return The degree of vertex
    *
    */  
    public int getDegree(int vertex) {
        return degrees[vertex];
    }   
    
    /**
    * Gets the number of edges in the graph.
    * @return The number of edges in the graph
    *
    */  
    public int getTotalEdges() {
        return totalE;
    }   
    
    /**
     * Gets the number of edges from sourceV to destV.
     * @param sourceV The source vertex
     * @param destV The destination vertex
     * @return The number of edges from sourceV to destV
     */
    public int getEdgeCount(int sourceV, int destV) {
        if (sourceV>=0 && sourceV<totalV && destV>=0 && destV<totalV)
            return edges[sourceV][destV];
        else
            return 0;
    }  
    
    /**
     * Gets the adjacency matrix of the graph.
     * @return The adjacency matrix of the graph 
     */
    public int[][] getAllEdges() {
        return edges;
    }
        
    /**
     * Resets unvisitedVDegree, visitedV, visitedE, and unvisitedE matrices for a new visitation.
     */
    public void resetVisitation() {
        for (int i=0; i<totalV; i++) {
            unvisitedVDegree[i] = degrees[i];
            visitedV[i] = false;
            for (int j=0; j<totalV; j++) {
                visitedE[i][j] = 0;
                unvisitedE[i][j] = edges[i][j];
            }
        }
    }

    /**
    * Check whether vertex has been visited.
    * @param vertex vertex whose visited status is checked
    * @return True iff vertex has been visited
    */
    public boolean isVisitedV(int vertex) {
        return (visitedV[vertex] == true);
    }      

    /**
    * Visit vertex.
    * <br>Side-effect: visitedV is modified.
    * @param vertex vertex being visited
    */
    public void visitV(int vertex) {
        if (vertex<0 || vertex>=totalV)  return;
        visitedV[vertex] = true;
    }      

    /**
    * Unvisit vertex.
    * <br>Side-effect: visitedV is modified.
    * @param vertex vertex being unvisited
    */
    public void unvisitV(int vertex) {
        if (vertex<0 || vertex>=totalV)  return;
        visitedV[vertex] = false;
    }  

// START OF CODE ADDED ON MARCH 4
    /**
    * Return number of visited edges between v1 and v2
    * @param v1 Vertex in graph
    * @param v2 Vertex in graph
    * @return number of visited edges between v1 and v2
    */
    public int getVisitedE(int v1, int v2) {
        return (visitedE[v1][v2]);
    }      
    /**
    * Return number of unvisited edges between v1 and v2
    * @param v1 Vertex in graph
    * @param v2 Vertex in graph
    * @return number of visited edges between v1 and v2
    */
    public int getUnvisitedE(int v1, int v2) {
        return (unvisitedE[v1][v2]);
    }      

// END OF CODE ADDED ON MARCH 4

    /**
    * Visit edge between two vertices.
    * <br>Side-effect: visitedE and unvisitedE are are modified.
    * @param v1 Vertex incident on edge being visited
    * @param v2 Vertex incident on edge being visited
    */
    public void visitE(int v1, int v2) {
        if (v1<0 || v1>=totalV || v2<0 || v2>=totalV)  return;
        visitedE[v1][v2] += 1;
        unvisitedE[v1][v2] -= 1;
        if (v1!=v2) {
            visitedE[v2][v1] += 1;
            unvisitedE[v2][v1] -= 1;
        }
    }      

    /**
    * Unvisit edge between two vertices.
    * <br>Side-effect: visitedE and unvisitedE are are modified.
    * @param v1 Vertex incident on edge being unvisited
    * @param v2 Vertex incident on edge being unvisited
    */
    public void unvisitE(int v1, int v2) {
        if (v1<0 || v1>=totalV || v2<0 || v2>=totalV)  return;
        visitedE[v1][v2] -= 1;
        unvisitedE[v1][v2] += 1;
        if (v1!=v2) {
            visitedE[v2][v1] -= 1;
            unvisitedE[v2][v1] += 1;
        }
    }      

//========================================================================================
// LAB6: MODIFY THE TWO METHODS UNDERNEATH AND SUBMIT YOUR MODIFIED GRAPH.JAVA
//        DO NOT MODIFY THE RETURN TYPE AND METHOD SIGNATURES OF EITHER OF THESE METHODS
//        BECAUSE THEY WILL BE AUTOMATICALLY TESTED.
//
// ASSIGNMENT: USE GRAPH.JAVA YOU MODIFIED IN LAB6 BUT DO NOT SUBMIT IT.
//        WE WILL RUN THE GRADING TESTS WITH OUR OWN SOLUTION FOR GRAPH.JAVA
//        THESE TESTS WILL ASSUME THAT ISCONNECT IS IMPLEMENTED 
//        WITH THE RETURN TYPE AND METHOD SIGNATURE BELOW.  DO NOT CHANGE IT IN YOUR OWN CODE.

      
    /**
     * Verifies whether graph is connected.
     * @return True iff graph is connected
     */
    public boolean isConnected() {
    }

        
    /**
     * Conducts a Depth First Search visit of the vertices of the graph starting at vertex.
     * <br>Ties between vertices are broken in numeric order.
     * <br>Side-effect: visitedV is modified.  All its entries will now be true.
     * <br>Side-effect: visit is modified.  It will include all the vertices visited in DFS order.
     * @param vertex First vertex to be visited
     * @param visit When this parameter is an instantiated Walk object, the vertices are added to it in DFS order.
     * When it is null, it is ignored by DFSvisit
     */
    public void DFSvisit(int vertex, Walk visit) {
    } 
    
    
}