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

/**
 * CRState.java - implementation of a channel routing state.  Uses a
 * adjacency list representation of a vertical constraint graph.
 *
 * Created 6/3/2002
 * @author David Hettlinger
 * @version 5
 * 100,000 iterations of next() takes b/tw 20 and 25 seconds.
 */

public class CRState4 implements Annealable
{
    //TOP and BOTTOM represent the row numbers in the donePins table
    //that correspond to the top and bottom rows of pins.
    final int TOP = 0;
    final int BOTTOM = 1;

    //HEAD and NEXT represent the column numbers that correspond to
    //the HEAD and NEXT columns in the adjacency list table.
    final int HEAD = 0;
    final int NEXT = 1;
    
    //Used to generate random moves.
    Random rng;
    int seed; //the rng seed

    //The following four variables are used to initialize CRState
    int subnetCount; //Number of subnets in this channel
    int vertNetCount;
    int[] columnDensity;  //The density of each column.
    int[] topRow;  //The top and bottom rows of pins.
    int[] bottomRow;  //Contain net numbers.

    int columns;  //The number of columns in this CRState.

    int verti; //Used for moves type M1, M2, and M3
    int vertj; //Used for moves type M1 and M2
    int ViSubnetNum; //Used for moves type M1, M2, and M3
    int VjSubnetNum; //Used for move type M1
    int lastMove; //Records the last move made on this state.
    boolean vertexRemoved = false;  //Used to reverse a move type M2
    boolean newVertMade = false; //Used to reverse a move type M3

    boolean[] marked; //Used for isVC()
    
    int v; //number of vertices, which is the channel width
    int maxPossibleSubnets;  //Max possible # of subnets

    int[] subsInVert;  //Number of subnets in each vertex

    //List of probabilities of each type of move.
    double[] probabilityList;

    public int[][] gPi; //The adjacency list representation of the
    //partition pi of the vertical contraint graph.
    //A note about gPi:  Vertex numbers begin with 0, so in gPi,
    //when it looks like there's no vertex listed, it could be that
    //the vertex's number is 0.
    public int[][] oldGPi; //Used to refer back to a previous gPi.
    //Keeps track of the last row used in gPi.
    int endOfGPi;
    //Keeps track of the previous endOfGpi.
    int oldEndOfGPi;

    public Subnet[][] verts; //List of all the vertices and their
    //subnets

    public StringBuffer sBuffer; //Used to print out info.

    /**
     * Constructs a channel routing state.
     * @param topPins the top row of net numbers
     * @param bottomPins the bottom row of net numbers
     * @param sd a seed for the random number generator
     */
    public CRState4(int[] topPins, int[] bottomPins, int sd){
	seed = sd;
	rng = new Random();
	if (seed >= 0) rng.setSeed(seed);
	sBuffer = new StringBuffer(100);

	topRow = topPins;
	bottomRow = bottomPins;
	columns = topPins.length;
	initialize();
		
    }


    /**
     * Constructs a channel routing state.
     * @param fileName the name of the pin info file
     * @param sd a seed for the random number generator
     */
    public CRState4(String fileName) {
	this(fileName, -1);
    }

    /**
     * Constructs a channel routing state.
     * @param fileName the name of the pin info file
     * @param sd a seed for the random number generator
     */
    public CRState4(String fileName, int sd){
	seed = sd;
	rng = new Random();
	if (seed >= 0) rng.setSeed(seed);
	sBuffer = new StringBuffer(100);

	FileReader reader = null;
	try{
	    reader = new FileReader(fileName);
	}
	catch(IOException e) {
	    System.out.println("IOException reading " + fileName);
	}
	BufferedReader in = null;
	if (reader != null)
	    in = new BufferedReader(reader);
	
	String input = "";
	try{
	    input = in.readLine();
	}
	catch(IOException e) {
	    System.out.println(e);
	}
	StringTokenizer st = new StringTokenizer(input);
	columns = Integer.parseInt(st.nextToken());
	topRow = new int[columns];
	bottomRow = new int[columns];

	for (int column = 0; column < columns; column++) {
	    try{
		input = in.readLine();
	    }
	    catch(IOException e) {
		System.out.println(e);
	    }
	    st = new StringTokenizer(input);
	    topRow[column] = Integer.parseInt(st.nextToken());
	    bottomRow[column] = Integer.parseInt(st.nextToken());
	}
	
	initialize();

	try{
	    in.close();
	    reader.close();
	}
	catch(IOException e){
	    System.out.println("Error closing file readers.");
	}
    }

    /**
     * Constructs a channel routing state.
     * @param n the desired number of nets
     * @param p the desired probability of having a directed edge
     * @param cMax the desired maximum number of columns
     * @param sd a seed for the random number generator
     */
    public CRState4(int n, double p, int cMax, int sd){
	seed = sd;
	rng = new Random();
	if (seed >= 0) rng.setSeed(seed);
	sBuffer = new StringBuffer(100);

	//Maximum number of columns that could possibly be used is the
	//total number of vertical constraints that could be set plus
	//one for the net that has not nets below it.
	int maxCols = n * (n - 1) / 2 + 1;
	int listLength = 0;
	if (cMax > maxCols) listLength = cMax;
	else listLength = maxCols;
		
	topRow = new int[listLength];
	bottomRow = new int[listLength];
	
	columns = crpGenerator(n, p, cMax);
	initialize();
    }

