- /*
- * The loop-detecting algorithm I use here is not quite the same
- * one as I've used in Slant and Loopy. Those two puzzles use a
- * very similar algorithm which works by finding connected
- * components, not of the graph _vertices_, but of the pieces of
- * space in between them. You divide the plane into maximal areas
- * that can't be intersected by a grid edge (faces in Loopy,
- * diamond shapes centred on a grid edge in Slant); you form a dsf
- * over those areas, and unify any pair _not_ separated by a graph
- * edge; then you've identified the connected components of the
- * space, and can now immediately tell whether an edge is part of
- * a loop or not by checking whether the pieces of space on either
- * side of it are in the same component.
- *
- * In Net, this doesn't work reliably, because of the toroidal
- * wrapping mode. A torus has non-trivial homology, which is to
- * say, there can exist a closed loop on its surface which is not
- * the boundary of any proper subset of the torus's area. For
- * example, consider the 'loop' consisting of a straight vertical
- * line going off the top of the grid and coming back on the
- * bottom to join up with itself. This certainly wants to be
- * marked as a loop, but it won't be detected as one by the above
- * algorithm, because all the area of the grid is still connected
- * via the left- and right-hand edges, so the two sides of the
- * loop _are_ in the same equivalence class.
- *
- * The replacement algorithm I use here is also dsf-based, but the
- * dsf is now over _sides of edges_. That is to say, on a general
- * graph, you would have two dsf elements per edge of the graph.
- * The unification rule is: for each vertex, iterate round the
- * edges leaving that vertex in cyclic order, and dsf-unify the
- * _near sides_ of each pair of adjacent edges. The effect of this
- * is to trace round the outside edge of each connected component
- * of the graph (this time of the actual graph, not the space
- * between), so that the outline of each component becomes its own
- * equivalence class. And now, just as before, an edge is part of
- * a loop iff its two sides are not in the same component.
- *
- * This correctly detects even homologically nontrivial loops on a
- * torus, because a torus is still _orientable_ - there's no way
- * that a loop can join back up with itself with the two sides
- * swapped. It would stop working, however, on a Mobius strip or a
- * Klein bottle - so if I ever implement either of those modes for
- * Net, I'll have to revisit this algorithm yet again and probably
- * replace it with a completely general and much more fiddly
- * approach such as Tarjan's bridge-finding algorithm (which is
- * linear-time, but looks to me as if it's going to take more
- * effort to get it working, especially when the graph is
- * represented so unlike an ordinary graph).
- *
- * In Net, the algorithm as I describe it above has to be fiddled
- * with just a little, to deal with the fact that there are two
- * kinds of 'vertex' in the graph - one set at face-centres, and
- * another set at edge-midpoints where two wires either do or do
- * not join. Since those two vertex classes have very different
- * representations in the Net data structure, separate code is
- * needed for them.
- */