return count;
}
-static Edge *makeedge(const char *pname, double distance,
- MovFeat *subseg, int movpos) {
+static Edge *edge_create(const char *pname, double distance,
+ MovFeat *subseg, int movpos) {
Edge *e;
int end;
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);
edgeend_attach(tomove,newtarget);
}
-static void edge_delete(Edge *edge) {
- edgeend_detach(&edge->ends[0]);
- edgeend_detach(&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("]");
}
*
* The conversion has two stages:
*
- * FIRST STAGE (movfeatmultedges):
+ * FIRST STAGE (movfeatsplitedges):
*
* We multiply up each edge of any segment with moveable features to
* be an edge which is a position of the *-feature.
* *----A1/P0----* becomes *----A1/P0J0----*
* `---A1/P0J1---'
*
- * SECOND STAGE:
+ * 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
* `---A1/P1J0----*---A1/P1J1--'
*/
-static void movfeatmultedges(void) {
+static void movfeatsplitedges(void) {
Node *node;
int side, l, i, weight, combpos, end;
EdgeEnd *edgeend;
MovFeat *subseg, *starfeature, *loopfeat;
char *featstr;
- trace("movfeatmultedges ...\n");
+ trace("movfeatsplitedges ...\n");
FOR_ALL_NODES(node) {
FOR_EDGES(edge, node,side,edgeend) {
subseg= edge->subseg;
segment= subseg->segment;
- trace("movfeatmultedges "); trace_edge(edge);
+ 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; }
trace(" ...\n");
for (combpos=0; combpos<starfeature->n_positions; combpos++) {
- trace("movfeatmultedges "); trace_combpos(combpos,segment);
+ trace("movfeatsplitedges "); 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);
+ 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(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.
- */
+ edge_delete(edgeend);
}
}
- trace("movfeatmultedges complete.\n");
+ trace("movfeatsplitedges complete.\n");
+}
+
+static int movfeat_isinnernode(Node *node) {
+ int side;
+ EdgeEnd *edgeend;
+ Segment *all_segment, *segment;
+
+ trace_node(node);
+ all_segment= 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 ",segment->segname);
+ return 1;
}
-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.
+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");
+ }
+ 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("%s/*%d",node->pname,edge->movpos));
+ edgeend_replumb(mate, &newnode->sides[mate->node->side]);
+ edgeend_replumb(edgeend, &newnode->sides[edgeend->node->side]);
+ }
+ }
+}
+
+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;
+ Node *node;
FOR_ALL_NODES(node) {
- next_node=node->next;
- trace("elimtrivial node "); trace_node(node); trace(" ");
+ 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");
masprintf("%s+%s", leftedge->edge->pname, 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;
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) {
+ FOR_SIDE_EDGEENDS(edgeend, node,side) {
trace(" consistency "); trace_nodeside(&node->sides[side]);
trace(" "); trace_edgeend(edgeend); trace("\n");
#define OI(x) { #x, x },
OI(printforneato)
OI(consistency)
- OI(movfeatmultedges)
- OI(elimtrivial)
+ OI(movfeatsplitedges)
+ OI(movfeatrmstubs)
+ OI(movfeatsplitnodes)
+ OI(trivpairnodes)
+ OI(trivnullnodes)
{ 0,0 }
};