This more or less completes the re-implementation of this class. The

main advantage of what has been done here is that it would now be fairly
easy to return ALL the paths connecting two nodes. In practice, this
would mean that we could find ALL ways of converting one format (say,
LaTeX) into another (say, PDF), and present these to the user for her
perusal.


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@31927 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Richard Heck 2009-11-10 01:18:43 +00:00
parent db42cac3ab
commit bf8f302b64
2 changed files with 122 additions and 51 deletions

View File

@ -3,7 +3,8 @@
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Dekel Tsur
* \author Dekel Tsur (original code)
* \author Richard Heck (re-implementation)
*
* Full author contact details are available in file CREDITS.
*/
@ -13,6 +14,9 @@
#include "Graph.h"
#include "Format.h"
#include "support/debug.h"
#include "support/lassert.h"
#include <algorithm>
using namespace std;
@ -41,6 +45,15 @@ bool Graph::bfs_init(int s, bool clear_visited)
}
void Graph::clearMarks()
{
Arrows::iterator it = arrows_.begin();
Arrows::iterator const en = arrows_.end();
for (; it != en; ++it)
it->marked = false;
}
vector<int> const
Graph::getReachableTo(int target, bool clear_visited)
{
@ -60,10 +73,10 @@ vector<int> const
if (current != target || formats.get(target).name() != "lyx")
result.push_back(current);
vector<Arrow>::iterator it = vertices_[current].in_arrows.begin();
vector<Arrow>::iterator const end = vertices_[current].in_arrows.end();
vector<Arrow *>::iterator it = vertices_[current].in_arrows.begin();
vector<Arrow *>::iterator const end = vertices_[current].in_arrows.end();
for (; it != end; ++it) {
const int cv = it->vertex;
const int cv = (*it)->from;
if (!vertices_[cv].visited) {
vertices_[cv].visited = true;
Q_.push(cv);
@ -96,12 +109,12 @@ vector<int> const
result.push_back(current);
}
vector<Arrow>::const_iterator cit =
vector<Arrow *>::const_iterator cit =
vertices_[current].out_arrows.begin();
vector<Arrow>::const_iterator end =
vector<Arrow *>::const_iterator end =
vertices_[current].out_arrows.end();
for (; cit != end; ++cit) {
int const cv = cit->vertex;
int const cv = (*cit)->to;
if (!vertices_[cv].visited) {
vertices_[cv].visited = true;
Q_.push(cv);
@ -127,12 +140,12 @@ bool Graph::isReachable(int from, int to)
if (current == to)
return true;
vector<Arrow>::const_iterator cit =
vector<Arrow *>::const_iterator cit =
vertices_[current].out_arrows.begin();
vector<Arrow>::const_iterator end =
vector<Arrow *>::const_iterator end =
vertices_[current].out_arrows.end();
for (; cit != end; ++cit) {
int const cv = cit->vertex;
int const cv = (*cit)->to;
if (!vertices_[cv].visited) {
vertices_[cv].visited = true;
Q_.push(cv);
@ -153,29 +166,29 @@ Graph::EdgePath const Graph::getPath(int from, int to)
if (to < 0 || !bfs_init(from))
return path;
// pair<vertex, edge>
vector<pair<int, int> > prev(vertices_.size());
// In effect, the way this works is that we construct a sub-graph
// by starting at "from" and following the arrows outward. Instead
// of actually constructing a sub-graph, though, we "mark" the
// arrows we traverse as we go. Once we hit "to", we abort the
// marking process and then call getMarkedPath() to reconstruct
// the marked path.
bool found = false;
clearMarks();
while (!Q_.empty()) {
int const current = Q_.front();
Q_.pop();
vector<Arrow>::const_iterator const beg =
vector<Arrow *>::const_iterator const beg =
vertices_[current].out_arrows.begin();
vector<Arrow>::const_iterator cit = beg;
vector<Arrow>::const_iterator end =
vector<Arrow *>::const_iterator cit = beg;
vector<Arrow *>::const_iterator end =
vertices_[current].out_arrows.end();
for (; cit != end; ++cit) {
int const cv = cit->vertex;
int const cv = (*cit)->to;
if (!vertices_[cv].visited) {
vertices_[cv].visited = true;
Q_.push(cv);
// FIXME This will not do for finding multiple paths.
// Perhaps we need a vector, or a set. We'll also want
// to add this info, even if the node is visited, so
// outside this conditional.
prev[cv] = pair<int, int>(current, cit->edge);
(*cit)->marked = true;
}
if (cv == to) {
found = true;
@ -186,27 +199,53 @@ Graph::EdgePath const Graph::getPath(int from, int to)
if (!found)
return path;
while (to != from) {
path.push_back(prev[to].second);
to = prev[to].first;
}
reverse(path.begin(), path.end());
getMarkedPath(from, to, path);
return path;
}
// We assume we have marked the graph, as in getPath(). We also
// assume that we have done so in such a way as to guarantee a
// marked path from "from" to "to".
// We then start at "to" and find the arrow leading to it that
// has been marked. We add that to the path we are constructing,
// step back on that arrow, and continue the process (i.e., recurse).
void Graph::getMarkedPath(int from, int to, EdgePath & path) {
if (from == to) {
reverse(path.begin(), path.end());
return;
}
// find marked in_arrow
vector<Arrow *>::const_iterator it = vertices_[to].in_arrows.begin();
vector<Arrow *>::const_iterator en = vertices_[to].in_arrows.end();
for (; it != en; ++it)
if ((*it)->marked)
break;
if (it == en) {
LASSERT(false, /* */);
return;
}
int const newnode = (*it)->from;
path.push_back(newnode);
getMarkedPath(from, newnode, path);
}
void Graph::init(int size)
{
vertices_ = vector<Vertex>(size);
arrows_.clear();
numedges_ = 0;
}
void Graph::addEdge(int from, int to)
{
vertices_[to].in_arrows.push_back(Arrow(from, numedges_));
vertices_[from].out_arrows.push_back(Arrow(to, numedges_));
++numedges_;
arrows_.push_back(Arrow(from, to, numedges_));
numedges_++;
Arrow * ar = &(arrows_.back());
vertices_[to].in_arrows.push_back(ar);
vertices_[from].out_arrows.push_back(ar);
}

View File

@ -4,7 +4,8 @@
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Dekel Tsur
* \author Dekel Tsur (original code)
* \author Richard Heck (re-implementation)
*
* Full author contact details are available in file CREDITS.
*/
@ -12,6 +13,7 @@
#ifndef GRAPH_H
#define GRAPH_H
#include <list>
#include <queue>
#include <vector>
@ -31,44 +33,74 @@ public:
/// \return a vector of the vertices that can be reached from "from"
std::vector<int> const
getReachable(int from, bool only_viewable, bool clear_visited);
/// Can "from" be reached from "to"?
/// can "from" be reached from "to"?
bool isReachable(int from, int to);
/// Find a path from "from" to "to".
/// find a path from "from" to "to". always returns one of the
/// shortest such paths.
EdgePath const getPath(int from, int to);
/// Called repeatedly to build the graph.
/// called repeatedly to build the graph
void addEdge(int from, int to);
/// Reset the internal data structures.
/// reset the internal data structures
void init(int size);
private:
///
bool bfs_init(int, bool clear_visited = true);
///
/// clears the "marks" on the arrows. should be called
/// before any new marking begins.
void clearMarks();
/// used to recover a marked path
void getMarkedPath(int from, int to, EdgePath & path);
/// these represent the arrows connecting the nodes of the graph.
/// this is the basic representation of the graph: as a bunch of
/// arrows.
struct Arrow {
///
Arrow(int v, int e): vertex(v), edge(e) {}
///
int vertex;
///
int edge;
Arrow(int f, int t, int i):
from(f), to(t), id(i), marked(false) {}
/// the vertex at the tail of the arrow
int from;
/// the vertex at the head
int to;
/// an id for this arrow, e.g., for use in describing paths
/// through the graph
int id;
/// used for "marking" paths, i.e., constructing sub-graphs
/// without really doing so.
bool marked;
};
///
/// a container for the arrows
/// we use a list because we want pointers to the arrows,
/// and a vector might invalidate them
typedef std::list<Arrow> Arrows;
Arrows arrows_;
/// Represents a vertex of the graph. Note that we could recover
/// the in_arrows and out_arrows from the Arrows, so these are in
/// effect a kind of cache.
struct Vertex {
/// vertices that point at this one
std::vector<Arrow> in_arrows;
/// paths out from here
std::vector<Arrow> out_arrows;
///
/// arrows that point at this one
std::vector<Arrow *> in_arrows;
/// arrows out from here
std::vector<Arrow *> out_arrows;
/// used in the search routines
bool visited;
};
///
/// a container for the vertices
/// the index into the vector functions as the identifier by which
/// these are referenced in the Arrow struct
/// the code making use of the Graph must keep track of the relation
/// between these indices and the objects they represent. (in the case
/// of Format, this is easy, since the Format objects already have ints
/// as identifiers.)
std::vector<Vertex> vertices_;
///
std::queue<int> Q_;
/// a counter that we use to assign id's to the arrows
/// FIXME This technique assumes a correspondence between the
/// ids of the arrows and ids associated with Converters that
/// seems kind of fragile. Perhaps a better solution would be
/// to pass the ids as we create the arrows.
int numedges_;
};