    /**
     * Initializes a channel routing state using the set of top and
     * bottom pins.
    */
    public void initialize(){

	probabilityList = new double[3];
	v = 0;
	subnetCount = 0;
	
	//The max possible number of subnets is double 1 less than the
	//channel length.
	maxPossibleSubnets = 2 * (columns - 1);
	
	subsInVert = new int[maxPossibleSubnets]; //Max possible
	//number of vertices is the max possible number of subnets
	
	verts = new Subnet[maxPossibleSubnets][maxPossibleSubnets];
	//Rows = the diferent vertices; columns = their respective
	//subnets
		
	//Fill the subnets and verts lists by going top to bottom and
	//left to right across the topRow and bottomRow lists looking
	//at each entry one at a time.
	boolean[][] donePins = new boolean[2][columns];
	boolean[] netAccountedFor = new boolean[columns];
	columnDensity = new int[columns];

	for (int column = 0; column < columns; column++){
	    
	    //examine top row pin of column
	    examinePin(column, donePins, true, netAccountedFor);
	    //and examine bottom row pin of column
	    examinePin(column, donePins, false, netAccountedFor);
	}

	//Making G by finding all the subnets that are below each
	//subnet and putting them in subnetsBelow of each subnet.

	//Compare vertex v1 to all the other vertices
	for(int v1 = 0; v1 < v; v1++){
	    //Compare subnet sn1 of vertex v1 to all the other subnets
	    for (int sn1 = 0; sn1 < subsInVert[v1]; sn1++){
		//Compare vertex v2 to v1
		for (int v2 = 0; v2 < v; v2++){
		    //Compare subnet sn2 of vertex v2 to sn1
		    for (int sn2 = 0; sn2 < subsInVert[v2]; sn2++){
			//Make sure that the two subnets are not in
			//the same vertex, and thus not in the same
			//net, then compare their beginning and ending
			//points.  When the pin of sn1 is in the top
			//row, then add the edge to the adjacency
			//list.
			if ((v1 != v2) && 
			    (((verts[v1][sn1].begin == verts[v2][sn2].begin) 
			      && (topRow[verts[v1][sn1].begin] == verts[v1][sn1].net)) 
			     || ((verts[v1][sn1].end == verts[v2][sn2].begin) 
				 && (topRow[verts[v1][sn1].end] == verts[v1][sn1].net))
			     || ((verts[v1][sn1].begin == verts[v2][sn2].end) 
				 && (topRow[verts[v1][sn1].begin] == verts[v1][sn1].net))
			     || ((verts[v1][sn1].end == verts[v2][sn2].end) 
				 && (topRow[verts[v1][sn1].end] == verts[v1][sn1].net)))){
			     
			    verts[v1][sn1].addSubBelow(verts[v2][sn2]);
			}
		    }
		}
	    }
	}

	//Initialize marked[] with it's length equal to the number of
	//subnets.
	marked = new boolean[subnetCount];
	
	//Now make gPi from G.
	int maxEdges = (subnetCount - 1) * (subnetCount - 1) * 2;
	//Max vertices = # of subnets
	gPi = new int[subnetCount + maxEdges][2];
	oldGPi = new int[subnetCount + maxEdges][2];

	endOfGPi = 0;
	oldEndOfGPi = 0;
	makeGPi();

	//Now check to see if the original gPi has at least one cycle
	//in it.
	boolean cycleFound = false;
	int count = 0;
	while ((count < v) && !cycleFound){
	    if (gPi[count][NEXT] != 0) // has neighbor
		//check those neighbor(s)
		cycleFound = !(isVC(gPi[count][HEAD])); 
	    count++;
	}
	//If it does have >= 1 cycle, then move each subnet into a
	//vertex of its own.
	if (cycleFound) {
	    moveSubnets(this);
	    makeGPi();
	}

   }    

    /**
     * Moves each subnet of a given CRState into its own vertex.
     * @param state the CRState object
     */
    public void moveSubnets(CRState4 state) {

	Subnet tempSubRef;
	for (int i = 0; i < state.v; i++){
	    for (int j = 1; j < state.subsInVert[i]; j++){
		tempSubRef = state.verts[i][j];
		state.verts[i][j] = null;
		state.verts[state.v][0] = tempSubRef;
		state.verts[state.v][0].setVertex(state.v);
		state.subsInVert[state.v] = 1;
		state.v++;
	    }
	    state.subsInVert[i] = 1;
	}
	state.makeGPi();
    }
    
