]> git.lyx.org Git - features.git/blob - src/Graph.cpp
0320b9bc3482dbde1cdfafd889549282584ebd38
[features.git] / src / Graph.cpp
1 /**
2  * \file Graph.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Dekel Tsur (original code)
7  * \author Richard Heck (re-implementation)
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "Graph.h"
15 #include "Format.h"
16
17 #include "support/debug.h"
18 #include "support/lassert.h"
19
20 #include <algorithm>
21
22 using namespace std;
23
24 namespace lyx {
25
26
27 bool Graph::bfs_init(int s, bool clear_visited)
28 {
29         if (s < 0)
30                 return false;
31
32         Q_ = queue<int>();
33
34         if (clear_visited) {
35                 vector<Vertex>::iterator it = vertices_.begin();
36                 vector<Vertex>::iterator en = vertices_.end();
37                 for (; it != en; ++it)
38                         it->visited = false;
39         }
40         if (!vertices_[s].visited) {
41                 Q_.push(s);
42                 vertices_[s].visited = true;
43         }
44         return true;
45 }
46
47
48 void Graph::clearPaths()
49 {
50         vector<Vertex>::iterator it = vertices_.begin();
51         vector<Vertex>::iterator en = vertices_.end();
52         for (; it != en; ++it)
53                 it->path.clear();
54 }
55
56
57 vector<int> const
58         Graph::getReachableTo(int target, bool clear_visited)
59 {
60         Mutex::Locker lock(&mutex_);
61
62         vector<int> result;
63         if (!bfs_init(target, clear_visited))
64                 return result;
65
66         // Here's the logic, which is shared by the other routines.
67         // Q_ holds a list of nodes we have been able to reach (in this
68         // case, reach backwards). It is initialized to the current node
69         // by bfs_init, and then we recurse, adding the nodes we can reach
70         // from the current node as we go. That makes it a breadth-first
71         // search.
72         while (!Q_.empty()) {
73                 int const current = Q_.front();
74                 Q_.pop();
75                 if (current != target || formats.get(target).name() != "lyx")
76                         result.push_back(current);
77
78                 vector<Arrow *>::iterator it = vertices_[current].in_arrows.begin();
79                 vector<Arrow *>::iterator const end = vertices_[current].in_arrows.end();
80                 for (; it != end; ++it) {
81                         const int cv = (*it)->from;
82                         if (!vertices_[cv].visited) {
83                                 vertices_[cv].visited = true;
84                                 Q_.push(cv);
85                         }
86                 }
87         }
88
89         return result;
90 }
91
92
93 vector<int> const
94         Graph::getReachable(int from, bool only_viewable,
95                 bool clear_visited)
96 {
97         Mutex::Locker lock(&mutex_);
98
99         vector<int> result;
100         if (!bfs_init(from, clear_visited))
101                 return result;
102
103         while (!Q_.empty()) {
104                 int const current = Q_.front();
105                 Q_.pop();
106                 Format const & format = formats.get(current);
107                 if (!only_viewable || !format.viewer().empty())
108                         result.push_back(current);
109                 else if (format.isChildFormat()) {
110                         Format const * const parent =
111                                 formats.getFormat(format.parentFormat());
112                         if (parent && !parent->viewer().empty())
113                                 result.push_back(current);
114                 }
115
116                 vector<Arrow *>::const_iterator cit =
117                         vertices_[current].out_arrows.begin();
118                 vector<Arrow *>::const_iterator end =
119                         vertices_[current].out_arrows.end();
120                 for (; cit != end; ++cit) {
121                         int const cv = (*cit)->to;
122                         if (!vertices_[cv].visited) {
123                                 vertices_[cv].visited = true;
124                                 Q_.push(cv);
125                         }
126                 }
127         }
128
129         return result;
130 }
131
132
133 bool Graph::isReachable(int from, int to)
134 {
135         Mutex::Locker lock(&mutex_);
136
137         if (from == to)
138                 return true;
139
140         if (to < 0 || !bfs_init(from))
141                 return false;
142
143         while (!Q_.empty()) {
144                 int const current = Q_.front();
145                 Q_.pop();
146                 if (current == to)
147                         return true;
148
149                 vector<Arrow *>::const_iterator cit =
150                         vertices_[current].out_arrows.begin();
151                 vector<Arrow *>::const_iterator end =
152                         vertices_[current].out_arrows.end();
153                 for (; cit != end; ++cit) {
154                         int const cv = (*cit)->to;
155                         if (!vertices_[cv].visited) {
156                                 vertices_[cv].visited = true;
157                                 Q_.push(cv);
158                         }
159                 }
160         }
161
162         return false;
163 }
164
165
166 Graph::EdgePath const Graph::getPath(int from, int to)
167 {
168         Mutex::Locker lock(&mutex_);
169
170         static const EdgePath path;
171         if (from == to)
172                 return path;
173
174         if (to < 0 || !bfs_init(from))
175                 return path;
176
177         clearPaths();
178         while (!Q_.empty()) {
179                 int const current = Q_.front();
180                 Q_.pop();
181
182                 vector<Arrow *>::const_iterator cit =
183                         vertices_[current].out_arrows.begin();
184                 vector<Arrow *>::const_iterator end =
185                         vertices_[current].out_arrows.end();
186                 for (; cit != end; ++cit) {
187                         int const cv = (*cit)->to;
188                         if (!vertices_[cv].visited) {
189                                 vertices_[cv].visited = true;
190                                 Q_.push(cv);
191                                 // NOTE If we wanted to collect all the paths, then
192                                 // we just need to collect them here and not worry
193                                 // about "visited".
194                                 EdgePath lastpath = vertices_[(*cit)->from].path;
195                                 lastpath.push_back((*cit)->id);
196                                 vertices_[cv].path = lastpath;
197                         }
198                         if (cv == to) {
199                                 return vertices_[cv].path;
200                         }
201                 }
202         }
203         // failure
204         return path;
205 }
206
207
208 void Graph::init(int size)
209 {
210         Mutex::Locker lock(&mutex_);
211
212         vertices_ = vector<Vertex>(size);
213         arrows_.clear();
214         numedges_ = 0;
215 }
216
217
218 void Graph::addEdge(int from, int to)
219 {
220         Mutex::Locker lock(&mutex_);
221
222         arrows_.push_back(Arrow(from, to, numedges_));
223         numedges_++;
224         Arrow * ar = &(arrows_.back());
225         vertices_[to].in_arrows.push_back(ar);
226         vertices_[from].out_arrows.push_back(ar);
227 }
228
229
230 // At present, we do not need this debugging code, but
231 // I am going to leave it here in case we need it again.
232 #if 0
233 void Graph::dumpGraph() const
234 {
235         vector<Vertex>::const_iterator it = vertices_.begin();
236         vector<Vertex>::const_iterator en = vertices_.end();
237         for (; it != en; ++it) {
238                 LYXERR0("Next vertex...");
239                 LYXERR0("In arrows...");
240                 std::vector<Arrow *>::const_iterator iit = it->in_arrows.begin();
241                 std::vector<Arrow *>::const_iterator ien = it->in_arrows.end();
242                 for (; iit != ien; ++iit)
243                         LYXERR0("From " << (*iit)->from << " to " << (*iit)->to);
244                 LYXERR0("Out arrows...");
245                 iit = it->out_arrows.begin();
246                 ien = it->out_arrows.end();
247                 for (; iit != ien; ++iit)
248                         LYXERR0("From " << (*iit)->from << " to " << (*iit)->to);
249         }
250 }
251 #endif
252
253
254 } // namespace lyx