From: ian Date: Sun, 20 Mar 2005 16:37:37 +0000 (+0000) Subject: movfeatmultedges seems to work X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ijackson/git?a=commitdiff_plain;h=07c4955ec30de48c9a4a7bb4f9442ce9add2c236;p=trains.git movfeatmultedges seems to work --- diff --git a/layout/extractgraph b/layout/extractgraph index dd047cc..52f66d8 100755 --- a/layout/extractgraph +++ b/layout/extractgraph @@ -251,7 +251,7 @@ sub writeout () { o("\n"); for ($i=0; $i<@nodes; $i++) { $node= $nodes[$i]; - o("static Node ",pr(Node,$node),"= { $i,\n"); + o("static Node ",pr(Node,$node),"= { \"$i\",\n"); o(" ".($i>0 ? '&'.pr(Node,$nodes[$i-1]) : '0'). ", ".($i<$#nodes ? '&'.pr(Node,$nodes[$i+1]) : '0')); o(", {"); @@ -276,7 +276,7 @@ sub writeout () { $maxedgenum=-1; for ($i=0; $i<@edges; $i++) { $edge= $edges[$i]; - o("static Edge ",pr(Edge,$edge),"= { $edge->{EdgeNum},\n"); + o("static Edge ",pr(Edge,$edge),"= { \"$edge->{EdgeNum}\",\n"); if ($edge->{EdgeNum} > $maxedgenum) { $maxedgenum= $edge->{EdgeNum}; } diff --git a/layout/graph-data.h b/layout/graph-data.h index eaafde6..37b860e 100644 --- a/layout/graph-data.h +++ b/layout/graph-data.h @@ -17,6 +17,7 @@ struct Segment { const char *segname; /* 0 if unknown (usually elided by extractgraph) */ int n_movfeats; MovFeat *movfeats; /* [0] is fixed */ + MovFeat *starfeature; /* set by movfeatmultedges */ }; struct MovFeat { @@ -33,7 +34,7 @@ struct EdgeEnd { }; struct Edge { - int edgenum; + const char *pname; double distance; MovFeat *subseg; int movpos; /* 0 if fixed */ @@ -47,7 +48,7 @@ struct NodeSide { }; struct Node { - int nodenum; + const char *pname; Node *back, *next; NodeSide sides[2]; }; @@ -60,4 +61,27 @@ extern NodeList all_nodes; extern int next_nodenum, next_edgenum; +#define FOR_ALL_NODES(node) for (node=all_nodes.head; node; node=node->next) +#define FOR_BOTH(sideend) for (sideend=0; sideend<2; sideend++) + +#define FOR_SEGMENT_MOVFEATS(i,f, segment) \ + for (i=0, f=segment->movfeats; i < segment->n_movfeats; i++, f++) + +/* Iteration over edges: + * FOR_ALL_NODES(node) { + * FOR_EDGES(edge, node,side,edgeend) + * | + * } + * arranges execute statement/block once for each edge, setting edge + * appropriately. edgeend must be of type EdgeEnd*, and side of type + * int; these are used as working variables by FOR_EDGES. + */ +#define FOR_EDGES(edge, node,side,edgeend) \ + FOR_BOTH(side) \ + for (edgeend=node->sides[side].head; edgeend; edgeend=edgeend->next) \ + if ((edge= edgeend->edge), edgeend->end) { \ + /* do each edge once, from end 0, only */ \ + } else + + #endif /*GRAPH_DATA_H*/ diff --git a/layout/redactgraph.c b/layout/redactgraph.c index 6f50fbb..004f3e2 100644 --- a/layout/redactgraph.c +++ b/layout/redactgraph.c @@ -5,12 +5,64 @@ #include #include #include +#include #include "dlist.h" #include "graph-data.h" +#define MMALLOC(sz) mmalloc(sz,__FILE__,__LINE__) +#define MREALLOC(p,sz) mrealloc(p,sz,__FILE__,__LINE__) + +static void *mmalloccheck(void *p, const char *op, size_t sz, + const char *file, int line) { + if (p) return p; + fprintf(stderr,"%s:%d: %s of %lu failed: %s\n", + file, line, op, (unsigned long)sz, strerror(errno)); + exit(16); +} + +static void *mmalloc(size_t sz, const char *file, int line) { + void *p; + if (!sz) return 0; + p= malloc(sz); + return mmalloccheck(p,"malloc",sz,file,line); +} + +static void *mrealloc(void *p, size_t sz, const char *file, int line) { + p= realloc(p,sz); + return mmalloccheck(p,"realloc",sz,file,line); +} + +static char *mvasprintf(const char *fmt, va_list al) + __attribute__((format(printf,1,0))); +static char *mvasprintf(const char *fmt, va_list al) { + char *p; + int r; + + r= vasprintf(&p,fmt,al); + if (r<0) { + fprintf(stderr,"vasprintf(\"%s\",...) failed: %s\n", + fmt, strerror(errno)); + exit(16); + } + return p; +} + +static char *masprintf(const char *fmt, ...) + __attribute__((format(printf,1,2))); +static char *masprintf(const char *fmt, ...) { + va_list al; + char *p; + va_start(al,fmt); + p= mvasprintf(fmt,al); + va_end(al); + return p; +} + /*---------- output processing ----------*/ +static void voutputstuff(int trace, const char *fmt, va_list al) + __attribute__((format(printf,2,0))); static void voutputstuff(int trace, const char *fmt, va_list al) { static struct OutputBuf { int used, allocd; @@ -19,10 +71,10 @@ static void voutputstuff(int trace, const char *fmt, va_list al) { struct OutputBuf *b; char *s, *p, *newline; - int r, l; + int l; b= &bufs[trace]; - r= vasprintf(&s,fmt,al); if (r<0) { perror("vasprintf"); exit(16); } + s= mvasprintf(fmt,al); p= s; for (;;) { @@ -31,8 +83,7 @@ static void voutputstuff(int trace, const char *fmt, va_list al) { l= strlen(p); if (l+b->used > b->allocd) { b->allocd= l+b->used; - b->buf= realloc(b->buf, b->allocd); - if (!b->buf) { perror("realloc voutputstuff"); exit(16); } + b->buf= MREALLOC(b->buf, b->allocd); } memcpy(b->buf+b->used, p, l); b->used += l; @@ -52,29 +103,55 @@ static void voutputstuff(int trace, const char *fmt, va_list al) { free(s); } -#define OF(fn,tr) \ - static void v##fn(const char *fmt, va_list al) { \ - voutputstuff(tr,fmt,al); \ - } \ - static void fn(const char *fmt, ...) { \ - va_list al; \ - va_start(al,fmt); \ - v##fn(fmt,al); \ - va_end(al); \ +#define OF(fn,tr) \ + static void v##fn(const char *fmt, va_list al) \ + __attribute__((format(printf,1,0))); \ + static void v##fn(const char *fmt, va_list al) { \ + voutputstuff(tr,fmt,al); \ + } \ + static void fn(const char *fmt, ...) __attribute__((format(printf,1,2))); \ + static void fn(const char *fmt, ...) { \ + va_list al; \ + va_start(al,fmt); \ + v##fn(fmt,al); \ + va_end(al); \ } OF(output,0) OF(trace,1) -/*---------- general graph-manipulation and printing stuff ----------*/ +/*---------- traceing of various things ----------*/ static void trace_node(Node *node) { - trace("node%d",node->nodenum); + trace("n%s",node->pname); +} + +static void trace_nodeside(NodeSide *node) { + trace("n%s %c",node->node->pname,"OI"[node->side]); +} + +static void trace_edge(Edge *edge) { + trace("e%s",edge->pname); } static void trace_edgeend(EdgeEnd *edge) { - trace("edge%d %c",edge->edge->edgenum,"OI"[edge->end]); + trace("e%s %c",edge->edge->pname,"OI"[edge->end]); } +static void trace_combpos(int combpos, Segment *seg) { + int i, weight; + MovFeat *f; + + trace("*"); + weight= 1; + FOR_SEGMENT_MOVFEATS(i,f,seg) { + if (!f->movfeat) continue; + trace("%s%d", f->movfeat, (combpos / weight) % f->n_positions); + weight *= f->n_positions; + } +} + +/*---------- general graph-manipulation tools ----------*/ + static EdgeEnd *edgeend_otherend(EdgeEnd *thisend) { return &thisend->edge->ends[!thisend->end]; } @@ -89,22 +166,49 @@ static int count_edges(const NodeSide *node) { return count; } -static void edgeend_disconnect(EdgeEnd *toremove) { - LIST_UNLINK(*toremove->node, toremove); +static Edge *makeedge(const char *pname, double distance, + MovFeat *subseg, int movpos) { + Edge *e; + int end; + + e= MMALLOC(sizeof(*e)); + e->pname= pname; + e->distance= distance; + e->subseg= subseg; + e->movpos= movpos; + FOR_BOTH(end) { + e->ends[end].edge= e; + e->ends[end].end= end; + } + trace("[new:"); trace_edge(e); trace("]"); + return e; } + +static void edgeend_detach(EdgeEnd *toremove) { + trace("[detach:"); trace_edgeend(toremove); + trace("-"); trace_nodeside(toremove->node); trace("]"); + LIST_UNLINK(*toremove->node, toremove); +} +static void edgeend_attach(EdgeEnd *toattach, NodeSide *newtarget) { + trace("[attach:"); trace_edgeend(toattach); + trace("-"); trace_nodeside(newtarget); trace("]"); + LIST_LINK_TAIL(*newtarget, toattach); + toattach->node= newtarget; +} static void edgeend_replumb(EdgeEnd *tomove, NodeSide *newtarget) { - edgeend_disconnect(tomove); - LIST_LINK_TAIL(*newtarget, tomove); - tomove->node= newtarget; + edgeend_detach(tomove); + edgeend_attach(tomove,newtarget); } static void edge_delete(Edge *edge) { - edgeend_disconnect(&edge->ends[0]); - edgeend_disconnect(&edge->ends[1]); + edgeend_detach(&edge->ends[0]); + edgeend_detach(&edge->ends[1]); + trace("[delete:"); trace_edge(edge); trace("]"); } static void node_surely_orphaned(Node *node) { + trace("[nodeorphan:"); trace_node(node); trace("]"); assert(!node->sides[0].head); assert(!node->sides[1].head); LIST_UNLINK(all_nodes, node); @@ -112,80 +216,159 @@ static void node_surely_orphaned(Node *node) { /*---------- operations ----------*/ -static void extendsplits(void) { - int pass=0, rightside, n; - Node *node; - EdgeEnd *leftedge, *rightedge; - - /* - * Whenever we have a node which has one or more moveable feature - * subsegments, part of the same moveable feature, on one side, and - * fixed portion of the same segment on the other, we eliminate - * the fixed portion and add its length to both the moveables, - * so that we have one subsegspec for the whole of each position: + /* We combine the individual moveable features in each segment into + * a single `feature' with the cross-product of all of the + * positions. The new feature does not appear in + * Segment->[n_]movfeats, but Edge->subseg and Edge->movpos are + * updated (ie Edge->subseg->segment->movfeats is the original array + * containing only the actual features, and so does not contain + * Edge->subseg). It is often printed as if its name was `*' (which + * is not legal in input files etc.). The positions of the + * *-feature are numbered according to the obvious arithmetic coding + * of the individual actual feature positions. See also + * hostside/safety.h. * - * <---l---> <----l'---> <---------(l+l')------> + * The conversion has two stages: * - * *----A1---*----A1/P0--* becomes *--------A1/P0----------* - * `---A1/P1---* `-------A1/P1-----------* + * FIRST STAGE (movfeatmultedges): * - * <----l''---> <---------(l+l'')------> + * We multiply up each edge of any segment with moveable features to + * be an edge which is a position of the *-feature. + * + * For example, if segment A1 has feature P with states 0 and 1 + * and feature J with states 0 and 1, then the edges are translated + * as follows + * old edge new edges + * A1 A1/P0J0 A1/P0J1 A1/P1J0 A1/P1J1 + * A1/P0 A1/P0J0 A1/P0J1 + * A1/P1 A1/P1J0 A1/P1J1 + * A1/J0 A1/P0J0 A1/P1J0 + * A1/J1 A1/P0J1 A1/P1J1 + * Eg, + * *----A1/P0----* becomes *----A1/P0J0----* + * `---A1/P0J1---' + * + * SECOND STAGE: + * + * We split nodes: every node which has (only) edges which are part + * of the same segment is split into one node for each position and + * direction (directions are end0-to-side0+end1-to-side1 and + * end0-to-side1+end1-to-side0), to which the corresponding edges + * are connected. Any resulting node which has only one edge is + * removed together with the edge. + * + * For example, when we start with: + * + * ,---A1/P0J0---. + * ...*----A1/P0J1----*---A1/P0J1---*... + * `---A1/P1J0---' `--A1/P1J1--' + * `--A1/P1J1--' + * + * the middle node is split like this (neglecting directions for + * clarity): + * + * ,---A1/P0J0----* + * ...*----A1/P0J1----*---A1/P0J1---*... + * `---A1/P1J0----* / + * `--A1/P1J1----*---A1/P1J1-' + * + * and then the stub edges and nodes are removed, leaving this: + * + * ...*----A1/P0J1----*---A1/P0J1---*... + * \ / + * `--A1/P1J1----*---A1/P1J1-' + * + * The actual implementation of the second stage simply considers + * whether to create each node as it goes along, rather than + * inventing nodes merely to delete them. */ - for (;;) { - pass++; - for (node=all_nodes.head; node; node=node->next) { - for (rightside=0; rightside<2; rightside++) { - trace("extendsplit pass %d node ",pass); trace_node(node); - trace("/%d ",rightside); - if ((n= count_edges(&node->sides[!rightside])) != 1) { - trace("no: lhs edges=%d\n", n); - goto no_extend_this; - } - leftedge= node->sides[!rightside].head; - if (leftedge->edge->subseg->movfeat) { - trace("no: lhs moveable\n"); - goto no_extend_this; +static void movfeatmultedges(void) { + Node *node; + int side, l, i, weight, combpos, end; + EdgeEnd *edgeend; + Edge *edge, *newedge; + Segment *segment; + MovFeat *subseg, *starfeature, *loopfeat; + char *featstr; + + trace("movfeatmultedges ...\n"); + FOR_ALL_NODES(node) { + FOR_EDGES(edge, node,side,edgeend) { + subseg= edge->subseg; + segment= subseg->segment; + + trace("movfeatmultedges "); trace_edge(edge); + trace(" segment %s ", segment->segname); + if (segment->n_movfeats<=1) { trace("no moveables\n"); continue; } + if (subseg == segment->starfeature) { trace("*-feature\n"); continue; } + + starfeature= segment->starfeature; + if (starfeature) { + trace("old"); + } else { + trace("new"); + starfeature= MMALLOC(sizeof(*starfeature)); + starfeature->segment= segment; + + l=2; starfeature->n_positions=1; + FOR_SEGMENT_MOVFEATS(i,loopfeat, segment) { + if (!loopfeat->movfeat) continue; + l += strlen(loopfeat->movfeat); + starfeature->n_positions *= loopfeat->n_positions; } - if (!node->sides[rightside].head) { - trace("no: rhs absent\n"); - goto no_extend_this; + + starfeature->movfeat= featstr= MMALLOC(l); + strcpy(featstr, "*"); + + FOR_SEGMENT_MOVFEATS(i,loopfeat, segment) { + if (!loopfeat->movfeat) continue; + strcat(featstr, loopfeat->movfeat); } - for (rightedge= node->sides[rightside].head; - rightedge; - rightedge= rightedge->next) { - if (!rightedge->edge->subseg->movfeat) { - trace("no: rhs fixed ("); trace_edgeend(rightedge); trace(")\n"); - goto no_extend_this; - } - if (leftedge->edge->subseg->segment != - rightedge->edge->subseg->segment) { - trace("no: lhs seg %s, rhs seg %s (", - leftedge->edge->subseg->segment->segname, - rightedge->edge->subseg->segment->segname); - trace_edgeend(rightedge); trace(")\n"); - goto no_extend_this; - } + + segment->starfeature= starfeature; + } + trace(" %s(%d) ", starfeature->movfeat, starfeature->n_positions); + + if (subseg->movfeat) { + weight= 1; + FOR_SEGMENT_MOVFEATS(i,loopfeat, segment) { + if (loopfeat == subseg) goto found; + weight *= loopfeat->n_positions; } - goto extend_this; + abort(); + found: + trace("%s(x%d)",subseg->movfeat,weight); + } else { + weight= 1; + trace("fixed"); + } - no_extend_this:; + trace(" ...\n"); + + for (combpos=0; combposn_positions; combpos++) { + trace("movfeatmultedges "); trace_combpos(combpos,segment); + if ((combpos / weight) % subseg->n_positions != edge->movpos) { + trace(" excluded\n"); + continue; + } + newedge= makeedge(masprintf("%s/*%d",edge->pname,combpos), + edge->distance, + starfeature, + combpos); + FOR_BOTH(end) + edgeend_attach(&newedge->ends[end], edge->ends[end].node); } + edge_delete(edge); + /* edge_delete doesn't destroy or modify this edge node, + * so we can safely carry on with this loop, which will + * now carry on with the next edge - either one we've just + * added, or the one which was next before we started messing + * about. + */ } - return; - - extend_this: - trace("yes, lhs "); trace_edgeend(leftedge); trace(":\n"); - for (rightedge= node->sides[rightside].head; - rightedge; - rightedge= rightedge->next) { - rightedge->edge->distance += leftedge->edge->distance; - edgeend_replumb(rightedge, edgeend_otherend(leftedge)->node); - } - edge_delete(leftedge->edge); - node_surely_orphaned(node); - trace(" extendsplit operation complete\n"); } + trace("movfeatmultedges complete.\n"); } static void elimtrivial(void) { @@ -195,8 +378,8 @@ static void elimtrivial(void) { */ EdgeEnd *leftedge, *rightedge; Node *node, *next_node; - - for (node=all_nodes.head; node; node=next_node) { + + FOR_ALL_NODES(node) { next_node=node->next; trace("elimtrivial node "); trace_node(node); trace(" "); if (!(count_edges(&node->sides[0])==1 && @@ -240,17 +423,16 @@ static void check(const char *why) { alloc_edone= used_edone= 0; trace("consistency check (%s) ...\n",why); - for (node=all_nodes.head; node; node=node->next) { - trace(" consistency node%d\n", node->nodenum); + FOR_ALL_NODES(node) { + trace(" consistency "); trace_node(node); trace("\n"); LIST_CHECKNODE(all_nodes,node); - for (side=0; side<2; side++) { - trace(" consistency node%d %c\n", node->nodenum, "OI"[side]); + FOR_BOTH(side) { + trace(" consistency "); trace_nodeside(&node->sides[side]); trace("\n"); assert(node->sides[side].node == node); assert(node->sides[side].side == side); for (edgeend=node->sides[side].head; edgeend; edgeend=edgeend->next) { - trace(" consistency node%d %c ", node->nodenum, "OI"[side]); - trace_edgeend(edgeend); - trace("\n"); + trace(" consistency "); trace_nodeside(&node->sides[side]); + trace(" "); trace_edgeend(edgeend); trace("\n"); edge= edgeend->edge; LIST_CHECKNODE(node->sides[side], edgeend); @@ -258,8 +440,7 @@ static void check(const char *why) { assert(edgeend->node == &node->sides[side]); if (used_edone==alloc_edone) { alloc_edone += 2; alloc_edone *= 2; - edone= realloc(edone, alloc_edone * sizeof(*edone)); - if (!edone) { perror("realloc check edone"); exit(16); } + edone= MREALLOC(edone, alloc_edone * sizeof(*edone)); } for (search_edone= edone; search_edone < edone + used_edone && search_edone->edge != edge; @@ -277,8 +458,8 @@ static void check(const char *why) { for (search_edone= edone; search_edone < edone + used_edone; search_edone++) { - trace("consistency edge%d count=%d\n", - search_edone->edge, search_edone->endsdone); + trace("consistency "); trace_edge(search_edone->edge); + trace(" count=%d\n", search_edone->endsdone); assert(search_edone->endsdone == 2); } trace("consistency check (%s) ok\n",why); @@ -296,32 +477,28 @@ static void printforneato(void) { output("digraph L {\n"); - for (node=all_nodes.head; node; node=node->next) { - output(" n%dO [label=\"%d\"];\n" - " n%dI [label=\"+\"];\n", - node->nodenum, node->nodenum, node->nodenum); - output(" n%dO -> n%dI [len=0.25 arrowsize=0];\n", - node->nodenum, node->nodenum); - for (side=0; side<2; side++) { - for (edgeend=node->sides[side].head; edgeend; edgeend=edgeend->next) { - if (edgeend->end) continue; /* do each edge once, from end 0, only */ - edge= edgeend->edge; - output(" n%d%c -> n%d%c [label=\"%d:", - edge->ends[0].node->node->nodenum, - "OI"[edge->ends[0].node->side], - edge->ends[1].node->node->nodenum, - "OI"[edge->ends[1].node->side], - edge->edgenum); - if (!edge->subseg->segment->segname) { - output("?"); - } else { - output(edge->subseg->segment->segname); - if (edge->subseg->movfeat) { - output("/%s%d",edge->subseg->movfeat,edge->movpos); - } + FOR_ALL_NODES(node) { + output(" n%sO [label=\"%s\"];\n" + " n%sI [label=\"+\"];\n", + node->pname, node->pname, node->pname); + output(" n%sO -> n%sI [len=0.25 arrowsize=0];\n", + node->pname, node->pname); + FOR_EDGES(edge, node,side,edgeend) { + output(" n%s%c -> n%s%c [label=\"%s:", + edge->ends[0].node->node->pname, + "OI"[edge->ends[0].node->side], + edge->ends[1].node->node->pname, + "OI"[edge->ends[1].node->side], + edge->pname); + if (!edge->subseg->segment->segname) { + output("?"); + } else { + output(edge->subseg->segment->segname); + if (edge->subseg->movfeat) { + output("/%s%d",edge->subseg->movfeat,edge->movpos); } - output(":%.2f\"];\n",edge->distance); } + output(":%.2f\"];\n",edge->distance); } } output("}\n"); @@ -336,7 +513,7 @@ static const OpInfo opinfos[]= { #define OI(x) { #x, x }, OI(printforneato) OI(consistency) - OI(extendsplits) + OI(movfeatmultedges) OI(elimtrivial) { 0,0 } };