mirror of
git://projects.qi-hardware.com/fped.git
synced 2024-11-18 11:30:18 +02:00
bc27b094af
the topological sort. "make test" or "make tests" invokes the regression tests, "make valgrind" runs them under valgrind's watchful eyes. - fped.c (usage, main): added option -T to force batch mode (for regression testing) - Makefile, test/Common: added regression test infrastructure - test/tsort: test cases for the topological sort - README: added pointer to test/tsort git-svn-id: http://svn.openmoko.org/trunk/eda/fped@5943 99fdad57-331a-0410-800a-d7fa5415bdb3
163 lines
4.0 KiB
C
163 lines
4.0 KiB
C
/*
|
|
* 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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
|
|
#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) {
|
|
fprintf(stderr, "cycle detected in partial order\n");
|
|
abort();
|
|
}
|
|
free(tsort);
|
|
res[n] = NULL;
|
|
return res;
|
|
}
|