package bgu.bio.adt.graphs;
import gnu.trove.list.array.TIntArrayList;
import java.util.ArrayList;
/**
* A class representing a flexible graph using array lists to hold the
* information (same functionality as {@link Graph})
*
* @author milon
*/
public class FlexibleGraph {
/**
* Holds the amount of nodes in the graph
*/
protected int nodeNum;
/**
* Holds the amount of edges in a graph
*/
protected int edgeNum;
/**
* Information on outgoing edges in the graph
*/
public ArrayList<TIntArrayList> outAdjLists;
/**
* Information on the incoming edges in the graph
*/
protected ArrayList<TIntArrayList> inAdjLists;
/**
* index the "in" neighborIx for an "out" neighbor.
*/
protected ArrayList<TIntArrayList> inToOutNeighborIx;
/**
* Maximal out degree in the graph
*/
protected int maxOutDeg;
/**
* Number of nodes with out degree of zero
*/
protected int numOfLeaves;
public FlexibleGraph() {
this.outAdjLists = new ArrayList<TIntArrayList>();
this.inAdjLists = new ArrayList<TIntArrayList>();
this.inToOutNeighborIx = new ArrayList<TIntArrayList>();
nodeNum = 0;
edgeNum = 0;
maxOutDeg = 0;
numOfLeaves = 0;
}
/**
* Add Node to the end of the list
*/
/**
* @return the id of the new node
*/
public int addNode() {
this.outAdjLists.add(new TIntArrayList());
this.inAdjLists.add(new TIntArrayList());
this.inToOutNeighborIx.add(new TIntArrayList());
this.nodeNum = this.outAdjLists.size();
this.numOfLeaves++;
return this.nodeNum - 1;
}
/**
* Add an edge to graph
*
* @param from
* the source of the edge
* @param to
* the target of the edge
*/
public void addEdge(int from, int to) {
// if the previous size was zero then it is no longer a leaf
if (this.outAdjLists.get(from).size() == 0) {
this.numOfLeaves--;
}
this.outAdjLists.get(from).add(to);
// check if the new edge turns the current node to the one with the
// maximum degree
if (this.maxOutDeg < this.outAdjLists.get(from).size()) {
this.maxOutDeg = this.outAdjLists.get(from).size();
}
this.edgeNum++;
// update the incoming edge information
this.inAdjLists.get(to).add(from);
this.inToOutNeighborIx.get(to).add(
this.outAdjLists.get(from).size() - 1);
}
/**
* Remove an edge from the graph
*
* @param from
* the source of the edge
* @param to
* the target of the edge
*/
public void removeEdge(int from, int to) {
// if the size is one, then it is a new leaf
final TIntArrayList fromOutList = this.outAdjLists.get(from);
if (fromOutList.size() == 1) {
this.numOfLeaves++;
fromOutList.resetQuick();
} else {
int index = this.getNeighborIx(from, to);
// switch the out edge in the list
fromOutList.set(index, fromOutList.get(fromOutList.size() - 1));
fromOutList.removeAt(index);
// change the incoming edge index in the new node in the old
// position
final TIntArrayList toInList = this.inAdjLists.get(fromOutList
.get(index));
final TIntArrayList toInIndexList = this.inToOutNeighborIx
.get(index);
int foundPos = -1;
for (int i = 0; i < toInList.size() && foundPos == -1; i++) {
if (toInList.get(i) == from) {
foundPos = i;
}
}
toInIndexList.set(foundPos, index);
}
removeInEdge(from, to);
edgeNum--;
calcMaxOutDeg();
}
private void removeInEdge(int from, int to) {
final TIntArrayList toInList = this.inAdjLists.get(to);
final TIntArrayList toInIndexList = this.inToOutNeighborIx.get(to);
if (inAdjLists.get(to).size() == 1) {
toInList.resetQuick();
toInIndexList.resetQuick();
} else {
int index = -1;
for (int i = 0; i < toInList.size() && index == -1; i++) {
if (toInList.get(i) == from) {
index = i;
}
}
// switch the locations
toInList.set(index, toInList.get(toInList.size() - 1));
toInIndexList.set(index,
toInIndexList.get(toInIndexList.size() - 1));
// remove the last location
toInList.removeAt(toInList.size() - 1);
toInIndexList.removeAt(toInIndexList.size() - 1);
}
}
/**
* Returns the number of nodes in the graph
*
* @return number of nodes in the graph
*/
public int getNodeNum() {
return nodeNum;
}
/**
* Return the out degree of a node
*
* @param nodeIx
* the node ix
* @return the number of adjacent nodes
*/
public int outDeg(int nodeIx) {
return outAdjLists.get(nodeIx).size();
}
/**
* Return the in degree of a node
*
* @param nodeIx
* the node ix
* @return the number of adjacent nodes
*/
public int inDeg(int nodeIx) {
return inAdjLists.get(nodeIx).size();
}
/**
* Gets the neighbor given an index in the list.
*
* @param node
* the node index
* @param neighborIx
* the neighbor index in the list
* @return the neighbor id
*/
public int getNeighbor(int node, int neighborIx) {
return outAdjLists.get(node).get(neighborIx);
}
/**
* Gets the neighbor index in the out adjacent list.
*
* @param node
* the node
* @param neighbor
* the neighbor id
* @return the neighbor index in the list of the node
*/
public int getNeighborIx(int node, int neighbor) {
final TIntArrayList is = outAdjLists.get(node);
for (int ix = 0; ix < is.size(); ++ix) {
if (is.get(ix) == neighbor) {
return ix;
}
}
return -1;
}
/**
* Return the number of edges in the graph
*
* @return the number of the edges in the graph
*/
public int getEdgeNum() {
return this.edgeNum;
}
/**
* Return the maximal out degree in the graph
*
* @return the maximal out degree in the graph
*/
public int getMaxOutDeg() {
return maxOutDeg;
}
/**
* Return the number of nodes with out degree of zero
*
* @return the number of nodes with out degree of zero
*/
public int getNumberOfLeaves() {
return this.numOfLeaves;
}
private void calcMaxOutDeg() {
this.maxOutDeg = -1;
for (int i = 0; i < this.outAdjLists.size(); i++) {
if (this.outAdjLists.get(i).size() > maxOutDeg) {
maxOutDeg = this.outAdjLists.get(i).size();
}
}
}
/**
* Return a representation of the graph in Graphviz format
*
* @return String representation of the graph in Graphviz format
*/
public String toDOTFormat() {
StringBuilder sb = new StringBuilder();
sb.append("digraph graphname {\n");
for (int i = 0; i < nodeNum; i++) {
sb.append("n" + i + ";\n");
}
for (int i = 0; i < nodeNum; i++) {
TIntArrayList outList = this.outAdjLists.get(i);
for (int oInd = 0; oInd < outList.size(); oInd++) {
sb.append("n" + i + " -> n" + outList.get(oInd) + ";\n");
}
}
sb.append("}\n");
return sb.toString();
}
}