/* diGraph - Directed graph routines. */
#include "common.h"
#include "hash.h"
#include "dlist.h"
#include "diGraph.h"

struct diGraph *dgNew()
/* Return a new directed graph object. */
{
struct diGraph *dg;
AllocVar(dg);
dg->nodeHash = newHash(0);
dg->edgeList = newDlList();
return dg;
}

static void dgNodeFree(struct dgNode **pNode)
/* Free a diGraph node. */
{
struct dgNode *node = *pNode;
if (node == NULL)
    return;
slFreeList(&node->nextList);
slFreeList(&node->prevList);
freez(pNode);
}

static void dgNodeFreeList(struct dgNode **pList)
/* Free list of diGraph nodes. */
{
struct dgNode *el, *next;

for (el = *pList; el != NULL; el = next)
    {
    next = el->next;
    dgNodeFree(&el);
    }
*pList = NULL;
}

void dgFree(struct diGraph **pGraph)
/* Free a directed graph. */
{
struct diGraph *dg = *pGraph;
if (dg == NULL)
    return;
freeHash(&dg->nodeHash);
dgNodeFreeList(&dg->nodeList);
freeDlListAndVals(&dg->edgeList);
freez(pGraph);
}


struct dgNode *dgAddNode(struct diGraph *dg, char *name, void *val)
/* Create new node in graph. It's legal (but not efficient) to add
 * a node with the same name and value twice.  It's not legal to
 * add a node with the same name and a different value.  
 * You can pass in NULL for the name in which case the 
 * hexadecimal representation of val will become the name. */
{
struct dgNode *node;
struct hashEl *hel;
struct hash *hash = dg->nodeHash;
char nbuf[17];
static int nameIx = 0;

if (name == NULL)
    {
    sprintf(nbuf, "%p", val);
    name = nbuf;
    }
hel = hashLookup(hash, name);
if (hel != NULL)
    {
    node = hel->val;
    if (node->val != val)
	{
	errAbort("Trying to add node %s with a new value (old %x new %x)",
	    name, node->val, val);
	}
    return node;
    }
AllocVar(node);
hel = hashAdd(hash, name, node);
node->name = hel->name;
node->val = val;
slAddHead(&dg->nodeList, node);
return node;
}

struct dgNode *dgAddNumberedNode(struct diGraph *dg, int id, void *val)
/* Create new node with a number instead of a name. */
{
char buf[16];
sprintf(buf, "%d", id);
return dgAddNode(dg, buf, val);
}

struct dgNode *dgFindNode(struct diGraph *dg, char *name)
/* Find existing node in graph. Return NULL if not in graph. */
{
struct hashEl *hel;
if ((hel = hashLookup(dg->nodeHash, name)) == NULL)
    return NULL;
return hel->val;
}

struct dgNode *dgFindNumberedNode(struct diGraph *dg, int id)
/* Find node given number. */
{
char buf[16];
sprintf(buf, "%d", id);
return dgFindNode(dg, buf);
}


void *dgNodeVal(struct dgNode *node)
/* Return value associated with node. */
{
return node->val;
}

void *dgNodeName(struct dgNode *node)
/* Return name associated with node. */
{
return node->name;
}

int dgNodeNumber(struct dgNode *node)
/* Return number of node.  (Will likely return 0 if node
 * was added with a name rather than a number). */
{
return atoi(node->name);
}

struct dgNodeRef *dgFindNodeInRefList(struct dgNodeRef *refList, struct dgNode *node)
/* Return reference to node if in list, or NULL if not. */
{
struct dgNodeRef *ref;
for (ref = refList; ref != NULL; ref = ref->next)
    if (ref->node == node)
	return ref;
return NULL;
}

struct dgConnection *dgFindNodeInConList(struct dgConnection *conList, struct dgNode *node)
/* Return connection to node if in list, or NULL if not. */
{
struct dgConnection *con;
for (con = conList; con != NULL; con = con->next)
    if (con->node == node)
	return con;
return NULL;
}

struct dgEdge *dgConnect(struct diGraph *dg, struct dgNode *a, struct dgNode *b)
/* Connect node a to node b.  Returns connecting edge. 
 * Not an error to reconnect.  However all connects can 
 * be broken with a single disconnect. */
{
dgConnectWithVal(dg, a, b, NULL);
}

