-/**/
+/*
+ * output format from printforforsafety is lines like this:
+ * segment <node>.<side> <node>.<side> <segment>[/<movinfo>] <distance>
+ * #... (comment)
+ * (blank)
+ * end (at end of file, mandatory)
+ * syntactic restrictions:
+ * <node> is alphanumeric and starts with a letter
+ * <side> is precisely the digit 0 or the digit 1
+ * <segment> is alphanumeric (and comes from the layout)
+ * <movinfo> is <movfeat><movpos>[<movfeat><movpos>...]*<combpos>
+ * where <movfeat> is alphabetic and <movpos> is and
+ * <combpos> are in decimal and have no unnecessary leading 0's
+ * <distance> contains only digits and perhaps decimal point
+ * semantic restrictions (not enforced by redactgraph but ought to
+ * be impossible for layout designer to violate):
+ * Each <segment>[/<movinfo>] appears exactly once.
+ * <segment> appears either once without /<movinfo> or always with.
+ * Each side of each node appears either
+ * - once as the 0th or 1th end of a <segment>
+ * - one or more times as the same end of <segment>/<movinfo>'s with
+ * the same <segment>
+ * - not at all (indicates a terminus)
+ * Refer to safety.h for info regarding <combpos>.
+ *
+ * See layout-data.h comment for info regarding layout data for
+ * control system.
+ */
+/* for debugging, runes like
+ * ./ours.redactgraph consistency movfeatsplitedges consistency movfeatrmstubs consistency movfeatsplitnodes consistency trivpairnodes consistency trivnullnodes consistency printforneato | neato -Tps >u.ps
+ * are often useful.
+ */
#include <stdarg.h>
#include <string.h>
#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("n%p",node);
+ 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("n%p%c",edge->node->node,"OI"[edge->node->side]);
+ 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 *edge_create(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 Node *node_create(const char *pname) {
+ Node *n;
+ int side;
+
+ n= MMALLOC(sizeof(*n));
+ n->pname= pname;
+ FOR_BOTH(side) {
+ n->sides[side].node= n;
+ n->sides[side].side= side;
+ LIST_INIT(n->sides[side]);
+ }
+ LIST_LINK_TAIL(all_nodes,n);
+ return n;
}
+/* Perhaps surprisingly, it's OK to remove nodes and edges in loops
+ * made with the macros in graph-data.h, provided that if the
+ * _current_ node or edge is removed, that happens last. This is
+ * because removing a node or edge it doesn't actually free it or
+ * change its contents, so the thing->next is still valid afterwards.
+ * This depends on the fact that we never free anything we're done
+ * with !
+ */
+
+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]);
+static void edge_delete(EdgeEnd *detachlast) {
+ Edge *edge;
+ edge= detachlast->edge;
+ edgeend_detach(&edge->ends[!detachlast->end]);
+ edgeend_detach(detachlast);
+ 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, 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:
+/*---------- operations - heavyweight graph redaction ----------*/
+
+ /* 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 (movfeatsplitedges):
*
- * <----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 (movfeatrmstubs):
+ *
+ * We remove stub moveable edges: whenever, at a node which has only
+ * edges which are part of the same segment, we find an edge on one
+ * side in which is not matched exactly (moveable feature settings
+ * and direction) by an edge on the other side, we delete that extra
+ * edge:
+ *
+ * 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 (neglecting direction for the purposes of the
+ * example), when we start with:
+ *
+ * ,---A1/P0J0---.
+ * ...*----A1/P0J1----*---A1/P0J1---*...
+ * `---A1/P1J0---' `--A1/P1J1--'
+ * `--A1/P1J1--'
+ *
+ * the edges A1/P0J0 and A1/P1J1 are not matched on the RHS of the node,
+ * so they are removed:
+ *
+ * ...*----A1/P0J1----*---A1/P0J1---*...
+ * `---A1/P1J0---' `--A1/P1J1--'
+ *
+ *
+ * THIRD STAGE:
+ *
+ * We split nodes: every node which has only edges which are part of
+ * the same segment is split into one node for each kind of edge
+ * which goes through it. The 2nd stage ensures that this is always
+ * straightforward.
+ *
+ * Our example above turns into:
+ *
+ * ...*----A1/P0J1----*---A1/P0J1---*...
+ * `---A1/P1J0----*---A1/P1J1--'
*/
- 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;
+static void movfeatsplitedges(void) {
+ Node *node;
+ int side, l, i, weight, combpos, end;
+ EdgeEnd *edgeend;
+ Edge *edge, *newedge;
+ Segment *segment;
+ MovFeat *subseg, *starfeature, *loopfeat;
+ char *featstr;
+
+ trace("movfeatsplitedges ...\n");
+ FOR_ALL_NODES(node) {
+ FOR_EDGES(edge, node,side,edgeend) {
+ subseg= edge->subseg;
+ segment= subseg->segment;
+
+ trace("movfeatsplitedges "); 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;
}
- leftedge= node->sides[!rightside].head;
- if (leftedge->edge->subseg->movfeat) {
- trace("no: lhs moveable\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);
}
- if (!node->sides[rightside].head) {
- trace("no: rhs absent\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;
}
- 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;
- }
+ abort();
+ found:
+ trace("%s(x%d)",subseg->movfeat,weight);
+ } else {
+ weight= 1;
+ trace("fixed");
+ }
+
+ trace(" ...\n");
+
+ for (combpos=0; combpos<starfeature->n_positions; combpos++) {
+ trace("movfeatsplitedges "); trace_combpos(combpos,segment);
+ if ((combpos / weight) % subseg->n_positions != edge->movpos) {
+ trace(" excluded\n");
+ continue;
}
- goto extend_this;
+ newedge= edge_create(masprintf("%s/*%d",edge->pname,combpos),
+ edge->distance,
+ starfeature,
+ combpos);
+ FOR_BOTH(end)
+ edgeend_attach(&newedge->ends[end], edge->ends[end].node);
+ }
+ edge_delete(edgeend);
+ }
+ }
+ trace("movfeatsplitedges complete.\n");
+}
- no_extend_this:;
+static int movfeat_isinnernode(Node *node) {
+ int side;
+ EdgeEnd *edgeend;
+ Segment *all_segment, *segment;
+
+ trace_node(node);
+ all_segment= 0;
+ FOR_BOTH(side) {
+ if (!node->sides[side].head) {
+ trace(" %c terminus\n", "OI"[side]);
+ return 0;
+ }
+ }
+ FOR_NODE_EDGEENDS(side,edgeend, node) {
+ segment= edgeend->edge->subseg->segment;
+ if (!all_segment) {
+ all_segment= segment;
+ } else if (all_segment != segment) {
+ trace(" not inner: %s and %s\n",
+ all_segment->segname, segment->segname);
+ return 0;
+ }
+ }
+ if (!all_segment) {
+ trace(" not inner: no edges?!\n");
+ return 0;
+ }
+ trace(" inner %s ",all_segment->segname);
+ return 1;
+}
+
+static EdgeEnd *movfeat_findmate(EdgeEnd *key) {
+ Node *node;
+ EdgeEnd *mate;
+ int mateside;
+
+ node= key->node->node;
+ mateside= !key->node->side;
+
+ FOR_SIDE_EDGEENDS(mate, node,mateside) {
+ if (key->edge->subseg == mate->edge->subseg &&
+ key->edge->movpos == mate->edge->movpos &&
+ key->end != mate->end)
+ return mate;
+ }
+ return 0; /* no mate - must be a stub */
+}
+
+static void movfeatrmstubs(void) {
+ int pass, anychange, side;
+ Node *node;
+ Edge *edge;
+ EdgeEnd *edgeend, *mate;
+
+ pass= 0;
+ do {
+ anychange= 0;
+ pass++;
+ trace("movfeatrmstubs pass %d ...\n", pass);
+ FOR_ALL_NODES(node) {
+ trace("movfeatrmstubs ");
+ if (!movfeat_isinnernode(node)) continue;
+ FOR_NODE_EDGEENDS(side,edgeend, node) {
+ edge= edgeend->edge;
+ mate= movfeat_findmate(edgeend);
+ if (mate) {
+ trace("("); trace_edgeend(edgeend); trace(")");
+ continue;
+ }
+ edge_delete(edgeend);
+ anychange++;
}
+ trace("\n");
}
- 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);
+ trace("movfeatrmstubs pass %d %s\n", pass, anychange?"changed":"alldone");
+ } while (anychange);
+}
+
+static void movfeatsplitnodes(void) {
+ Node *node, *newnode;
+ EdgeEnd *edgeend, *mate;
+ Edge *edge;
+ int side;
+
+ FOR_ALL_NODES(node) {
+ trace("movfeatsplitnodes ");
+ if (!movfeat_isinnernode(node)) continue;
+ if (!node->sides[0].head->next) {
+ trace("pair-trivial\n");
+ continue;
+ }
+ FOR_NODE_EDGEENDS(side,edgeend, node) {
+ edge= edgeend->edge;
+ mate= movfeat_findmate(edgeend);
+ assert(mate);
+ newnode= node_create(masprintf("%sx%d",node->pname,edge->movpos));
+ edgeend_replumb(mate, &newnode->sides[mate->node->side]);
+ edgeend_replumb(edgeend, &newnode->sides[edgeend->node->side]);
}
- edge_delete(leftedge->edge);
- node_surely_orphaned(node);
- trace(" extendsplit operation complete\n");
}
}
-static void elimtrivial(void) {
- /* Eliminate trivial nodes: ones which have only two edges, which
- * come in on opposite sides, have the same subsegspec, and with the
- * two ends identically aligned.
+/*---------- operations - trivial graph redaction etc. ----------*/
+
+static void trivpairnodes(void) {
+ /* Eliminate near-trivial nodes - in this case, ones which have only
+ * two edges, which come in on opposite sides, have the same
+ * subsegspec, and with the two ends identically aligned.
*/
EdgeEnd *leftedge, *rightedge;
- Node *node, *next_node;
-
- for (node=all_nodes.head; node; node=next_node) {
- next_node=node->next;
- trace("elimtrivial node "); trace_node(node); trace(" ");
+ Node *node;
+
+ FOR_ALL_NODES(node) {
+ trace("trivpairnodes node "); trace_node(node); trace(" ");
if (!(count_edges(&node->sides[0])==1 &&
count_edges(&node->sides[1])==1)) {
trace("no, a non-unitary side\n");
continue;
}
leftedge= node->sides[0].head;
- rightedge= node->sides[0].head;
+ rightedge= node->sides[1].head;
if (leftedge->edge->subseg != rightedge->edge->subseg) {
trace("no, (sub)segments differ\n");
continue;
continue;
}
trace(" yes:\n");
+ if (!strchr(rightedge->edge->pname, '+')) {
+ rightedge->edge->pname=
+ masprintf("%s+", rightedge->edge->pname);
+ }
rightedge->edge->distance += leftedge->edge->distance;
edgeend_replumb(rightedge, edgeend_otherend(leftedge)->node);
- edge_delete(leftedge->edge);
+ edge_delete(leftedge);
+ trace(" trivpairnodes operation complete\n");
+ }
+}
+
+static void trivnullnodes(void) {
+ /* Eliminate trivial nodes - ones which have no edges. */
+ Node *node;
+ int side;
+
+ trace("trivnullnodes ");
+ FOR_ALL_NODES(node) {
+ FOR_BOTH(side) {
+ if (node->sides[side].head)
+ goto non_null;
+ }
node_surely_orphaned(node);
- trace(" elimtrivial operation complete\n");
+ non_null:;
}
-}
+ trace(" trivnullnodes done.\n");
+}
+
+static void check(const char *why) {
+ Node *node;
+ int alloc_edone, used_edone;
+ typedef struct { Edge *edge; int endsdone; } EdgeDone;
+ EdgeDone *edone, *search_edone;
+ int side;
+ EdgeEnd *edgeend;
+ Edge *edge;
+
+ edone= 0;
+ alloc_edone= used_edone= 0;
+
+ trace("consistency check (%s) ...\n",why);
+ FOR_ALL_NODES(node) {
+ trace(" consistency "); trace_node(node); trace("\n");
+ LIST_CHECKNODE(all_nodes,node);
+ 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_SIDE_EDGEENDS(edgeend, node,side) {
+ trace(" consistency "); trace_nodeside(&node->sides[side]);
+ trace(" "); trace_edgeend(edgeend); trace("\n");
+
+ edge= edgeend->edge;
+ LIST_CHECKNODE(node->sides[side], edgeend);
+ assert(edgeend == &edge->ends[edgeend->end]);
+ assert(edgeend->node == &node->sides[side]);
+ if (used_edone==alloc_edone) {
+ alloc_edone += 2; alloc_edone *= 2;
+ edone= MREALLOC(edone, alloc_edone * sizeof(*edone));
+ }
+ for (search_edone= edone;
+ search_edone < edone + used_edone && search_edone->edge != edge;
+ search_edone++);
+ if (search_edone >= edone + used_edone) {
+ search_edone->edge= edge;
+ search_edone->endsdone= 0;
+ used_edone++;
+ }
+ search_edone->endsdone++;
+ assert(search_edone->endsdone <= 2);
+ }
+ }
+ }
+ for (search_edone= edone;
+ search_edone < edone + used_edone;
+ search_edone++) {
+ 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);
+}
static void consistency(void) {
+ check("command-line request");
}
+/*---------- operations - output ----------*/
+
static void printforneato(void) {
Node *node;
EdgeEnd *edgeend;
output("digraph L {\n");
- for (node=all_nodes.head; node; node=node->next) {
- output(" n%pO [label=\"O\"];\n"
- " n%pI [label=\"+\"];\n",
- node, node);
- output(" n%pO -> n%pI [len=0.25 arrowsize=0];\n",
- node, node);
- 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%p%c -> n%p%c [label=\"",
- edge->ends[0].node->node, "OI"[edge->ends[0].node->side],
- edge->ends[1].node->node, "OI"[edge->ends[1].node->side]);
- 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("\"];\n");
}
+ output(":%.2f\"];\n",edge->distance);
}
}
output("}\n");
}
+static void printforforsafety(void) {
+ Node *node;
+ Edge *edge;
+ EdgeEnd *edgeend;
+ int side, end, weight, i;
+ Segment *segment;
+ MovFeat *movfeat;
+
+ FOR_ALL_NODES(node) {
+ FOR_EDGES(edge, node,side,edgeend) {
+ output("segment ");
+ FOR_BOTH(end) {
+ output("n%s.%d ",
+ edge->ends[end].node->node->pname,
+ edge->ends[end].node->side);
+ }
+ segment= edge->subseg->segment;
+ output(segment->segname);
+ if (segment->n_movfeats>1) {
+ if (!segment->starfeature) {
+ fprintf(stderr,"printforforsafety before movfeatsplitedges!\n");
+ exit(8);
+ }
+ output("/");
+ weight= 1;
+ FOR_SEGMENT_MOVFEATS(i,movfeat, segment) {
+ if (!movfeat->movfeat) continue;
+ output("%s%d", movfeat->movfeat,
+ (edge->movpos / weight) % movfeat->n_positions);
+ weight *= movfeat->n_positions;
+ }
+ output("*%d", edge->movpos);
+ }
+ output(" %f\n", edge->distance);
+ }
+ }
+ output("end\n");
+}
+
+static void printforlayoutsegjoins(void) {
+ Node *node;
+ EdgeEnd *edgeend;
+ Segment **segmentp, *segment;
+ int side;
+
+ FOR_ALL_NODES(node) {
+ output("layer ");
+ FOR_BOTH(side)
+ if (!node->sides[side].head) {
+ output("segterminus");
+ goto one_sided;
+ }
+ output("segjoin");
+ one_sided:
+ output("%d\n", (node->layermin + node->layermax)/2);
+
+ output("abs segjoin_%s",node->pname);
+ FOR_NODE_EDGEENDS(side,edgeend, node)
+ output("_%s",edgeend->edge->subseg->segment->segname);
+ output(" %f %f %f\n", node->x,node->y,node->a);
+ FOR_ALL_SEGMENTS(segmentp,segment) segment->u= 0;
+ FOR_NODE_EDGEENDS(side,edgeend, node) {
+ segment= edgeend->edge->subseg->segment;
+ if (segment->u++) continue;
+ output("segend %s\n",segment->segname);
+ output("segcmap %s 0.5 setgray\n",segment->segname);
+ }
+ output("\n");
+ }
+}
+
+/*---------- main program ----------*/
+
typedef struct {
const char *name;
void (*fn)(void);
static const OpInfo opinfos[]= {
#define OI(x) { #x, x },
OI(printforneato)
+ OI(printforforsafety)
+ OI(printforlayoutsegjoins)
OI(consistency)
- OI(extendsplits)
- OI(elimtrivial)
+ OI(movfeatsplitedges)
+ OI(movfeatrmstubs)
+ OI(movfeatsplitnodes)
+ OI(trivpairnodes)
+ OI(trivnullnodes)
{ 0,0 }
};