    /**
     * Finds and makes the Subnets given a pin.
     * @param column the column to start on
     * @param pinsExamined Keeps track of pins accounted for
     * @param top true if the pin is on the top row; false if on
     * bottom row.
     * @param netAccountedFor designates whether the span of this
     * pin's net has been accounted for in the given column.
     */
    public void examinePin(int column, boolean[][] pinsExamined, boolean top, boolean[] netAccountedFor){
	
	int thisRow = 0;
	int otherRow = 0;
	int current = 0;
	int next = 0;
	if (top) {
	    thisRow = 0;
	    otherRow = 1;
	    current = topRow[column];
	    next = bottomRow[column];
	}
	else {
	    thisRow = 1;
	    otherRow = 0;
	    current = bottomRow[column];
	    next = topRow[column];
	}

	//Keeps track of the last column to have a pin of net current
	//in it
	int lastColumn = column;
	//for when a net has two pins in the same column
	int sameColumn = -1;
	vertNetCount = 0;
	for (int i = 0; i < netAccountedFor.length; i++)
	    netAccountedFor[i] = false;
	
	if (current != 0 && !pinsExamined[thisRow][column]){

	    if (next == current){

		sameColumn = column;
		pinsExamined[otherRow][column] = true;
	    }
	    
	    //Now go through and examine the other columns, looking
	    //for the pins in the same net as current.
	    for (int nextColumn = column + 1; nextColumn < columns; nextColumn++){
		//Examine the pin on the top row.
		next = topRow[nextColumn];
		
		if (next == current){
		    if (sameColumn != -1) sameColumn = -1;

		    addSubnet(subnetCount, current, lastColumn, nextColumn, maxPossibleSubnets - 1, netAccountedFor);

		    lastColumn = nextColumn;
		    pinsExamined[TOP][nextColumn] = true;
		}
		
		//Examine the pin on the bottom row.
		next = bottomRow[nextColumn];
		if ((next == current) && (next != topRow[nextColumn])){
		    if (sameColumn != -1) sameColumn = -1;
		    
		    addSubnet(subnetCount, current, lastColumn, nextColumn, maxPossibleSubnets - 1, netAccountedFor);
		    
		    lastColumn = nextColumn;
		    pinsExamined[BOTTOM][nextColumn] = true;
		}
		else if (next == topRow[nextColumn]) 
		    pinsExamined[BOTTOM][nextColumn] = true;
	    }

	    if (sameColumn != -1) 
		addSubnet(subnetCount, current, lastColumn, lastColumn, maxPossibleSubnets - 1, netAccountedFor);
	    
	    pinsExamined[thisRow][column] = true;
	    subsInVert[v] = vertNetCount;
	    v++;
	}
    }

    /**
     * Adds a Subnet to a vertex.
     * @param name the name of the Subnet
     * @param net the net of this Subnet
     * @param begin the column it begins in
     * @param end the column it ends in
     * @param listSize the alotted size for the subnetsBelow array
     * @param netAccountedFor an array telling which columns this
     * net has been registered as spanning.
     */
    public void addSubnet(int name, int net, int begin, int end, int listSize, boolean[] netAccountedFor) {

	//First add the Subnet...
	verts[v][vertNetCount] = 
	    new Subnet(name, net, begin, end, listSize);
	verts[v][vertNetCount].setVertex(v);
	vertNetCount++;
	subnetCount++;

	//then increment the counter for the columns that this Subnet
	//spans.
	for (int i = begin; i <= end; i++)
	    if (!netAccountedFor[i]){
		
		if (topRow[i] != bottomRow[i]) columnDensity[i]++;
		netAccountedFor[i] = true;
	    }
    }
    
    /**
     * Finds the (maximum) density for this problem, which is the most
     * number of subnets that cross a single column.  
     * @return greatestD The maximum density (d).  
     */
    public int getDensity(){
	
	int greatestD = 0;
	for (int i = 0; i < columns; i++)
	    if (columnDensity[i] > greatestD)
		greatestD = columnDensity[i];
	
	return greatestD;
    }
    
    /**
     * Finds the longest chain length in the vertical constraint graph.
     * @return greatestP The longest chain length.
     */
    public int getP(){
	int greatestP = 0;
	int currentP = 0;

	//If there is a vertex with more than one subnet in it,
	//gPi may have a longer chain length than G, so need to put
	//each subnet into its own vertex (temporarily)
	CRState4 temp = null;
	if (v < subnetCount) {
	 
	    temp = (CRState4) this.clone();
	    moveSubnets(temp);
	}
	else temp = this;

	for (int i = 0; i < v; i++) {

	    currentP = chainLength(i, 0, temp, 0);
	    if (currentP > greatestP) 
		greatestP = currentP;
	}

	return greatestP;
    }

    /**
     * Finds the longest path in G beginning with start.
     * @param start the vertex to start on
     * @param prevNodes the number of nodes up to this
     * node in the current chain of nodes
     * @param state the CRState object containing the correct gPi
     * @param maxNodes the current max chain length found
     * @return maxNodes the number of nodes in the longest
     * thus far
     */
    private int chainLength(int start, int prevNodes, CRState4 state, int maxNodes) {

	int nextNeighbor;
	int nodes;
	int nextRow = state.gPi[start][NEXT];
	int currentNodes = prevNodes + 1;

	if (nextRow == 0) return currentNodes;
	else {
	    while(nextRow != 0) {
		
		nextNeighbor = state.gPi[nextRow][HEAD];
		
		nodes = chainLength(nextNeighbor, currentNodes, state, maxNodes);
		if (nodes > maxNodes) maxNodes = nodes;

		nextRow = state.gPi[nextRow][NEXT];
	    }
	}

	return maxNodes;
    }

    /**
     * Returns a lower bound on the channel width.  It's either the
     * channel density or the longest chain length, whichever is
     * greater.
     * @return a lower bound.
     */
    public double getLowerBound(){
	int d = getDensity();
	int p = getP();

	if (d > p) return d;
	else return p;
    }