struct dgEdge *dgConnectWithVal(struct diGraph *dg, struct dgNode *a, 
     struct dgNode *b, void *val)
/* Connect node a to node b and put val on edge.  An error to
 * reconnect with a different val. */
{
struct dgConnection *con;
struct dgEdge *edge;
struct dlNode *edgeOnList;

/* Check to see if it's already there. */
if ((con = dgFindNodeInConList(a->nextList, b)) != NULL)
    {
    edge =  con->edgeOnList->val;
    if (val != edge->val)
        warn("Trying to add new value to edge between %s and %s, ignoring",
	   a->name, b->name);
    return edge;
    }
/* Allocate edge and put on list. */
AllocVar(edge);
edge->a = a;
edge->b = b;
edge->val = val;
edgeOnList = dlAddValTail(dg->edgeList, edge);

/* Connect nodes to each other. */
AllocVar(con);
con->node = b;
con->edgeOnList = edgeOnList;
slAddHead(&a->nextList, con);
AllocVar(con);
con->node = a;
con->edgeOnList = edgeOnList;
slAddHead(&b->prevList, con);

return edge;
}

struct dgEdge *dgConnectUnflippable(struct diGraph *dg, struct dgNode *a, struct dgNode *b,
    void *val)
/* Connect a to b with an edge than can't be flipped. */
{
struct dgEdge *edge = dgConnectWithVal(dg, a, b, val);
edge->unflippable = TRUE;
return edge;
}

static struct dlNode *dgRemoveFromConList(struct dgConnection **pConList, 
	struct dgNode *node, struct dgConnection **retCon)
/* Remove reference to node from list. */
{
struct dgConnection *newList = NULL;
struct dgConnection *con, *next;
struct dlNode *edgeOnList = NULL;

for (con = *pConList; con != NULL; con = next)
    {
    next = con->next;
    if (con->node == node)
	{
	edgeOnList = con->edgeOnList;
	if (retCon != NULL)
	    *retCon = con;
	else
	    freeMem(con);
	}
    else
	{
	slAddHead(&newList, con);
	}
    }
slReverse(&newList);
*pConList = newList;
return edgeOnList;
}

void dgDisconnect(struct diGraph *dg, struct dgNode *a, struct dgNode *b)
/* Disconnect nodes a and b. */
{
struct dlNode *edgeInList;
struct dgEdge *edge;

dgRemoveFromConList(&a->nextList, b, NULL);
edgeInList = dgRemoveFromConList(&b->prevList, a, NULL);
if (edgeInList != NULL)
    {
    edge = edgeInList->val;
    dlRemove(edgeInList);
    freeMem(edgeInList);
    freeMem(edge);
    }
}


void dgClearVisitFlags(struct diGraph *dg)
/* Clear out visit flags. */
{
struct dgNode *node;
for (node = dg->nodeList; node != NULL; node = node->next)
    node->visited = FALSE;
}

void dgClearConnFlags(struct diGraph *dg)
/* Clear out connect flags. */
{
struct dgNode *node;
for (node = dg->nodeList; node != NULL; node = node->next)
    node->conn = FALSE;
}

static struct dgNode *rTarget;

static boolean rPathExists(struct dgNode *a)
/* Recursively find if path from a to b exists. */
{
struct dgConnection *ref;
if (a == rTarget)
    return TRUE;
a->visited = TRUE;

for (ref = a->nextList; ref != NULL; ref = ref->next)
    {
    if (!ref->node->visited && rPathExists(ref->node))
	return TRUE;
    }
return FALSE;
}

boolean dgPathExists(struct diGraph *dg, struct dgNode *a, struct dgNode *b)
/* Return TRUE if there's a path from a to b. */
{
rTarget = b;
dgClearVisitFlags(dg);
return rPathExists(a);
}

static int topoIx;

static void rTopoSort(struct dgNode *node)
{
struct dgConnection *ref;
node->visited = TRUE;
for (ref = node->nextList; ref != NULL; ref = ref->next)
    {
    if (!ref->node->visited)
	rTopoSort(ref->node);
    }
node->topoOrder = ++topoIx;
}

