/*
* tsort.c - Topological sort
*
* Written 2010 by Werner Almesberger
* Copyright 2010 by Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
/*
* We use a slight variation of Kahn's algorithm. The difference is that we add
* a priority. Edges with the highest priority get selected before edges with
* lower priority.
*
* We maintain the initial list of nodes in the order in which they were added.
* Therefore, the first node with inbound edges will always be sorted first.
* E.g., the root frame.
*
* add_node and add_edge can be invoked multiple times with the same
* parameters. In the case of add_node, simply the existing node is returned.
* In the case of add_edge, the new edge's priority is added to the priority of
* the previous edges.
*
* Priority is accumulated in a node until the node is output. If a node has
* the "decay" flag set, it resets the priorities of all other nodes when
* output. E.g., when outputting a vector, all priorities accumulated from
* previous vectors (towards referencing them with ".") lose their effect.
*
* Last but not least, the algorithm is stable: a pre-existing order that
* conflicts neither with the partial order nor the priorities is preserved.
*
* Thus, we have the following sorting criteria, in decreasing importance:
* - the destination if an edge never precedes its origin
* - higher priority comes before lower priority
* - earlier add_node comes before later
*/
#include
#include
#include
#include "util.h"
#include "tsort.h"
struct edge {
struct node *to;
int priority; /* edge priority */
struct edge *next;
};
struct node {
void *user;
struct edge *edges; /* outbound edges */
int incoming; /* number of incoming edges */
int priority; /* cumulative node priority */
int decay; /* all node prio. decay after issuing this */
struct node *next;
};
struct tsort {
struct node *nodes;
struct node **next_node;
int n_nodes;
};
void add_edge(struct node *from, struct node *to, int priority)
{
struct edge **edge;
for (edge = &from->edges; *edge; edge = &(*edge)->next)
if ((*edge)->to == to) {
(*edge)->priority += priority;
return;
}
*edge = alloc_type(struct edge);
(*edge)->to = to;
(*edge)->priority = priority;
(*edge)->next = NULL;
to->incoming++;
}
struct node *add_node(struct tsort *tsort, void *user, int decay)
{
struct node *node;
for (node = tsort->nodes; node; node = node->next)
if (node->user == user)
return node;
node = alloc_type(struct node);
node->user = user;
node->edges = NULL;
node->incoming = 0;
node->priority = 0;
node->decay = decay;
node->next = NULL;
*tsort->next_node = node;
tsort->next_node = &node->next;
tsort->n_nodes++;
return node;
}
struct tsort *begin_tsort(void)
{
struct tsort *tsort;
tsort = alloc_type(struct tsort);
tsort->nodes = NULL;
tsort->next_node = &tsort->nodes;
tsort->n_nodes = 0;
return tsort;
}
void **end_tsort(struct tsort *tsort)
{
struct node **walk, **first, *node;
struct edge *edge;
void **res;
int n = 0;
res = alloc_size(sizeof(void *)*(tsort->n_nodes+1));
while (1) {
first = NULL;
for (walk = &tsort->nodes; *walk; walk = &(*walk)->next) {
if ((*walk)->incoming)
continue;
if (!first || (*first)->priority < (*walk)->priority)
first = walk;
}
if (!first)
break;
if ((*first)->decay)
for (node = tsort->nodes; node; node = node->next)
node->priority = 0;
node = *first;
*first = node->next;
res[n++] = node->user;
while (node->edges) {
edge = node->edges;
edge->to->incoming--;
edge->to->priority += edge->priority;
node->edges = edge->next;
free(edge);
}
free(node);
}
if (tsort->nodes) /* we have at least one cycle */
abort();
free(tsort);
res[n] = NULL;
return res;
}