    /**
     * Uses verts to make gPi.  gPi an array representation of a
     * partition of the vertical constraint graph (G), which is
     * represented by the subnetsBelow arrays of each Subnet object.
     */
    private void makeGPi(){
	
	//Switch the references -- gPi refers to the former oldGPi
	//array; oldGpi refers to the former Gpi array.
	int[][] tempArr = gPi;
	gPi = oldGPi;
	oldGPi = tempArr;

	//Clear out gPi
	for (int i = 0; i < oldEndOfGPi; i++) {
	    for (int j = 0; j < gPi[i].length; j++)
		gPi[i][j] = 0;
	}
	oldEndOfGPi = endOfGPi;

	Subnet subnet1 = null;
	Subnet subnet2 = null;
	int vertName = 0;
	int sbnet = 0;

	boolean found = false;
	int NEXTval = 0;
	int foundRow = 0;
	int nextEdge = subnetCount; //The first edge will be in the
	//first row after the last possible vertex.

	//Examine each of the vertices
	for (int v1 = 0; v1 < v; v1++){
	    gPi[v1][HEAD] = v1;

	    //Examine each of the subnets in v1
	    for (int sn1 = 0; sn1 < subsInVert[v1]; sn1++){
		subnet1 = verts[v1][sn1];

		//Examine each of the subnets below subnet1
		for (int i = 0; i < subnet1.numSubsBelow; i++){

		    subnet2 = subnet1.subnetsBelow[i];
		    vertName = subnet2.vertex;

		    //Now add the subnet entry to gPi
		    NEXTval = v1;
		    do{
			foundRow = NEXTval;
			NEXTval = gPi[NEXTval][NEXT];
		    } while ((NEXTval != 0) && 
			     (gPi[NEXTval][HEAD] != vertName));

		    if (NEXTval == 0){
			gPi[foundRow][NEXT] = nextEdge;
			gPi[nextEdge][HEAD] = vertName;
			nextEdge++;
		    }
		}
	    } 

	}
	endOfGPi = nextEdge;

    }

    /**
     * Makes a random vertical constraint matrix.
     * Taken from Figure 2 p. 5 of Chao and Harper.
     * @param n the number of nets
     * @param p the probability for an directed edge to be made
     * @return vc a vertical constraint matrix.
     */
    public int[][] makeVC(int n, double p){
	int[][] vc = new int[n + 1][n + 1];
	
	for (int i = 1; i <= n - 1; i++)
	    for (int j = i + 1; j <= n; j++)
		if (rng.nextDouble() < p) vc[i][j] = 1;

	return vc;
    }

    /**
     * Performs a transistive reduction on the original adjacency matrix.
     * @param vcMat a vertical constraint matrix to reduce.
     * @param nets the number of nets in the vertical contraint graph.
     */
    public void transReductor(int[][] vcMat, int nets){
	boolean[] trArray = new boolean[nets + 1];
        Stack trStack = new Stack();
	
	for (int i = 1; i <= nets - 1; i++){
	    for (int j = i; j < trArray.length; j++) trArray[j] = false;
	    depthFirstReduct(trStack, trArray, new Integer(i), nets, vcMat);
	}
    }

    /**
     * Starts at the given node and eliminates shortcut connections.
     * @param nodeStack the Stack object to be used to keep treck of the 
     * path sequence.
     * @param marked the boolean array used to keep track of which nodes
     * have already been visited.
     * @param node an Integer representing the node to start searching from.
     * @param nets the number of nets in the vertical constraint graph.
     * @param vc the vertical constraint matrix to be reduced.
     */
    public void depthFirstReduct(Stack nodeStack, boolean[] marked, Integer node, int nets, int[][] vc){
	
	Integer ancestorNode = null;
	int[] connections = new int[nets];
	int neighborCount = 0;
        int nextNeighbor = 0;
	int n = 0;

	nodeStack.push(node);

	//Retrace path from 2 nodes back to the first node and
	//eliminate shortcuts.
	for (n = nodeStack.size() - 3; n >= 0; n--){
	    ancestorNode = (Integer) nodeStack.elementAt(n);
	    if(vc[ancestorNode.intValue()][node.intValue()] == 1)
		vc[ancestorNode.intValue()][node.intValue()] = 0;
	}

	if (!marked[node.intValue()]){
	    //Now get the neighbors of the current node...
	    for (n = 1; n <= nets; n++)
		if (vc[node.intValue()][n] == 1){
		    connections[neighborCount] = n;
		    neighborCount++;
		}
	    
	    //and examine the paths through each neighbor. 
	    for (n = 0; n < neighborCount; n++){
		nextNeighbor = connections[n];
		depthFirstReduct(nodeStack, marked, new Integer(nextNeighbor), nets, vc);
	    }
	}
	//Exiting depthFirstReduct, so pop the stack and label this
	//vertex (node) as marked.
	nodeStack.pop();
	marked[node.intValue()] = true;
    }