void dgTopoSort(struct diGraph *dg)
/* Fill in topological order of nodes. */
{
struct dgNode *node;

topoIx = 0;
dgClearVisitFlags(dg);
for (node = dg->nodeList; node != NULL; node = node->next)
    {
    if (!node->visited)
	rTopoSort(node);
    }
}

static boolean  rHasCycles(struct dgNode *node)
/* Recursively see if has cycles by looking for
 * backwards topoOrder. */
{
struct dgConnection *ref;
struct dgNode *child;

node->visited = TRUE;
for (ref = node->nextList; ref != NULL; ref = ref->next)
    {
    child = ref->node;
    if (child->topoOrder > node->topoOrder)
	return TRUE;
    if (!child->visited)
	if (rHasCycles(child))
	    return TRUE;
    }
return FALSE;
}

boolean dgHasCycles(struct diGraph *dg)
/* Return TRUE if directed graph has cycles. */
{
struct dgNode *node;

dgTopoSort(dg);
dgClearVisitFlags(dg);
for (node = dg->nodeList; node != NULL; node = node->next)
    {
    if (!node->visited)
	if (rHasCycles(node))
	    return TRUE;
    }
return FALSE;
}

struct dgNodeRef *rRefList;
bool rRespectFlipper;			/* Set to TRUE if rFindConnected only to
                                         * consider nodes with values in connections. */

static void rFindConnected(struct dgNode *a)
/* Find all things connected to a directly or not that haven't
 * already been visited and put them on rRefList. */
{
if (!a->conn)
    {
    struct dgNodeRef *ref;
    struct dgConnection *con;
    struct dgEdge *edge;

    AllocVar(ref);
    ref->node = a;
    slAddHead(&rRefList, ref);
    a->conn = TRUE;
    for (con = a->nextList; con != NULL; con = con->next)
	{
	edge = con->edgeOnList->val;
	if (!rRespectFlipper || !edge->unflippable)
	    rFindConnected(con->node);
	}
    for (con = a->prevList; con != NULL; con = con->next)
	{
	edge = con->edgeOnList->val;
	if (!rRespectFlipper || !edge->unflippable)
	    rFindConnected(con->node);
	}
    }
}

struct dgNodeRef *dgFindNextConnected(struct diGraph *dg)
/* Return list of nodes that make up next connected component,
 * or NULL if no more components.  slFreeList this when
 * done.  Call "dgClearConnFlags" before first call to this.
 * Do not call dgFindConnectedToNode between dgFindFirstConnected 
 * and this as they use the same flag variables to keep track of
 * what vertices are used. */
{
struct dgNode *a;

for (a=dg->nodeList; a != NULL; a = a->next)
    {
    if (!a->conn)
	break;
    }
if (a == NULL)
    return NULL;
rRefList = NULL;
rRespectFlipper = FALSE;
rFindConnected(a);
return rRefList;
}

struct dgNodeRef *dgFindNextFlippableConnected(struct diGraph *dg)
/* Like dgFindConnected, but only considers graph nodes that
 * are conencted by a flippable edge. */
{
struct dgNode *a;

for (a=dg->nodeList; a != NULL; a = a->next)
    {
    if (!a->conn)
	break;
    }
if (a == NULL)
    return NULL;
rRefList = NULL;
rRespectFlipper = TRUE;
rFindConnected(a);
return rRefList;
}


static int connectedComponents(struct diGraph *dg)
/* Count number of connected components and set component field
 * of each node to reflect which component it is in. */
{
struct dgNodeRef *ref;
int conCount = 0;
struct dgNode *a = dg->nodeList;

dgClearConnFlags(dg);
for (;;)
    {
    for (; a != NULL; a = a->next)
	{
	if (!a->conn)
	    break;
	}
    if (a == NULL)
	break;
    rRefList = NULL;
    rFindConnected(a);
    ++conCount;
    for (ref = rRefList; ref != NULL; ref = ref->next)
	{
	ref->node->component = conCount;
	}
    slFreeList(&rRefList);
    }
return conCount;
}

int dgConnectedComponents(struct diGraph *dg)
/* Count number of connected components and set component field
 * of each node to reflect which component it is in. */
{
rRespectFlipper = FALSE;
connectedComponents(dg);
}

