#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
+#include <errno.h>
#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;
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 (;;) {
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;
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];
}
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);
/*---------- 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; combpos<starfeature->n_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) {
*/
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 &&
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);
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;
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);
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");
#define OI(x) { #x, x },
OI(printforneato)
OI(consistency)
- OI(extendsplits)
+ OI(movfeatmultedges)
OI(elimtrivial)
{ 0,0 }
};