    /**
     * Assigns columns for each net.
     * Taken from Figure 3 p. 7 of Chao and Harper.
     * @param vcMat a vertical constraint matrix
     * @param span a table of the span of each net
     * @param cMax the current number of columns
     * @param color a table of the "color" of each net
     * @param nets the number of nets in this problem
     * @return the new cMax value
     */
    public int makeCol(int[][] vcMat, int[][] span, int cMax, int[] color, int nets){

	//BEGIN and END are used with the span table to represent the
	//beginning and end of each net's span.
	final int BEGIN = 0;
	final int END = 1;

	int c = 0;
	int l = 0;
	int r = 0;
	for (int i = 1; i <= nets - 1; i++){

	    for (int j = i + 1; j <= nets; j++){
		if (vcMat[i][j] == 1){
		    switch (color[i]){
		    case 0:  
			c = pickCol(0, cMax - 1);
			if (c == -1){
			    cMax++;
			    c = cMax - 1;
			}
			span[i][BEGIN] = c;
			color[i] = 1;
			break;

		    case 1:
			c = pickCol(0, cMax - 1);
			if (c ==  -1){
			    cMax++;
			    c = cMax - 1;
			}
			if (c < span[i][BEGIN]){
			    span[i][END] = span[i][BEGIN];
			    span[i][BEGIN] = c;
			}
			else span[i][END] = c;
			color[i] = 2;
			break;

		    case 2:
			if (span[i][END] >= span[j][BEGIN]){
			    l = span[j][BEGIN];
			    r = span[i][END];
			}
			else if (span[j][END] >= span[i][BEGIN]){
			    l = span[i][BEGIN];
			    r = span[j][END];
			}
			else {
			    l = span[i][END];
			    r = span[j][BEGIN];
			}
			c = pickCol(l, r);
			if (c == -1) c = pickCol(0, l);
			if (c == -1) c = pickCol(r, cMax - 1);
			if (c == -1){
			    cMax++;
			    c = cMax - 1;
			}
			if (c < span[i][BEGIN]) span[i][BEGIN] = c;
			else if (c > span[i][END]) span[i][END] = c;			
			break;
		    }
		    
		    topRow[c] = i;
		    bottomRow[c] = j;

		    if (color[j] == 0){
			color[j] = 1;
			span[j][BEGIN] = c;
		    }
		    else if (color[j] == 1){
			color[j] = 2;
			if (c < span[j][BEGIN]){
			    span[j][END] = span[j][BEGIN];
			    span[j][BEGIN] = c;
			}
			else span[j][END] = c;
		    }
		    else {
			if (c < span[j][BEGIN]) span[j][BEGIN] = c;
			else if (c > span[j][END]) span[j][END] = c;			
		    }
		}
	    }
	}

	return cMax;
    }

    /**
     * Randomly picks a free column in the interval [a, b].
     * If there is no free column available in the interval,
     * -1 is returned.
     * Taken from Chao and Harper p. 5.
     * @param a the start of the interval
     * @param b the end of the interval
     * @return c a free column if found or -1 if none found
     */
    public int pickCol(int a, int b){
	int c = -1;
	int start = rng.nextInt(b - a + 1) + a;
	
	for (int i = start; i <= b && c == -1; i++)
	    if (topRow[i] == 0 && bottomRow[i] == 0)
		c = i;
	
	if (c == -1)
	    for (int i = a; i < start && c == -1; i++)
		if (topRow[i] == 0 && bottomRow[i] == 0)
		    c = i;
	return c;
    }

    /**
     * A random difficult CRP generator.
     * Taken from Figure 4 p. 8 of Chao and Harper
     * @param n the number of nets
     * @param p the probability of a directed edge in the 
     * vertical constraint graph.
     * @param maxColumns the desired number of columns
     * @return cMax the number of columns in this CR problem.
     */
    public int crpGenerator(int n, double p, int maxColumns){

	final int BEGIN = 0;  //Used to refer to the beginning and
	final int END = 1;  //end of a net span.
	int nets = n; //Number of nets in this CRState object.
	int[][] vc; //The vertical consistency matrix.
	int[] color = new int[nets + 1]; //Keeps track of the "color" of
	//each net.
	int[][] span = new int[nets + 1][2];  //The spans of the nets.
	int cMax = maxColumns; //After CRP-Generator is run, it is the
	//number of columns in this CRState problem.

	vc = makeVC(nets, p);
	transReductor(vc, nets);
	cMax = makeCol(vc, span, cMax, color, nets);

	int i = 1;
	int c = -1;
	while (i <= n){

	    if (color[i] == 2) i++;
	    else{

		if (color[i] == 1) {
		    int left = span[i][BEGIN];
		    int right = left;

		    while (c == -1 && (left >= 0 || right < cMax)){
			left--;
			right++;
			if (left >= 0 && 
			    topRow[left] == 0 && bottomRow[left] == 0)
			    c = left;
			else if (right < cMax && 
				 topRow[right] == 0 && bottomRow[right] == 0)
			    c = right;
		    }
		
		    if (c == -1) {
			cMax++;
			c = cMax - 1;		    
		    }

		    if (c < span[i][BEGIN]){
			span[i][END] = span[i][BEGIN];
			span[i][BEGIN] = c;
		    }
		    else span[i][END] = c;
		
		    color[i] = 2;
		}
	    
		if (color[i] == 0) {
		    c = pickCol(0, cMax - 1);
		    if (c == -1){
			cMax++;
			c = cMax - 1;	
		    }
		
		    span[i][BEGIN] = c;
		    color[i] = 1;
		}
		
		if (rng.nextDouble() > .5) topRow[c] = i;
		else bottomRow[c] = i;
		c = -1;
	    }
	}

	return cMax;
    }