int dgConnectedFlippableComponents(struct diGraph *dg)
/* Count number of connected components and set component field
 * of each node to reflect which component it is in. Only
 * consider components connected by flippable edges. */
{
rRespectFlipper = TRUE;
connectedComponents(dg);
}

struct dgNodeRef *dgFindNewConnected(struct diGraph *dg, struct dgNode *a)
/* Find a connected component guaranteed not to be covered before 
 * including a. */
{
rRefList = NULL;
rRespectFlipper = FALSE;
rFindConnected(a);
return rRefList;
}

struct dgNodeRef *dgFindNewFlippableConnected(struct diGraph *dg, struct dgNode *a)
/* Find a connected component guaranteed not to be covered before 
 * that includes a.  Connected components must be connected by flippable
 * edges. */
{
rRefList = NULL;
rRespectFlipper = TRUE;
rFindConnected(a);
return rRefList;
}


struct dgNodeRef *dgFindConnectedToNode(struct diGraph *dg, struct dgNode *a)
/* Return reference list of all nodes connected to a, including a.
 * slFreeList this list when done. */
{
dgClearConnFlags(dg);
return dgFindNewConnected(dg, a);
}

struct dgEdge *dgDirectlyFollows(struct diGraph *dg, struct dgNode *a, struct dgNode *b)
/* Return TRUE if b directly follows a. */
{
struct dgConnection *con = dgFindNodeInConList(a->nextList, b);
if (con == NULL)
    return NULL;
return con->edgeOnList->val;
}

struct dgNodeRef *dgFindPath(struct diGraph *dg, struct dgNode *a, struct dgNode *b)
/* Find shortest path from a to b.  Return NULL if can't be found. */
{
struct dgNodeRef *refList  = NULL, *ref;
struct dgConnection *con;
struct dgNode *node, *nNode;
struct dlList *fifo;
struct dlNode *ffNode;
struct dgNode endNode;
int fifoSize = 1;

/* Do some quick and easy tests first to return if have no way out
 * of node A, or if B directly follows A. */
if (a->nextList == NULL)
    return NULL;
if (a == b)
    {
    AllocVar(ref);
    ref->node = a;
    return ref;
    }
if ((con = dgFindNodeInConList(a->nextList, b)) != NULL)
    {
    AllocVar(refList);
    refList->node = a;
    node = con->node;
    AllocVar(ref);
    ref->node = node;
    slAddTail(&refList, ref);
    return refList;
    }

/* Set up for breadth first traversal.  Will use a doubly linked
 * list as a fifo. */
for (node = dg->nodeList; node != NULL; node = node->next)
    node->tempEntry = NULL;
fifo = newDlList();
dlAddValTail(fifo, a);
a->tempEntry = &endNode;

while ((ffNode = dlPopHead(fifo)) != NULL)
    {
    --fifoSize;
    node = ffNode->val;
    freeMem(ffNode);
    for (con = node->nextList; con != NULL; con = con->next)
	{
	nNode = con->node;
	if (nNode->tempEntry == NULL)
	    {
	    nNode->tempEntry = node;
	    if (nNode == b)
		{
		while (nNode != &endNode && nNode != NULL)
		    {
		    AllocVar(ref);
		    ref->node = nNode;
		    slAddHead(&refList, ref);
		    nNode = nNode->tempEntry;
		    }
		break;
		}
	    else
		{
		dlAddValTail(fifo, nNode);
		++fifoSize;
		if (fifoSize > 100000)
		    errAbort("Internal error in dgFindPath");
		}
	    }
	}
    }
freeDlList(&fifo);
return refList;
}

static int cmpPriority(const void *va, const void *vb)
/* Sort smallest offset into needle first. */
{
const struct dgNode *a = *((struct dgNode **)va);
const struct dgNode *b = *((struct dgNode **)vb);
return (a->priority - b->priority);
}

boolean dgParentsAllVisited(struct dgNode *node)
/* Return TRUE if all parents of node have  been visited. */
{
struct dgConnection *con;
for (con = node->prevList; con != NULL; con = con->next)
    {
    if (con->node->visited == FALSE)
	return FALSE;
    }
return TRUE;
}

