chiark / gitweb /
movfeatmultedges seems to work
authorian <ian>
Sun, 20 Mar 2005 16:37:37 +0000 (16:37 +0000)
committerian <ian>
Sun, 20 Mar 2005 16:37:37 +0000 (16:37 +0000)
layout/extractgraph
layout/graph-data.h
layout/redactgraph.c

index dd047cce1cb426ddbfcaf4e241a93d1fcc6e22c2..52f66d8f3a50bea7c91797c7f91ac6edbe8d6d75 100755 (executable)
@@ -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};
        }
index eaafde65a05e14a3fea7d6332f3e7306de632458..37b860ec707a778fab3dc6a5ff2901cb724fe8dc 100644 (file)
@@ -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)
+ *       <statement>|<block>
+ *   }
+ * 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*/
index 6f50fbb100d60f0c4d653ddf11f37693e0abf730..004f3e21210204e671ef9a3ff726e1f8975ee724 100644 (file)
@@ -5,12 +5,64 @@
 #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;
@@ -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; 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) {
@@ -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 }
 };