    /**
     * Makes a move of type M1 if valid.  Attempts to swap two random
     * subnets, one from vertex verti and one from vertex vertj.  If
     * this results in an invalid partition, then no move is made,
     * otherwise the move is made.
     * @return true if the move is accomplished; false if it isn't
     */
    public boolean moveM1(){
	
	lastMove = 1;
	verti = rng.nextInt(v);
	do{ vertj = rng.nextInt(v); } while (vertj == verti);
	ViSubnetNum = rng.nextInt(subsInVert[verti]);
	VjSubnetNum = rng.nextInt(subsInVert[vertj]);

	//System.out.println("verti = " + verti + "; ViSubnetNum = " + ViSubnetNum);
	//System.out.println("vertj = " + vertj + "; VjSubnetNum = " + VjSubnetNum);
	
	//Need to switch the subnets before checking for horizontal
	//consistency.
	Subnet tempSubRef = verts[verti][ViSubnetNum];    
	verts[verti][ViSubnetNum] = verts[vertj][VjSubnetNum];
	verts[vertj][VjSubnetNum] = tempSubRef;

	if (isHC(verts[verti][ViSubnetNum], verti) 
	    && isHC(verts[vertj][VjSubnetNum], vertj)){
	 
	    verts[verti][ViSubnetNum].setVertex(verti);
	    verts[vertj][VjSubnetNum].setVertex(vertj);

	    makeGPi();

	    //Now check for vertical consistency
	    
	    if (isVC(verti) && isVC(vertj)) return true;
	    else reverseM1();
	}
	else {

	    verts[vertj][VjSubnetNum] = verts[verti][ViSubnetNum];
	    verts[verti][ViSubnetNum] = tempSubRef;
	} 
	return false;
    }

    /**
     * Attempts to make a move of type M2 -- Moves a random subnet
     * from vertex verti to vertex vertj if that results in a valid
     * partition.
     * @return true if the move is accomplished; false if it isn't
     */
    public boolean moveM2(){
	
	lastMove = 2;
	verti = rng.nextInt(v);
	do{ vertj = rng.nextInt(v); } while (vertj == verti);
	ViSubnetNum = rng.nextInt(subsInVert[verti]);

	//System.out.println("verti = " + verti + "; ViSubnetNum = " + ViSubnetNum);
	//System.out.println("vertj = " + vertj);
	
	if (isHC(verts[verti][ViSubnetNum], vertj)){

	    Subnet tempSubRef = verts[verti][ViSubnetNum];
	    verts[verti][ViSubnetNum] = null;
	    verts[vertj][subsInVert[vertj]] = tempSubRef;
	    verts[vertj][subsInVert[vertj]].setVertex(vertj);
	    subsInVert[vertj]++;

	    //If there's still at least one Subnet left in verti, then
	    //move the end Subnet to the spot where the removed Subnet
	    //was.
	    if (subsInVert[verti] > 1){
		vertexRemoved = false;
		tempSubRef = verts[verti][subsInVert[verti] - 1];
		verts[verti][subsInVert[verti] - 1] = null;
		verts[verti][ViSubnetNum] = tempSubRef;
		subsInVert[verti]--;
	    }
	    //If there are no Subnets left in verti, then move the last
	    //vertex to verti's spot.
	    else{
		vertexRemoved = true;
		//This is for the case where vertex j gets moved
		//because it is in the last row.
		if (vertj == v - 1) vertj = verti;

		Subnet[] tempSbnArr = verts[verti];
		verts[verti] = verts[v - 1];
		verts[v - 1] = tempSbnArr;
		subsInVert[verti] = subsInVert[v - 1];
		subsInVert[v - 1] = 0;
		for (int i = 0; i < subsInVert[verti]; i++)
		    verts[verti][i].setVertex(verti);
		v--;
	    }
	  
	    makeGPi();
	 	    
	    //Now check for vertical consistency.
	    if (isVC(vertj)) return true;  
	    else reverseM2();
	}

	return false;
    }

    /**
     * Makes a move of type M3.  Removes a random subnet from a
     * random vertex and uses it to make a new vertex.  Always a valid
     * partition results, so always returns true.
     * @return true if the move is accomplished; false if it isn't
     */
    public boolean moveM3(){

	lastMove = 3;
	verti = rng.nextInt(v);
	ViSubnetNum = rng.nextInt(subsInVert[verti]);
	newVertMade = false;

	//System.out.println("verti = " + verti + "; ViSubnetNum = " + ViSubnetNum);
	//System.out.println("moveM3:  subnet i = " + verts[verti][ViSubnetNum]);
	
	if (subsInVert[verti] > 1){
	    newVertMade = true;

	    Subnet tempSubRef = verts[verti][ViSubnetNum];
	    verts[verti][ViSubnetNum] = null;
	    verts[v][0] = tempSubRef;
	    verts[v][0].setVertex(v);
	    subsInVert[v] = 1;
	    v++;

	    tempSubRef = verts[verti][subsInVert[verti] - 1];
	    verts[verti][subsInVert[verti] - 1] = null;
	    verts[verti][ViSubnetNum] = tempSubRef;
	    subsInVert[verti]--;

	    makeGPi();
	}

	return true;
    }

    /**
     * Reverses the M1 move that was previously made.
     */
    public void reverseM1(){
	
	Subnet tempSubRef = verts[verti][ViSubnetNum];
	verts[verti][ViSubnetNum] = verts[vertj][VjSubnetNum];
	verts[vertj][VjSubnetNum] = tempSubRef;
	verts[verti][ViSubnetNum].setVertex(verti);
	verts[vertj][VjSubnetNum].setVertex(vertj);

	int[][] tempArr = gPi;
	gPi = oldGPi;
	oldGPi = tempArr;

	//I do need to use the 3-step swap to get the correct
	//oldEndOfGPi value for clearing out oldGPi in makeGPi().
	int tempInt = endOfGPi;
	endOfGPi = oldEndOfGPi;
	oldEndOfGPi = tempInt;
    }