struct dgNodeRef *dgConstrainedPriorityOrder(struct diGraph *dg)
/* Return traversal of graph in priority order subject to
 * constraint that all parents must be output before
 * their children regardless of node priority. 
 * Graph must be cycle free. */
{
struct dlList *sortedList = newDlList();
struct dgNode *graphNode;
struct dlNode *listNode;
struct dgNodeRef *refList = NULL, *ref;

if (dgHasCycles(dg))
    errAbort("Call to dgConstrainedPriorityOrder on graph with cycles.");

/* Make up list sorted by priority. */
for (graphNode = dg->nodeList; graphNode != NULL; graphNode = graphNode->next)
    {
    dlAddValTail(sortedList, graphNode);
    graphNode->visited = FALSE;
    }
dlSort(sortedList, cmpPriority);

/* Loop taking first member of list with no untraversed parents. */
while (!dlEmpty(sortedList))
    {
    for (listNode = sortedList->head; listNode->next != NULL; listNode = listNode->next)
	{
	graphNode = listNode->val;
	if (dgParentsAllVisited(graphNode))
	    {
	    dlRemove(listNode);
	    freeMem(listNode);
	    AllocVar(ref);
	    ref->node = graphNode;
	    slAddHead(&refList, ref);
	    graphNode->visited = TRUE;
	    break;
	    }
	}
    }
freeDlList(&sortedList);
slReverse(&refList);
return refList;
}

struct dgEdgeRef *dgFindSubEdges(struct diGraph *dg, struct dgNodeRef *subGraph,
	boolean onlyFlippable)
/* Return list of edges in graph that connected together nodes in subGraph. 
 * Optionally return only flippable edges. */
{
struct hash *hash = newHash(0);
struct dgNodeRef *nr;
struct dgConnection *con;
struct dgEdgeRef *erList = NULL, *er;
struct dgEdge *edge;
struct dgNode *node;

/* Build up hash of nodes in subGraph. */
for (nr = subGraph; nr != NULL; nr = nr->next)
    {
    node = nr->node;
    hashAdd(hash, node->name, node);
    }

for (nr = subGraph; nr != NULL; nr = nr->next)
    {
    node = nr->node;
    for (con = node->nextList; con != NULL; con = con->next)
	{
	if (hashLookup(hash, con->node->name))
	    {
	    edge = con->edgeOnList->val;
	    if (!onlyFlippable || !edge->unflippable)
		{
		AllocVar(er);
		er->edge = con->edgeOnList->val;
		slAddHead(&erList, er);
		}
	    }
	}
    }
freeHash(&hash);
return erList;
}

void dgSwapEdges(struct diGraph *dg, struct dgEdgeRef *erList)
/* Swap polarity of all edges in erList.  (Assumes you don't have
 * edges going both directions in graph.) */
{
struct dgEdgeRef *er;
struct dgEdge *edge;
struct dgNode *a, *b;
struct dgConnection *con1, *con2;

/* Remove edges from next and previous list of all
 * involved nodes and swap nodes in edge itself. */
for (er = erList; er != NULL; er = er->next)
    {
    edge = er->edge;
    a = edge->a;
    b = edge->b;
    dgRemoveFromConList(&a->nextList, b, &con1);
    dgRemoveFromConList(&b->prevList, a, &con2);
    edge->a = b;
    edge->b = a;
    con1->node = a;
    slAddHead(&b->nextList, con1);
    con2->node = b;
    slAddHead(&a->prevList, con2);
    }
}


struct dgConnection *dgNextList(struct dgNode *node)
/* Return list of nodes that follow node. */
{
return node->nextList;
}

struct dgConnection *dgPrevList(struct dgNode *node)
/* Return list of nodes that precede node. */
{
return node->prevList;
}

void dgDumpGraph(struct diGraph *dg, FILE *out, boolean hideIsolated)
/* Dump info on graph to output. */
{
struct dgNode *node;
struct dgConnection *con;

for (node = dg->nodeList; node != NULL; node = node->next)
    {
    if (hideIsolated  && node->nextList == NULL)
	continue;
    fprintf(out, "%s:", node->name);
    for (con = node->nextList; con != NULL; con = con->next)
	fprintf(out, " %s", con->node->name);
    fprintf(out, "\n");
    }
}


