18 |
18 |
19 namespace lemon { |
19 namespace lemon { |
20 /** |
20 /** |
21 [PAGE]sec_graph_adaptors[PAGE] Graph Adaptors |
21 [PAGE]sec_graph_adaptors[PAGE] Graph Adaptors |
22 |
22 |
23 \todo Clarify this section. |
23 In typical algorithms and applications related to graphs and networks, |
24 |
24 we usually encounter situations in which a specific alteration of a graph |
25 Alteration of standard containers need a very limited number of |
25 has to be considered. |
26 operations, these together satisfy the everyday requirements. |
26 If some nodes or arcs have to be hidden (maybe temporarily) or the reverse |
27 In the case of graph structures, different operations are needed which do |
27 oriented graph has to be used, then this is the case. |
28 not alter the physical graph, but gives another view. If some nodes or |
28 However, actually modifing physical storage of the graph or |
29 arcs have to be hidden or the reverse oriented graph have to be used, then |
29 making a copy of the graph structure along with the required maps |
30 this is the case. It also may happen that in a flow implementation |
30 could be rather expensive (in time or in memory usage) compared to the |
31 the residual graph can be accessed by another algorithm, or a node-set |
31 operations that should be performed on the altered graph. |
32 is to be shrunk for another algorithm. |
32 In such cases, the LEMON \e graph \e adaptor \e classes could be used. |
33 LEMON also provides a variety of graphs for these requirements called |
33 |
34 \ref graph_adaptors "graph adaptors". Adaptors cannot be used alone but only |
34 |
35 in conjunction with other graph representations. |
35 [SEC]sec_reverse_digraph[SEC] Reverse Oriented Digraph |
36 |
36 |
37 The main parts of LEMON are the different graph structures, generic |
37 Let us suppose that we have an instance \c g of a directed graph type, say |
38 graph algorithms, graph concepts, which couple them, and graph |
38 \ref ListDigraph and an algorithm |
39 adaptors. While the previous notions are more or less clear, the |
39 \code |
40 latter one needs further explanation. Graph adaptors are graph classes |
40 template <typename Digraph> |
41 which serve for considering graph structures in different ways. |
41 int algorithm(const Digraph&); |
42 |
42 \endcode |
43 A short example makes this much clearer. Suppose that we have an |
43 is needed to run on the reverse oriented digraph. |
44 instance \c g of a directed graph type, say ListDigraph and an algorithm |
44 In this situation, a certain adaptor class |
45 \code |
45 \code |
46 template <typename Digraph> |
46 template <typename Digraph> |
47 int algorithm(const Digraph&); |
47 class ReverseDigraph; |
48 \endcode |
48 \endcode |
49 is needed to run on the reverse oriented graph. It may be expensive |
49 can be used. |
50 (in time or in memory usage) to copy \c g with the reversed |
50 |
51 arcs. In this case, an adaptor class is used, which (according |
51 The graph adaptors are special classes that serve for considering other graph |
52 to LEMON \ref concepts::Digraph "digraph concepts") works as a digraph. |
52 structures in different ways. They can be used exactly the same as "real" |
53 The adaptor uses the original digraph structure and digraph operations when |
53 graphs, i.e. they conform to the \ref graph_concepts "graph concepts", thus all |
54 methods of the reversed oriented graph are called. This means that the adaptor |
54 generic algorithms can be performed on them. However, the adaptor classes |
55 have minor memory usage, and do not perform sophisticated algorithmic |
55 cannot be used alone but only in conjunction with actual graph representations. |
56 actions. The purpose of it is to give a tool for the cases when a |
56 They do not alter the physical graph storage, they just give another view of it. |
57 graph have to be used in a specific alteration. If this alteration is |
57 When the methods of the adaptors are called, they use the underlying |
58 obtained by a usual construction like filtering the node or the arc set or |
58 graph structures and their operations, thus these classes have only negligible |
59 considering a new orientation, then an adaptor is worthwhile to use. |
59 memory usage and do not perform sophisticated algorithmic actions. |
60 To come back to the reverse oriented graph, in this situation |
60 |
61 \code |
61 This technique yields convenient tools that help writing compact and elegant |
62 template<typename Digraph> class ReverseDigraph; |
62 code, and makes it possible to easily implement complex algorithms based on |
63 \endcode |
63 well tested standard components. |
64 template class can be used. The code looks as follows |
64 |
65 \code |
65 For solving the problem introduced above, we could use the follwing code. |
66 ListDigraph g; |
66 |
67 ReverseDigraph<ListDigraph> rg(g); |
67 \code |
68 int result = algorithm(rg); |
68 ListDigraph g; |
69 \endcode |
69 ReverseDigraph<ListDigraph> rg(g); |
70 During running the algorithm, the original digraph \c g is untouched. |
70 int result = algorithm(rg); |
71 This techniques give rise to an elegant code, and based on stable |
71 \endcode |
72 graph adaptors, complex algorithms can be implemented easily. |
72 |
73 |
73 Note that the original digraph \c g remains untouched during the whole |
74 In flow, circulation and matching problems, the residual |
74 procedure. |
75 graph is of particular importance. Combining an adaptor implementing |
75 |
76 this with shortest path algorithms or minimum mean cycle algorithms, |
76 LEMON also provides simple "creator functions" for the adaptor |
77 a range of weighted and cardinality optimization algorithms can be |
77 classes to make their usage even simpler. |
78 obtained. For other examples, the interested user is referred to the |
78 For example, \ref reverseDigraph() returns an instance of \ref ReverseDigraph, |
79 detailed documentation of particular adaptors. |
79 thus the above code can be written like this. |
80 |
80 |
81 The behavior of graph adaptors can be very different. Some of them keep |
81 \code |
82 capabilities of the original graph while in other cases this would be |
82 ListDigraph g; |
83 meaningless. This means that the concepts that they meet depend |
83 int result = algorithm(reverseDigraph(g)); |
84 on the graph adaptor, and the wrapped graph. |
84 \endcode |
85 For example, if an arc of a reversed digraph is deleted, this is carried |
85 |
86 out by deleting the corresponding arc of the original digraph, thus the |
86 Another essential feature of the adaptors is that their \c Node and \c Arc |
87 adaptor modifies the original digraph. |
87 types convert to the original item types. |
88 However in case of a residual digraph, this operation has no sense. |
88 Therefore, the maps of the original graph can be used in connection with |
89 |
89 the adaptor. |
90 Let us stand one more example here to simplify your work. |
90 |
91 ReverseDigraph has constructor |
91 In the following code, Dijksta's algorithm is run on the reverse oriented |
92 \code |
92 graph but using the original node and arc maps. |
93 ReverseDigraph(Digraph& digraph); |
93 |
94 \endcode |
94 \code |
95 This means that in a situation, when a <tt>const %ListDigraph&</tt> |
95 ListDigraph g; |
96 reference to a graph is given, then it have to be instantiated with |
96 ListDigraph::ArcMap length(g); |
97 <tt>Digraph=const %ListDigraph</tt>. |
97 ListDigraph::NodeMap dist(g); |
|
98 |
|
99 ListDigraph::Node s = g.addNode(); |
|
100 // add more nodes and arcs |
|
101 |
|
102 dijkstra(reverseDigraph(g), length).distMap(dist).run(s); |
|
103 \endcode |
|
104 |
|
105 In the above examples, we used \ref ReverseDigraph in such a way that the |
|
106 underlying digraph was not changed. However, the adaptor class can even be |
|
107 used for modifying the original graph structure. |
|
108 It allows adding and deleting arcs or nodes, and these operations are carried |
|
109 out by calling suitable functions of the underlying digraph (if it supports |
|
110 them). |
|
111 |
|
112 For this, \ref ReverseDigraph "ReverseDigraph<GR>" has a constructor of the |
|
113 following form. |
|
114 \code |
|
115 ReverseDigraph(GR& gr); |
|
116 \endcode |
|
117 |
|
118 This means that in a situation, when the modification of the original graph |
|
119 has to be avoided (e.g. it is given as a const reference), then the adaptor |
|
120 class has to be instantiated with \c GR set to be \c const type |
|
121 (e.g. <tt>GR = const %ListDigraph</tt>), as in the following example. |
|
122 |
98 \code |
123 \code |
99 int algorithm1(const ListDigraph& g) { |
124 int algorithm1(const ListDigraph& g) { |
100 ReverseDigraph<const ListDigraph> rg(g); |
125 ReverseDigraph<const ListDigraph> rg(g); |
101 return algorithm2(rg); |
126 return algorithm2(rg); |
102 } |
127 } |
103 \endcode |
128 \endcode |
104 |
129 |
105 <hr> |
130 \note Modification capabilities are not supported for all adaptors. |
106 |
131 E.g. for \ref ResidualDigraph (see \ref sec_other_adaptors "later"), |
107 The LEMON graph adaptor classes serve for considering graphs in |
132 this makes no sense. |
108 different ways. The adaptors can be used exactly the same as "real" |
133 |
109 graphs (i.e., they conform to the graph concepts), thus all generic |
134 As a more complex example, let us see how \ref ReverseDigraph can be used |
110 algorithms can be performed on them. However, the adaptor classes use |
135 together with a graph search algorithm to decide whether a directed graph is |
111 the underlying graph structures and operations when their methods are |
136 strongly connected or not. |
112 called, thus they have only negligible memory usage and do not perform |
137 We exploit the fact the a digraph is strongly connected if and only if |
113 sophisticated algorithmic actions. This technique yields convenient and |
138 for an arbitrarily selected node \c u, each other node is reachable from |
114 elegant tools for the cases when a graph has to be used in a specific |
139 \c u (along a directed path) and \c u is reachable from each node. |
115 alteration, but copying it would be too expensive (in time or in memory |
140 The latter condition is the same that each node is reachable from \c u |
116 usage) compared to the algorithm that should be executed on it. The |
141 in the reversed digraph. |
117 following example shows how the \ref ReverseDigraph adaptor can be used |
142 |
118 to run Dijksta's algorithm on the reverse oriented graph. Note that the |
143 \code |
119 maps of the original graph can be used in connection with the adaptor, |
144 template <typename Digraph> |
120 since the node and arc types of the adaptors convert to the original |
145 bool stronglyConnected(const Digraph& g) { |
121 item types. |
146 typedef typename Digraph::NodeIt NodeIt; |
122 |
147 NodeIt u(g); |
123 \code |
148 if (u == INVALID) return true; |
124 dijkstra(reverseDigraph(g), length).distMap(dist).run(s); |
149 |
125 \endcode |
150 // Run BFS on the original digraph |
126 |
151 Bfs<Digraph> bfs(g); |
127 Using \ref ReverseDigraph could be as efficient as working with the |
152 bfs.run(u); |
128 original graph, but not all adaptors can be so fast, of course. For |
153 for (NodeIt n(g); n != INVALID; ++n) { |
129 example, the subgraph adaptors have to access filter maps for the nodes |
154 if (!bfs.reached(n)) return false; |
130 and/or the arcs, thus their iterators are significantly slower than the |
155 } |
131 original iterators. LEMON also provides some more complex adaptors, for |
156 |
132 instance, \ref SplitNodes, which can be used for splitting each node in |
157 // Run BFS on the reverse oriented digraph |
133 a directed graph and \ref ResidualDigraph for modeling the residual |
158 typedef ReverseDigraph<const Digraph> RDigraph; |
134 network for flow and matching problems. |
159 RDigraph rg(g); |
135 |
160 Bfs<RDigraph> rbfs(rg); |
136 Therefore, in cases when rather complex algorithms have to be used |
161 rbfs.run(u); |
137 on a subgraph (e.g. when the nodes and arcs have to be traversed several |
162 for (NodeIt n(g); n != INVALID; ++n) { |
138 times), it could worth copying the altered graph into an efficient structure |
163 if (!rbfs.reached(n)) return false; |
139 and run the algorithm on it. |
164 } |
|
165 |
|
166 return true; |
|
167 } |
|
168 \endcode |
|
169 |
|
170 Note that we have to use the adaptor with '<tt>const Digraph</tt>' type, since |
|
171 \c g is a \c const reference to the original graph structure. |
|
172 The \ref stronglyConnected() function provided in LEMON has a quite |
|
173 similar implementation. |
|
174 |
|
175 |
|
176 [SEC]sec_subgraphs[SEC] Subgraph Adaptorts |
|
177 |
|
178 Another typical requirement is the use of certain subgraphs of a graph, |
|
179 or in other words, hiding nodes and/or arcs from a graph. |
|
180 LEMON provides several convenient adaptors for these purposes. |
|
181 |
|
182 \ref FilterArcs can be used when some arcs have to be hidden from a digraph. |
|
183 A \e filter \e map has to be given to the constructor, which assign \c bool |
|
184 values to the arcs specifying whether they have to be shown or not in the |
|
185 subgraph structure. |
|
186 Suppose we have a \ref ListDigraph structure \c g. |
|
187 Then we can construct a subgraph in which some arcs (\c a1, \c a2 etc.) |
|
188 are hidden as follows. |
|
189 |
|
190 \code |
|
191 ListDigraph::ArcMap filter(g, true); |
|
192 filter[a1] = false; |
|
193 filter[a2] = false; |
|
194 // ... |
|
195 FilterArcs<ListDigraph> subgraph(g, filter); |
|
196 \endcode |
|
197 |
|
198 The following more complex code runs Dijkstra's algorithm on a digraph |
|
199 that is obtained from another digraph by hiding all arcs having negative |
|
200 lengths. |
|
201 |
|
202 \code |
|
203 ListDigraph::ArcMap<int> length(g); |
|
204 ListDigraph::NodeMap<int> dist(g); |
|
205 |
|
206 dijkstra(filterArcs( g, lessMap(length, constMap<ListDigraph::Arc>(0)) ), |
|
207 length).distMap(dist).run(s); |
|
208 \endcode |
|
209 |
|
210 Note the extensive use of map adaptors and creator functions, which makes |
|
211 the code really compact and elegant. |
|
212 |
|
213 \note Implicit maps and graphs (e.g. created using functions) can only be |
|
214 used with the function-type interfaces of the algorithms, since they store |
|
215 only references for the used structures. |
|
216 |
|
217 \ref FilterEdges can be used for hiding edges from an undirected graph (like |
|
218 \ref FilterArcs is used for digraphs). \ref FilterNodes serves for filtering |
|
219 nodes along with the incident arcs or edges in a directed or undirected graph. |
|
220 If both arcs/edges and nodes have to be hidden, then you could use |
|
221 \ref SubDigraph or \ref SubGraph adaptors. |
|
222 |
|
223 \code |
|
224 ListGraph ug; |
|
225 ListGraph::NodeMap<bool> node_filter(ug); |
|
226 ListGraph::EdgeMap<bool> edge_filter(ug); |
|
227 |
|
228 SubGraph<ListGraph> sg(ug, node_filter, edge_filter); |
|
229 \endcode |
|
230 |
|
231 As you see, we needed two filter maps in this case: one for the nodes and |
|
232 another for the edges. If a node is hidden, then all of its incident edges |
|
233 are also considered to be hidden independently of their own filter values. |
|
234 |
|
235 The subgraph adaptors also make it possible to modify the filter values |
|
236 even after the construction of the adaptor class, thus the corresponding |
|
237 graph items can be hidden or shown on the fly. |
|
238 The adaptors store references to the filter maps, thus the map values can be |
|
239 set directly and even by using the \c enable(), \c disable() and \c status() |
|
240 functions. |
|
241 |
|
242 \code |
|
243 ListDigraph g; |
|
244 ListDigraph::Node x = g.addNode(); |
|
245 ListDigraph::Node y = g.addNode(); |
|
246 ListDigraph::Node z = g.addNode(); |
|
247 |
|
248 ListDigraph::NodeMap<bool> filter(g, true); |
|
249 FilterNodes<ListDigraph> subgraph(g, filter); |
|
250 std::cout << countNodes(subgraph) << ", "; |
|
251 |
|
252 filter[x] = false; |
|
253 std::cout << countNodes(subgraph) << ", "; |
|
254 |
|
255 subgraph.enable(x); |
|
256 subgraph.disable(y); |
|
257 subgraph.status(z, !subgraph.status(z)); |
|
258 std::cout << countNodes(subgraph) << std::endl; |
|
259 \endcode |
|
260 |
|
261 The above example prints out this line. |
|
262 \code |
|
263 3, 2, 1 |
|
264 \endcode |
|
265 |
|
266 Similarly to \ref ReverseDigraph, the subgraph adaptors also allow the |
|
267 modification of the underlying graph structures unless the graph template |
|
268 parameter is set to be \c const type. |
|
269 Moreover the item types of the original graphs and the subgraphs are |
|
270 convertible to each other. |
|
271 |
|
272 The iterators of the subgraph adaptors use the iterators of the original |
|
273 graph structures in such a way that each item with \c false filter value |
|
274 is skipped. If both the node and arc sets are filtered, then the arc iterators |
|
275 check for each arc the status of its end nodes in addition to its own assigned |
|
276 filter value. If the arc or one of its end nodes is hidden, then the arc |
|
277 is left out and the next arc is considered. |
|
278 (It is the same for edges in undirected graphs.) |
|
279 Therefore, the iterators of these adaptors are significantly slower than the |
|
280 original iterators. |
|
281 |
|
282 Using adaptors, these efficiency aspects should be kept in mind. |
|
283 For example, if rather complex algorithms have to be performed on a |
|
284 subgraph (e.g. the nodes and arcs need to be traversed several times), |
|
285 then it could worth copying the altered graph into an efficient |
|
286 structure (e.g. \ref StaticDigraph) and run the algorithm on it. |
140 Note that the adaptor classes can also be used for doing this easily, |
287 Note that the adaptor classes can also be used for doing this easily, |
141 without having to copy the graph manually, as shown in the following |
288 without having to copy the graph manually, as shown in the following |
142 example. |
289 example. |
143 |
290 |
144 \code |
291 \code |
145 ListDigraph g; |
292 ListDigraph g; |
146 ListDigraph::NodeMap<bool> filter_map(g); |
293 ListDigraph::NodeMap<bool> filter_map(g); |
147 // construct the graph and fill the filter map |
294 // construct the graph and fill the filter map |
148 |
295 |
149 { |
296 { |
150 SmartDigraph temp_graph; |
297 StaticDigraph tmp_graph; |
151 ListDigraph::NodeMap<SmartDigraph::Node> node_ref(g); |
298 ListDigraph::NodeMap<StaticDigraph::Node> node_ref(g); |
152 digraphCopy(filterNodes(g, filter_map), temp_graph) |
299 digraphCopy(filterNodes(g, filter_map), tmp_graph) |
153 .nodeRef(node_ref).run(); |
300 .nodeRef(node_ref).run(); |
154 |
301 |
155 // use temp_graph |
302 // use tmp_graph |
156 } |
303 } |
157 \endcode |
304 \endcode |
158 |
305 |
159 <hr> |
306 \note Using \ref ReverseDigraph could be as efficient as working with the |
160 |
307 original graph, but most of the adaptors cannot be so fast, of course. |
161 Another interesting adaptor in LEMON is \ref SplitNodes. |
308 |
162 It can be used for splitting each node into an in-node and an out-node |
309 |
163 in a directed graph. Formally, the adaptor replaces each node |
310 [SEC]sec_other_adaptors[SEC] Other Graph Adaptors |
164 u in the graph with two nodes, namely node u<sub>in</sub> and node |
311 |
165 u<sub>out</sub>. Each arc (u,c) in the original graph will correspond to an |
312 Two other practical adaptors are \ref Undirector and \ref Orienter. |
166 arc (u<sub>out</sub>,v<sub>in</sub>). The adaptor also adds an |
313 \ref Undirector makes an undirected graph from a digraph disregarding the |
167 additional bind arc (u<sub>in</sub>,u<sub>out</sub>) for each node u |
314 orientations of the arcs. More precisely, an arc of the original digraph |
168 of the original digraph. |
315 is considered as an edge (and two arcs, as well) in the adaptor. |
169 |
316 \ref Orienter can be used for the reverse alteration, it assigns a certain |
170 The aim of this class is to assign costs to the nodes when using |
317 orientation to each edge of an undirected graph to form a directed graph. |
171 algorithms which would otherwise consider arc costs only. |
318 A \c bool edge map of the underlying graph must be given to the constructor |
172 For example, let us suppose that we have a directed graph with costs |
319 of the class, which define the direction of the arcs in the created adaptor |
173 given for both the nodes and the arcs. |
320 (with respect to the inherent orientation of the original edges). |
174 Then Dijkstra's algorithm can be used in connection with \ref SplitNodes |
321 |
175 as follows. |
322 \code |
|
323 ListGraph graph; |
|
324 ListGraph::EdgeMap<bool> dir_map(graph, true); |
|
325 Orienter<ListGraph> directed_graph(graph, dir_map); |
|
326 \endcode |
|
327 |
|
328 LEMON also provides some more complex adaptors, for |
|
329 instance, \ref SplitNodes, which can be used for splitting each node of a |
|
330 directed graph into an in-node and an out-node. |
|
331 Formally, the adaptor replaces each node u in the graph with two nodes, |
|
332 namely u<sub>in</sub> and u<sub>out</sub>. Each arc (u,v) of the original |
|
333 graph will correspond to an arc (u<sub>out</sub>,v<sub>in</sub>). |
|
334 The adaptor also adds an additional bind arc (u<sub>in</sub>,u<sub>out</sub>) |
|
335 for each node u of the original digraph. |
|
336 |
|
337 The aim of this class is to assign costs or capacities to the nodes when using |
|
338 algorithms which would otherwise consider arc costs or capacities only. |
|
339 For example, let us suppose that we have a digraph \c g with costs assigned to |
|
340 both the nodes and the arcs. Then Dijkstra's algorithm can be used in |
|
341 connection with \ref SplitNodes as follows. |
176 |
342 |
177 \code |
343 \code |
178 typedef SplitNodes<ListDigraph> SplitGraph; |
344 typedef SplitNodes<ListDigraph> SplitGraph; |
179 SplitGraph sg(g); |
345 SplitGraph sg(g); |
180 SplitGraph::CombinedArcMap<NodeCostMap, ArcCostMap> |
346 SplitGraph::CombinedArcMap<NodeCostMap, ArcCostMap> |
181 combined_cost(node_cost, arc_cost); |
347 combined_cost(node_cost, arc_cost); |
182 SplitGraph::NodeMap<double> dist(sg); |
348 SplitGraph::NodeMap<double> dist(sg); |
183 dijkstra(sg, combined_cost).distMap(dist).run(sg.outNode(u)); |
349 dijkstra(sg, combined_cost).distMap(dist).run(sg.outNode(u)); |
184 \endcode |
350 \endcode |
185 |
351 |
186 Note that this problem can be solved more efficiently with |
352 \note This problem can also be solved using map adaptors to create |
187 map adaptors. |
353 an implicit arc map that assigns for each arc the sum of its cost |
188 |
354 and the cost of its target node. This map can be used with the original |
189 These techniques help writing compact and elegant code, and makes it possible |
355 graph more efficiently than using the above solution. |
190 to easily implement complex algorithms based on well tested standard components. |
356 |
191 For instance, in flow and matching problems the residual graph is of |
357 Another nice application is the problem of finding disjoint paths in |
192 particular importance. |
358 a digraph. |
193 Combining \ref ResidualDigraph adaptor with various algorithms, a |
359 The maximum number of \e edge \e disjoint paths from a source node to |
194 range of weighted and cardinality optimization methods can be obtained |
360 a sink node in a digraph can be easily computed using a maximum flow |
195 directly. |
361 algorithm with all arc capacities set to 1. |
|
362 On the other hand, \e node \e disjoint paths cannot be found directly |
|
363 using a standard algorithm. |
|
364 However, \ref SplitNodes adaptor makes it really simple. |
|
365 If a maximum flow computation is performed on this adaptor, then the |
|
366 bottleneck of the flow (i.e. the minimum cut) will be formed by bind arcs, |
|
367 thus the found flow will correspond to the union of some node disjoint |
|
368 paths in terms of the original digraph. |
|
369 |
|
370 In flow, circulation and matching problems, the residual network is of |
|
371 particular importance, which is implemented in \ref ResidualDigraph. |
|
372 Combining this adaptor with various algorithms, a range of weighted and |
|
373 cardinality optimization methods can be implemented easily. |
|
374 |
|
375 To construct a residual network, a digraph structure, a flow map and a |
|
376 capacity map have to be given to the constructor of the adaptor as shown |
|
377 in the following code. |
|
378 |
|
379 \code |
|
380 ListDigraph g; |
|
381 ListDigraph::ArcMap<int> flow(g); |
|
382 ListDigraph::ArcMap<int> capacity(g); |
|
383 |
|
384 ResidualDigraph<ListDigraph> res_graph(g, capacity, flow); |
|
385 \endcode |
|
386 |
|
387 \note In fact, this class is implemented using two other adaptors: |
|
388 \ref Undirector and \ref FilterArcs. |
196 |
389 |
197 [TRAILER] |
390 [TRAILER] |
198 */ |
391 */ |
199 } |
392 } |