    /**
     * Reverses the M2 move that was previously made.
     */
    public void reverseM2(){

	Subnet removedSub = verts[vertj][subsInVert[vertj] - 1];
	verts[vertj][subsInVert[vertj] - 1] = null;
	subsInVert[vertj]--;

	if (!vertexRemoved){
	    subsInVert[verti]++;
	    verts[verti][subsInVert[verti] - 1] = verts[verti][ViSubnetNum];
	    verts[verti][ViSubnetNum] = removedSub;
	    verts[verti][ViSubnetNum].setVertex(verti);
	}
	else{
	    v++;
	    Subnet[] tempSbnArr;

	    subsInVert[v - 1] = subsInVert[verti];
	    subsInVert[verti] = 1;

	    tempSbnArr = verts[v - 1];
	    verts[v - 1] = verts[verti];  
	    verts[verti] = tempSbnArr;

	    verts[verti][0] = removedSub;
	    verts[verti][0].setVertex(verti);

	    for (int i = 0; i < subsInVert[v - 1]; i++)	
		verts[v - 1][i].setVertex(v - 1);
	}
	
	int[][] tempArr = gPi;
	gPi = oldGPi;
	oldGPi = tempArr;

	int tempInt = endOfGPi;
	endOfGPi = oldEndOfGPi;
	oldEndOfGPi = tempInt;

    }

    /**
     * Reverses the M3 move that was previously made.
     */
    public void reverseM3(){
	if (newVertMade){
	    
	    Subnet tempSubRef = verts[verti][ViSubnetNum];
	    verts[verti][subsInVert[verti]] = tempSubRef;
	    Subnet tempSubRef2 = verts[v - 1][0];
	    verts[v - 1][0] = null;
	    verts[verti][ViSubnetNum] = tempSubRef2;
	    verts[verti][ViSubnetNum].setVertex(verti);
	    
	    subsInVert[verti]++;
	    subsInVert[v - 1] = 0;
	    v--;

	    int[][] tempArr = gPi;
	    gPi = oldGPi;
	    oldGPi = tempArr;
	    
	    int tempInt = endOfGPi;
	    endOfGPi = oldEndOfGPi;
	    oldEndOfGPi = tempInt;
	}
    }
    
    /**
     * Checks to see if the subnet sbnt in vertex vtx is horizontally
     * consistent with the rest of the subnets in vtx.
     * @param sbnt the subnet to be checked
     * @param vtx the number of the vertex contains the subnets that
     * sbnt will be checked against.
     * @return true if sbnt is horizontally consistent with the other
     * subnets in vtx; false if not.
     */
    public boolean isHC(Subnet sbnt, int vtx){
	//Check sbnt against all other subnets in vtx.
	for (int i = 0; i < subsInVert[vtx]; i++){
	    //If the 3 conditions for horizontal consistency do not
	    //hold, then return false...
	    if (!(sbnt.net == verts[vtx][i].net) && 
		!((sbnt.end < verts[vtx][i].begin) 
		  || (verts[vtx][i].end < sbnt.begin))){
		return false;
	    }
	}
	//Otherwise return true.
	return true;
    }

    //I used Figure 14.4 p. 693 of Main for the next method.
    /**
     * Checks gPi for vertical consistency beginning at vertex start.
     * If gPi has no cycles, then it is vertically consistent.
     * @param vtx the number of the vertex to begin searching at.
     * @return false if a cycle is found; true if not.
     */
    private boolean isVC(int vtx){

	int nextNeighbor;
	int nextRow = gPi[vtx][NEXT];

	marked[vtx] = true;

	//Traverse all the neighbors looking for a marked vertex.

	boolean vertCon = true;
	while(nextRow != 0 && vertCon) {

	    nextNeighbor = gPi[nextRow][HEAD];

	    if (marked[nextNeighbor]) { 
			
		marked[vtx] = false;
		return false;
	    }
	    else vertCon = isVC(nextNeighbor);

	    nextRow = gPi[nextRow][NEXT];
	}

	marked[vtx] = false;
	return vertCon;
    }

    /**
     * Creates the next valid partition.
     */
    public void next() {

	double b = 3.0; //Wong's default value.
	double w = v * 1.0;
	int vert1 = 0;
	int vert2 = 0;
	double move = 0;
	boolean completed = false;
	
	//Create the probability list
	double m1 = 1 / (1 + b) * (w - 1) / w;
	double m2 = b / (1 + b) * (w - 1) / w;
	double m3 = 1 / w;

	probabilityList[0] = m1;
	probabilityList[1] = probabilityList[0] + m2;
	probabilityList[2] = probabilityList[1] + m3;
	
	while (!completed){

	    //Randomly choose one type of move
	    move = rng.nextDouble() * probabilityList[2];

	    //Now perform the move.
	    if (move <= probabilityList[0])
		completed = moveM1();
	    else if (move <= probabilityList[1])
		completed = moveM2();
	    else completed = moveM3();
	    //If invalid (completed == false), repeat the above.
	}
    }

    /**
     * Returns CRState back to its previous state.
     */
    public void reject(){
	if (lastMove == 1) reverseM1();
	else if (lastMove == 2) reverseM2();
	else reverseM3();
    }
		
    /**
     * Return the energy of the system 
     * @return the width of this channel, which is 
     * the system energy.
     */
    public double energy()
    {
	return v;
    }

    /**
     * Sets up an ordering for the vertices.
     * @return vertOrder a list of the order of the vertices
     * (indices represent track numbers; values represent vertex
     * numbers).
     */
    public int[] getVertsOrder(){
	
	int[] chainLength = new int[v];
	for (int i = 0; i < v; i++) 
	    chainLength[i] = chainLength(i, 0, this, 0);
	
	int[] vertOrder = new int[v];
	int trackCount = 0;
        for (int chainCount = 1; chainCount <= v && trackCount < v; chainCount++) {

	    for (int count = 0; count < v && trackCount < v; count++) {

		if (chainLength[count] == chainCount) {
		    vertOrder[trackCount] = count;
		    trackCount++;
		}
	    }
	}
	
	return vertOrder;
    }

    /**
     * Clones a CRState object.
     * @return cloned a near duplicate of the 
     * current CRState object.
     */
    public Object clone(){
	CRState4 cloned = null;
	try {cloned = (CRState4) super.clone();}
	catch(CloneNotSupportedException e){
	    System.out.println("CloneNotSupportedException caught");
	    return null;
	}
	cloned.rng = new Random();
	if (cloned.seed >= 0) cloned.rng.setSeed(this.seed);
	cloned.sBuffer = new StringBuffer(this.sBuffer.capacity());

	cloned.subsInVert = (int[]) this.subsInVert.clone();

	cloned.gPi = new int[this.gPi.length][];
	for (int i = 0; i < cloned.gPi.length; i++)
	    cloned.gPi[i] = (int[]) this.gPi[i].clone();

	cloned.oldGPi = new int[this.oldGPi.length][];
	for (int i = 0; i < cloned.oldGPi.length; i++)
	    cloned.oldGPi[i] = (int[]) this.oldGPi[i].clone();
	    
	int count = 0;
	Subnet[] tempArray = new Subnet[this.subnetCount];
	for (int i = 0; i < this.v; i++)
	    for (int j = 0; j < this.subsInVert[i]; j++){
		tempArray[count] = (Subnet) this.verts[i][j].clone();
		count++;
	    }

	cloned.verts = 
	    new Subnet[this.maxPossibleSubnets][this.maxPossibleSubnets];

	//Need to set all the cell references to the correct Subnet
	//objects.
	    
	for (int i = 0; i < cloned.v; i++)
	    for (int j = 0; j < cloned.subsInVert[i]; j++)
		for (int k = 0; k < tempArray.length && cloned.verts[i][j] == null; k++) 
		    if (this.verts[i][j].name == tempArray[k].name){
			cloned.verts[i][j] = tempArray[k];
			cloned.verts[i][j].setSubsBelowList(tempArray, 
							    this.verts[i][j].subnetsBelow);
		    }
	    
	return cloned;
    
	
    }

    // These next four methods came from Horstmann Table2.java p. 466
    // - 7
    /**
       Makes a String representation of verts.
       @param table the values to be printed
       @param width the column width
       @return a String representing verts
    */
    public String vertsTable(Subnet[][] table, int width) {
	//	sBuffer.delete(0, sBuffer.length());
	for (int i = 0; i < v; i++) {
	    sBuffer.append(i + ":  ");
	    for (int j = 0; j < subsInVert[i]; j++) 
		sBuffer.append(format(table[i][j] + "", width));
	    sBuffer.append("\n");
	}
	return sBuffer.toString();
    }

    /**
       Makes a String representation of gPi.
       @param table the values to be printed
       @param width the column width
       @param end one beyond the last row in the table used
       @return a String reprsenting gPi
    */
    public String anyTable(int[][] table, int width, int end) {
	//	sBuffer.delete(0, sBuffer.length());
	for (int i = 0; i < end; i++) {
	    sBuffer.append(i + ": ");  
	    for (int j = 0; j < table[i].length; j++)
		sBuffer.append(format(table[i][j], width));
	    sBuffer.append("\n");
	}
	return sBuffer.toString();
    }

    /**
       Formats a string to fit in a field of constant width
       @param n the string to format
       @param width the field width
       @return a string of length width, consisting of 
       leading spaces followed by the number n
    */
    public String format(String n, int width) {
	String nstr = n;

	// pad with spaces
	
	while (nstr.length() < width)
	    nstr = " " + nstr;
	
	return nstr;
    }

    /**
       Formats an integer to fit in a field of constant width
       @param n the integer to format
       @param width the field width
       @return a string of length width, consisting of 
       leading spaces followed by the number n
    */
    public String format(int n, int width) {
	
	return format(" " + n, width);
    }
    
    /**
       Returns a String representation of this CRState object.
       @return returnString a String representation of this CRState object
    */
    public String toString(){
	
	sBuffer.delete(0, sBuffer.length());
	sBuffer.append("verts:\n");
	vertsTable(verts, 13);
	sBuffer.append("gPi:\n");
	anyTable(gPi, 3, endOfGPi);
	return sBuffer.toString();
    }
	
}
