deba@2017
|
1 |
/* -*- C++ -*-
|
deba@2017
|
2 |
*
|
deba@2017
|
3 |
* This file is a part of LEMON, a generic C++ optimization library
|
deba@2017
|
4 |
*
|
alpar@2553
|
5 |
* Copyright (C) 2003-2008
|
deba@2017
|
6 |
* Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
|
deba@2017
|
7 |
* (Egervary Research Group on Combinatorial Optimization, EGRES).
|
deba@2017
|
8 |
*
|
deba@2017
|
9 |
* Permission to use, modify and distribute this software is granted
|
deba@2017
|
10 |
* provided that this copyright notice appears in all copies. For
|
deba@2017
|
11 |
* precise terms see the accompanying LICENSE file.
|
deba@2017
|
12 |
*
|
deba@2017
|
13 |
* This software is provided "AS IS" with no warranty of any kind,
|
deba@2017
|
14 |
* express or implied, and with no claim as to its suitability for any
|
deba@2017
|
15 |
* purpose.
|
deba@2017
|
16 |
*
|
deba@2017
|
17 |
*/
|
deba@2017
|
18 |
|
deba@2017
|
19 |
#ifndef LEMON_MIN_COST_ARBORESCENCE_H
|
deba@2017
|
20 |
#define LEMON_MIN_COST_ARBORESCENCE_H
|
deba@2017
|
21 |
|
deba@2017
|
22 |
///\ingroup spantree
|
deba@2017
|
23 |
///\file
|
deba@2017
|
24 |
///\brief Minimum Cost Arborescence algorithm.
|
deba@2017
|
25 |
|
deba@2017
|
26 |
#include <vector>
|
deba@2017
|
27 |
|
deba@2017
|
28 |
#include <lemon/list_graph.h>
|
deba@2025
|
29 |
#include <lemon/bin_heap.h>
|
deba@2017
|
30 |
|
deba@2017
|
31 |
namespace lemon {
|
deba@2017
|
32 |
|
deba@2017
|
33 |
|
deba@2017
|
34 |
/// \brief Default traits class of MinCostArborescence class.
|
deba@2017
|
35 |
///
|
deba@2017
|
36 |
/// Default traits class of MinCostArborescence class.
|
deba@2017
|
37 |
/// \param _Graph Graph type.
|
deba@2017
|
38 |
/// \param _CostMap Type of cost map.
|
deba@2017
|
39 |
template <class _Graph, class _CostMap>
|
deba@2017
|
40 |
struct MinCostArborescenceDefaultTraits{
|
deba@2017
|
41 |
|
deba@2017
|
42 |
/// \brief The graph type the algorithm runs on.
|
deba@2017
|
43 |
typedef _Graph Graph;
|
deba@2017
|
44 |
|
deba@2017
|
45 |
/// \brief The type of the map that stores the edge costs.
|
deba@2017
|
46 |
///
|
deba@2017
|
47 |
/// The type of the map that stores the edge costs.
|
alpar@2260
|
48 |
/// It must meet the \ref concepts::ReadMap "ReadMap" concept.
|
deba@2017
|
49 |
typedef _CostMap CostMap;
|
deba@2017
|
50 |
|
deba@2017
|
51 |
/// \brief The value type of the costs.
|
deba@2017
|
52 |
///
|
deba@2017
|
53 |
/// The value type of the costs.
|
deba@2017
|
54 |
typedef typename CostMap::Value Value;
|
deba@2017
|
55 |
|
deba@2017
|
56 |
/// \brief The type of the map that stores which edges are
|
deba@2017
|
57 |
/// in the arborescence.
|
deba@2017
|
58 |
///
|
deba@2017
|
59 |
/// The type of the map that stores which edges are in the arborescence.
|
alpar@2260
|
60 |
/// It must meet the \ref concepts::WriteMap "WriteMap" concept.
|
alpar@2259
|
61 |
/// Initially it will be set to false on each edge. After it
|
deba@2025
|
62 |
/// will set all arborescence edges once.
|
deba@2017
|
63 |
typedef typename Graph::template EdgeMap<bool> ArborescenceMap;
|
deba@2017
|
64 |
|
deba@2017
|
65 |
/// \brief Instantiates a ArborescenceMap.
|
deba@2017
|
66 |
///
|
deba@2017
|
67 |
/// This function instantiates a \ref ArborescenceMap.
|
deba@2017
|
68 |
/// \param _graph is the graph, to which we would like to define the
|
deba@2017
|
69 |
/// ArborescenceMap.
|
deba@2017
|
70 |
static ArborescenceMap *createArborescenceMap(const Graph &_graph){
|
deba@2017
|
71 |
return new ArborescenceMap(_graph);
|
deba@2017
|
72 |
}
|
deba@2017
|
73 |
|
deba@2025
|
74 |
/// \brief The type of the PredMap
|
deba@2025
|
75 |
///
|
deba@2025
|
76 |
/// The type of the PredMap. It is a node map with an edge value type.
|
deba@2025
|
77 |
typedef typename Graph::template NodeMap<typename Graph::Edge> PredMap;
|
deba@2025
|
78 |
|
deba@2025
|
79 |
/// \brief Instantiates a PredMap.
|
deba@2025
|
80 |
///
|
deba@2025
|
81 |
/// This function instantiates a \ref PredMap.
|
deba@2025
|
82 |
/// \param _graph is the graph, to which we would like to define the
|
deba@2025
|
83 |
/// PredMap.
|
deba@2025
|
84 |
static PredMap *createPredMap(const Graph &_graph){
|
deba@2025
|
85 |
return new PredMap(_graph);
|
deba@2025
|
86 |
}
|
deba@2025
|
87 |
|
deba@2017
|
88 |
};
|
deba@2017
|
89 |
|
deba@2017
|
90 |
/// \ingroup spantree
|
deba@2017
|
91 |
///
|
deba@2017
|
92 |
/// \brief %MinCostArborescence algorithm class.
|
deba@2017
|
93 |
///
|
deba@2017
|
94 |
/// This class provides an efficient implementation of
|
deba@2017
|
95 |
/// %MinCostArborescence algorithm. The arborescence is a tree
|
deba@2017
|
96 |
/// which is directed from a given source node of the graph. One or
|
deba@2017
|
97 |
/// more sources should be given for the algorithm and it will calculate
|
deba@2017
|
98 |
/// the minimum cost subgraph which are union of arborescences with the
|
deba@2017
|
99 |
/// given sources and spans all the nodes which are reachable from the
|
deba@2042
|
100 |
/// sources. The time complexity of the algorithm is \f$ O(n^2+e) \f$.
|
deba@2017
|
101 |
///
|
deba@2025
|
102 |
/// The algorithm provides also an optimal dual solution to arborescence
|
deba@2042
|
103 |
/// that way the optimality of the solution can be proofed easily.
|
deba@2025
|
104 |
///
|
deba@2017
|
105 |
/// \param _Graph The graph type the algorithm runs on. The default value
|
deba@2017
|
106 |
/// is \ref ListGraph. The value of _Graph is not used directly by
|
deba@2017
|
107 |
/// MinCostArborescence, it is only passed to
|
deba@2017
|
108 |
/// \ref MinCostArborescenceDefaultTraits.
|
deba@2017
|
109 |
/// \param _CostMap This read-only EdgeMap determines the costs of the
|
deba@2017
|
110 |
/// edges. It is read once for each edge, so the map may involve in
|
deba@2017
|
111 |
/// relatively time consuming process to compute the edge cost if
|
deba@2017
|
112 |
/// it is necessary. The default map type is \ref
|
alpar@2260
|
113 |
/// concepts::Graph::EdgeMap "Graph::EdgeMap<int>". The value
|
deba@2017
|
114 |
/// of _CostMap is not used directly by MinCostArborescence,
|
deba@2017
|
115 |
/// it is only passed to \ref MinCostArborescenceDefaultTraits.
|
deba@2017
|
116 |
/// \param _Traits Traits class to set various data types used
|
deba@2017
|
117 |
/// by the algorithm. The default traits class is
|
deba@2017
|
118 |
/// \ref MinCostArborescenceDefaultTraits
|
deba@2017
|
119 |
/// "MinCostArborescenceDefaultTraits<_Graph,_CostMap>". See \ref
|
deba@2017
|
120 |
/// MinCostArborescenceDefaultTraits for the documentation of a
|
deba@2017
|
121 |
/// MinCostArborescence traits class.
|
deba@2017
|
122 |
///
|
deba@2017
|
123 |
/// \author Balazs Dezso
|
deba@2017
|
124 |
#ifndef DOXYGEN
|
deba@2017
|
125 |
template <typename _Graph = ListGraph,
|
deba@2017
|
126 |
typename _CostMap = typename _Graph::template EdgeMap<int>,
|
deba@2017
|
127 |
typename _Traits =
|
deba@2017
|
128 |
MinCostArborescenceDefaultTraits<_Graph, _CostMap> >
|
deba@2017
|
129 |
#else
|
deba@2017
|
130 |
template <typename _Graph, typename _CostMap, typedef _Traits>
|
deba@2017
|
131 |
#endif
|
deba@2017
|
132 |
class MinCostArborescence {
|
deba@2017
|
133 |
public:
|
deba@2017
|
134 |
|
deba@2017
|
135 |
/// \brief \ref Exception for uninitialized parameters.
|
deba@2017
|
136 |
///
|
deba@2017
|
137 |
/// This error represents problems in the initialization
|
deba@2017
|
138 |
/// of the parameters of the algorithms.
|
deba@2017
|
139 |
class UninitializedParameter : public lemon::UninitializedParameter {
|
deba@2017
|
140 |
public:
|
alpar@2151
|
141 |
virtual const char* what() const throw() {
|
deba@2017
|
142 |
return "lemon::MinCostArborescence::UninitializedParameter";
|
deba@2017
|
143 |
}
|
deba@2017
|
144 |
};
|
deba@2017
|
145 |
|
deba@2017
|
146 |
/// The traits.
|
deba@2017
|
147 |
typedef _Traits Traits;
|
deba@2017
|
148 |
/// The type of the underlying graph.
|
deba@2017
|
149 |
typedef typename Traits::Graph Graph;
|
deba@2017
|
150 |
/// The type of the map that stores the edge costs.
|
deba@2017
|
151 |
typedef typename Traits::CostMap CostMap;
|
deba@2017
|
152 |
///The type of the costs of the edges.
|
deba@2017
|
153 |
typedef typename Traits::Value Value;
|
deba@2025
|
154 |
///The type of the predecessor map.
|
deba@2025
|
155 |
typedef typename Traits::PredMap PredMap;
|
deba@2017
|
156 |
///The type of the map that stores which edges are in the arborescence.
|
deba@2017
|
157 |
typedef typename Traits::ArborescenceMap ArborescenceMap;
|
deba@2017
|
158 |
|
deba@2017
|
159 |
protected:
|
deba@2017
|
160 |
|
deba@2017
|
161 |
typedef typename Graph::Node Node;
|
deba@2017
|
162 |
typedef typename Graph::Edge Edge;
|
deba@2017
|
163 |
typedef typename Graph::NodeIt NodeIt;
|
deba@2017
|
164 |
typedef typename Graph::EdgeIt EdgeIt;
|
deba@2017
|
165 |
typedef typename Graph::InEdgeIt InEdgeIt;
|
deba@2017
|
166 |
typedef typename Graph::OutEdgeIt OutEdgeIt;
|
deba@2017
|
167 |
|
deba@2017
|
168 |
struct CostEdge {
|
deba@2017
|
169 |
|
deba@2017
|
170 |
Edge edge;
|
deba@2017
|
171 |
Value value;
|
deba@2017
|
172 |
|
deba@2017
|
173 |
CostEdge() {}
|
deba@2017
|
174 |
CostEdge(Edge _edge, Value _value) : edge(_edge), value(_value) {}
|
deba@2017
|
175 |
|
deba@2017
|
176 |
};
|
deba@2017
|
177 |
|
deba@2025
|
178 |
const Graph *graph;
|
deba@2025
|
179 |
const CostMap *cost;
|
deba@2017
|
180 |
|
deba@2025
|
181 |
PredMap *_pred;
|
deba@2025
|
182 |
bool local_pred;
|
deba@2017
|
183 |
|
deba@2025
|
184 |
ArborescenceMap *_arborescence;
|
deba@2025
|
185 |
bool local_arborescence;
|
deba@2025
|
186 |
|
deba@2025
|
187 |
typedef typename Graph::template EdgeMap<int> EdgeOrder;
|
deba@2025
|
188 |
EdgeOrder *_edge_order;
|
deba@2025
|
189 |
|
deba@2025
|
190 |
typedef typename Graph::template NodeMap<int> NodeOrder;
|
deba@2025
|
191 |
NodeOrder *_node_order;
|
deba@2017
|
192 |
|
deba@2017
|
193 |
typedef typename Graph::template NodeMap<CostEdge> CostEdgeMap;
|
deba@2017
|
194 |
CostEdgeMap *_cost_edges;
|
deba@2017
|
195 |
|
deba@2017
|
196 |
struct StackLevel {
|
deba@2017
|
197 |
|
deba@2017
|
198 |
std::vector<CostEdge> edges;
|
deba@2017
|
199 |
int node_level;
|
deba@2017
|
200 |
|
deba@2017
|
201 |
};
|
deba@2017
|
202 |
|
deba@2017
|
203 |
std::vector<StackLevel> level_stack;
|
deba@2017
|
204 |
std::vector<Node> queue;
|
deba@2017
|
205 |
|
deba@2025
|
206 |
typedef std::vector<typename Graph::Node> DualNodeList;
|
deba@2025
|
207 |
|
deba@2025
|
208 |
DualNodeList _dual_node_list;
|
deba@2025
|
209 |
|
deba@2025
|
210 |
struct DualVariable {
|
deba@2025
|
211 |
int begin, end;
|
deba@2025
|
212 |
Value value;
|
deba@2025
|
213 |
|
deba@2025
|
214 |
DualVariable(int _begin, int _end, Value _value)
|
deba@2025
|
215 |
: begin(_begin), end(_end), value(_value) {}
|
deba@2025
|
216 |
|
deba@2025
|
217 |
};
|
deba@2025
|
218 |
|
deba@2025
|
219 |
typedef std::vector<DualVariable> DualVariables;
|
deba@2025
|
220 |
|
deba@2025
|
221 |
DualVariables _dual_variables;
|
deba@2025
|
222 |
|
deba@2025
|
223 |
typedef typename Graph::template NodeMap<int> HeapCrossRef;
|
deba@2025
|
224 |
|
deba@2025
|
225 |
HeapCrossRef *_heap_cross_ref;
|
deba@2025
|
226 |
|
mqrelly@2263
|
227 |
typedef BinHeap<int, HeapCrossRef> Heap;
|
deba@2025
|
228 |
|
deba@2025
|
229 |
Heap *_heap;
|
deba@2017
|
230 |
|
deba@2017
|
231 |
public:
|
deba@2017
|
232 |
|
deba@2017
|
233 |
/// \name Named template parameters
|
deba@2017
|
234 |
|
deba@2017
|
235 |
/// @{
|
deba@2017
|
236 |
|
deba@2017
|
237 |
template <class T>
|
deba@2017
|
238 |
struct DefArborescenceMapTraits : public Traits {
|
deba@2017
|
239 |
typedef T ArborescenceMap;
|
deba@2017
|
240 |
static ArborescenceMap *createArborescenceMap(const Graph &)
|
deba@2017
|
241 |
{
|
deba@2017
|
242 |
throw UninitializedParameter();
|
deba@2017
|
243 |
}
|
deba@2017
|
244 |
};
|
deba@2017
|
245 |
|
deba@2017
|
246 |
/// \brief \ref named-templ-param "Named parameter" for
|
deba@2017
|
247 |
/// setting ArborescenceMap type
|
deba@2017
|
248 |
///
|
deba@2017
|
249 |
/// \ref named-templ-param "Named parameter" for setting
|
deba@2017
|
250 |
/// ArborescenceMap type
|
deba@2017
|
251 |
template <class T>
|
deba@2017
|
252 |
struct DefArborescenceMap
|
deba@2017
|
253 |
: public MinCostArborescence<Graph, CostMap,
|
deba@2017
|
254 |
DefArborescenceMapTraits<T> > {
|
deba@2017
|
255 |
typedef MinCostArborescence<Graph, CostMap,
|
deba@2017
|
256 |
DefArborescenceMapTraits<T> > Create;
|
deba@2017
|
257 |
};
|
deba@2025
|
258 |
|
deba@2025
|
259 |
template <class T>
|
deba@2025
|
260 |
struct DefPredMapTraits : public Traits {
|
deba@2025
|
261 |
typedef T PredMap;
|
deba@2025
|
262 |
static PredMap *createPredMap(const Graph &)
|
deba@2025
|
263 |
{
|
deba@2025
|
264 |
throw UninitializedParameter();
|
deba@2025
|
265 |
}
|
deba@2025
|
266 |
};
|
deba@2025
|
267 |
|
deba@2025
|
268 |
/// \brief \ref named-templ-param "Named parameter" for
|
deba@2025
|
269 |
/// setting PredMap type
|
deba@2025
|
270 |
///
|
deba@2025
|
271 |
/// \ref named-templ-param "Named parameter" for setting
|
deba@2025
|
272 |
/// PredMap type
|
deba@2025
|
273 |
template <class T>
|
deba@2025
|
274 |
struct DefPredMap
|
deba@2025
|
275 |
: public MinCostArborescence<Graph, CostMap, DefPredMapTraits<T> > {
|
deba@2025
|
276 |
typedef MinCostArborescence<Graph, CostMap, DefPredMapTraits<T> > Create;
|
deba@2025
|
277 |
};
|
deba@2017
|
278 |
|
deba@2017
|
279 |
/// @}
|
deba@2017
|
280 |
|
deba@2017
|
281 |
/// \brief Constructor.
|
deba@2017
|
282 |
///
|
deba@2017
|
283 |
/// \param _graph The graph the algorithm will run on.
|
deba@2017
|
284 |
/// \param _cost The cost map used by the algorithm.
|
deba@2017
|
285 |
MinCostArborescence(const Graph& _graph, const CostMap& _cost)
|
deba@2025
|
286 |
: graph(&_graph), cost(&_cost), _pred(0), local_pred(false),
|
deba@2025
|
287 |
_arborescence(0), local_arborescence(false),
|
deba@2025
|
288 |
_edge_order(0), _node_order(0), _cost_edges(0),
|
deba@2025
|
289 |
_heap_cross_ref(0), _heap(0) {}
|
deba@2017
|
290 |
|
deba@2017
|
291 |
/// \brief Destructor.
|
deba@2017
|
292 |
~MinCostArborescence() {
|
deba@2017
|
293 |
destroyStructures();
|
deba@2017
|
294 |
}
|
deba@2017
|
295 |
|
deba@2017
|
296 |
/// \brief Sets the arborescence map.
|
deba@2017
|
297 |
///
|
deba@2017
|
298 |
/// Sets the arborescence map.
|
deba@2017
|
299 |
/// \return \c (*this)
|
deba@2017
|
300 |
MinCostArborescence& arborescenceMap(ArborescenceMap& m) {
|
deba@2025
|
301 |
if (local_arborescence) {
|
deba@2025
|
302 |
delete _arborescence;
|
deba@2025
|
303 |
}
|
deba@2025
|
304 |
local_arborescence = false;
|
deba@2025
|
305 |
_arborescence = &m;
|
deba@2025
|
306 |
return *this;
|
deba@2025
|
307 |
}
|
deba@2025
|
308 |
|
deba@2025
|
309 |
/// \brief Sets the arborescence map.
|
deba@2025
|
310 |
///
|
deba@2025
|
311 |
/// Sets the arborescence map.
|
deba@2025
|
312 |
/// \return \c (*this)
|
deba@2025
|
313 |
MinCostArborescence& predMap(PredMap& m) {
|
deba@2025
|
314 |
if (local_pred) {
|
deba@2025
|
315 |
delete _pred;
|
deba@2025
|
316 |
}
|
deba@2025
|
317 |
local_pred = false;
|
deba@2025
|
318 |
_pred = &m;
|
deba@2017
|
319 |
return *this;
|
deba@2017
|
320 |
}
|
deba@2017
|
321 |
|
deba@2017
|
322 |
/// \name Query Functions
|
deba@2017
|
323 |
/// The result of the %MinCostArborescence algorithm can be obtained
|
deba@2017
|
324 |
/// using these functions.\n
|
deba@2017
|
325 |
/// Before the use of these functions,
|
deba@2017
|
326 |
/// either run() or start() must be called.
|
deba@2017
|
327 |
|
deba@2017
|
328 |
/// @{
|
deba@2017
|
329 |
|
deba@2017
|
330 |
/// \brief Returns a reference to the arborescence map.
|
deba@2017
|
331 |
///
|
deba@2017
|
332 |
/// Returns a reference to the arborescence map.
|
deba@2017
|
333 |
const ArborescenceMap& arborescenceMap() const {
|
deba@2025
|
334 |
return *_arborescence;
|
deba@2017
|
335 |
}
|
deba@2017
|
336 |
|
deba@2017
|
337 |
/// \brief Returns true if the edge is in the arborescence.
|
deba@2017
|
338 |
///
|
deba@2017
|
339 |
/// Returns true if the edge is in the arborescence.
|
deba@2017
|
340 |
/// \param edge The edge of the graph.
|
deba@2017
|
341 |
/// \pre \ref run() must be called before using this function.
|
deba@2025
|
342 |
bool arborescence(Edge edge) const {
|
deba@2025
|
343 |
return (*_pred)[graph->target(edge)] == edge;
|
deba@2025
|
344 |
}
|
deba@2025
|
345 |
|
deba@2025
|
346 |
/// \brief Returns a reference to the pred map.
|
deba@2025
|
347 |
///
|
deba@2025
|
348 |
/// Returns a reference to the pred map.
|
deba@2025
|
349 |
const PredMap& predMap() const {
|
deba@2025
|
350 |
return *_pred;
|
deba@2025
|
351 |
}
|
deba@2025
|
352 |
|
deba@2025
|
353 |
/// \brief Returns the predecessor edge of the given node.
|
deba@2025
|
354 |
///
|
deba@2025
|
355 |
/// Returns the predecessor edge of the given node.
|
deba@2025
|
356 |
bool pred(Node node) const {
|
deba@2025
|
357 |
return (*_pred)[node];
|
deba@2017
|
358 |
}
|
deba@2017
|
359 |
|
deba@2017
|
360 |
/// \brief Returns the cost of the arborescence.
|
deba@2017
|
361 |
///
|
deba@2017
|
362 |
/// Returns the cost of the arborescence.
|
deba@2025
|
363 |
Value arborescenceValue() const {
|
deba@2017
|
364 |
Value sum = 0;
|
deba@2017
|
365 |
for (EdgeIt it(*graph); it != INVALID; ++it) {
|
deba@2025
|
366 |
if (arborescence(it)) {
|
deba@2017
|
367 |
sum += (*cost)[it];
|
deba@2017
|
368 |
}
|
deba@2017
|
369 |
}
|
deba@2017
|
370 |
return sum;
|
deba@2017
|
371 |
}
|
deba@2017
|
372 |
|
deba@2025
|
373 |
/// \brief Indicates that a node is reachable from the sources.
|
deba@2025
|
374 |
///
|
deba@2025
|
375 |
/// Indicates that a node is reachable from the sources.
|
deba@2025
|
376 |
bool reached(Node node) const {
|
deba@2025
|
377 |
return (*_node_order)[node] != -3;
|
deba@2025
|
378 |
}
|
deba@2025
|
379 |
|
deba@2025
|
380 |
/// \brief Indicates that a node is processed.
|
deba@2025
|
381 |
///
|
deba@2025
|
382 |
/// Indicates that a node is processed. The arborescence path exists
|
deba@2025
|
383 |
/// from the source to the given node.
|
deba@2025
|
384 |
bool processed(Node node) const {
|
deba@2025
|
385 |
return (*_node_order)[node] == -1;
|
deba@2025
|
386 |
}
|
deba@2025
|
387 |
|
deba@2025
|
388 |
/// \brief Returns the number of the dual variables in basis.
|
deba@2025
|
389 |
///
|
deba@2025
|
390 |
/// Returns the number of the dual variables in basis.
|
deba@2025
|
391 |
int dualSize() const {
|
deba@2025
|
392 |
return _dual_variables.size();
|
deba@2025
|
393 |
}
|
deba@2025
|
394 |
|
deba@2025
|
395 |
/// \brief Returns the value of the dual solution.
|
deba@2025
|
396 |
///
|
deba@2025
|
397 |
/// Returns the value of the dual solution. It should be
|
deba@2025
|
398 |
/// equal to the arborescence value.
|
deba@2025
|
399 |
Value dualValue() const {
|
deba@2025
|
400 |
Value sum = 0;
|
deba@2385
|
401 |
for (int i = 0; i < int(_dual_variables.size()); ++i) {
|
deba@2025
|
402 |
sum += _dual_variables[i].value;
|
deba@2025
|
403 |
}
|
deba@2025
|
404 |
return sum;
|
deba@2025
|
405 |
}
|
deba@2025
|
406 |
|
deba@2025
|
407 |
/// \brief Returns the number of the nodes in the dual variable.
|
deba@2025
|
408 |
///
|
deba@2025
|
409 |
/// Returns the number of the nodes in the dual variable.
|
deba@2025
|
410 |
int dualSize(int k) const {
|
deba@2025
|
411 |
return _dual_variables[k].end - _dual_variables[k].begin;
|
deba@2025
|
412 |
}
|
deba@2025
|
413 |
|
deba@2025
|
414 |
/// \brief Returns the value of the dual variable.
|
deba@2025
|
415 |
///
|
deba@2025
|
416 |
/// Returns the the value of the dual variable.
|
deba@2025
|
417 |
const Value& dualValue(int k) const {
|
deba@2025
|
418 |
return _dual_variables[k].value;
|
deba@2025
|
419 |
}
|
deba@2025
|
420 |
|
deba@2025
|
421 |
/// \brief Lemon iterator for get a dual variable.
|
deba@2025
|
422 |
///
|
deba@2025
|
423 |
/// Lemon iterator for get a dual variable. This class provides
|
deba@2025
|
424 |
/// a common style lemon iterator which gives back a subset of
|
deba@2025
|
425 |
/// the nodes.
|
deba@2025
|
426 |
class DualIt {
|
deba@2025
|
427 |
public:
|
deba@2025
|
428 |
|
deba@2025
|
429 |
/// \brief Constructor.
|
deba@2025
|
430 |
///
|
deba@2025
|
431 |
/// Constructor for get the nodeset of the variable.
|
deba@2025
|
432 |
DualIt(const MinCostArborescence& algorithm, int variable)
|
deba@2025
|
433 |
: _algorithm(&algorithm), _variable(variable)
|
deba@2025
|
434 |
{
|
deba@2025
|
435 |
_index = _algorithm->_dual_variables[_variable].begin;
|
deba@2025
|
436 |
}
|
deba@2025
|
437 |
|
deba@2025
|
438 |
/// \brief Invalid constructor.
|
deba@2025
|
439 |
///
|
deba@2025
|
440 |
/// Invalid constructor.
|
deba@2025
|
441 |
DualIt(Invalid) : _algorithm(0) {}
|
deba@2025
|
442 |
|
deba@2025
|
443 |
/// \brief Conversion to node.
|
deba@2025
|
444 |
///
|
deba@2025
|
445 |
/// Conversion to node.
|
deba@2025
|
446 |
operator Node() const {
|
deba@2025
|
447 |
return _algorithm ? _algorithm->_dual_node_list[_index] : INVALID;
|
deba@2025
|
448 |
}
|
deba@2025
|
449 |
|
deba@2025
|
450 |
/// \brief Increment operator.
|
deba@2025
|
451 |
///
|
deba@2025
|
452 |
/// Increment operator.
|
deba@2025
|
453 |
DualIt& operator++() {
|
deba@2025
|
454 |
++_index;
|
deba@2025
|
455 |
if (_algorithm->_dual_variables[_variable].end == _index) {
|
deba@2025
|
456 |
_algorithm = 0;
|
deba@2025
|
457 |
}
|
deba@2025
|
458 |
return *this;
|
deba@2025
|
459 |
}
|
deba@2025
|
460 |
|
deba@2025
|
461 |
bool operator==(const DualIt& it) const {
|
deba@2385
|
462 |
return static_cast<Node>(*this) == static_cast<Node>(it);
|
deba@2025
|
463 |
}
|
deba@2025
|
464 |
bool operator!=(const DualIt& it) const {
|
deba@2385
|
465 |
return static_cast<Node>(*this) != static_cast<Node>(it);
|
deba@2025
|
466 |
}
|
deba@2025
|
467 |
bool operator<(const DualIt& it) const {
|
deba@2385
|
468 |
return static_cast<Node>(*this) < static_cast<Node>(it);
|
deba@2025
|
469 |
}
|
deba@2025
|
470 |
|
deba@2025
|
471 |
private:
|
deba@2025
|
472 |
const MinCostArborescence* _algorithm;
|
deba@2025
|
473 |
int _variable;
|
deba@2025
|
474 |
int _index;
|
deba@2025
|
475 |
};
|
deba@2025
|
476 |
|
deba@2017
|
477 |
/// @}
|
deba@2017
|
478 |
|
deba@2017
|
479 |
/// \name Execution control
|
deba@2017
|
480 |
/// The simplest way to execute the algorithm is to use
|
deba@2017
|
481 |
/// one of the member functions called \c run(...). \n
|
deba@2017
|
482 |
/// If you need more control on the execution,
|
deba@2017
|
483 |
/// first you must call \ref init(), then you can add several
|
deba@2017
|
484 |
/// source nodes with \ref addSource().
|
deba@2025
|
485 |
/// Finally \ref start() will perform the arborescence
|
deba@2017
|
486 |
/// computation.
|
deba@2017
|
487 |
|
deba@2017
|
488 |
///@{
|
deba@2017
|
489 |
|
deba@2017
|
490 |
/// \brief Initializes the internal data structures.
|
deba@2017
|
491 |
///
|
deba@2017
|
492 |
/// Initializes the internal data structures.
|
deba@2017
|
493 |
///
|
deba@2017
|
494 |
void init() {
|
deba@2017
|
495 |
initStructures();
|
deba@2025
|
496 |
_heap->clear();
|
deba@2017
|
497 |
for (NodeIt it(*graph); it != INVALID; ++it) {
|
deba@2017
|
498 |
(*_cost_edges)[it].edge = INVALID;
|
deba@2025
|
499 |
_node_order->set(it, -3);
|
deba@2025
|
500 |
_heap_cross_ref->set(it, Heap::PRE_HEAP);
|
deba@2385
|
501 |
_pred->set(it, INVALID);
|
deba@2017
|
502 |
}
|
deba@2017
|
503 |
for (EdgeIt it(*graph); it != INVALID; ++it) {
|
deba@2025
|
504 |
_arborescence->set(it, false);
|
deba@2025
|
505 |
_edge_order->set(it, -1);
|
deba@2017
|
506 |
}
|
deba@2025
|
507 |
_dual_node_list.clear();
|
deba@2025
|
508 |
_dual_variables.clear();
|
deba@2017
|
509 |
}
|
deba@2017
|
510 |
|
deba@2017
|
511 |
/// \brief Adds a new source node.
|
deba@2017
|
512 |
///
|
deba@2017
|
513 |
/// Adds a new source node to the algorithm.
|
deba@2017
|
514 |
void addSource(Node source) {
|
deba@2017
|
515 |
std::vector<Node> nodes;
|
deba@2017
|
516 |
nodes.push_back(source);
|
deba@2017
|
517 |
while (!nodes.empty()) {
|
deba@2017
|
518 |
Node node = nodes.back();
|
deba@2017
|
519 |
nodes.pop_back();
|
deba@2017
|
520 |
for (OutEdgeIt it(*graph, node); it != INVALID; ++it) {
|
deba@2025
|
521 |
Node target = graph->target(it);
|
deba@2025
|
522 |
if ((*_node_order)[target] == -3) {
|
deba@2025
|
523 |
(*_node_order)[target] = -2;
|
deba@2025
|
524 |
nodes.push_back(target);
|
deba@2025
|
525 |
queue.push_back(target);
|
deba@2017
|
526 |
}
|
deba@2017
|
527 |
}
|
deba@2017
|
528 |
}
|
deba@2025
|
529 |
(*_node_order)[source] = -1;
|
deba@2017
|
530 |
}
|
deba@2017
|
531 |
|
deba@2017
|
532 |
/// \brief Processes the next node in the priority queue.
|
deba@2017
|
533 |
///
|
deba@2017
|
534 |
/// Processes the next node in the priority queue.
|
deba@2017
|
535 |
///
|
deba@2017
|
536 |
/// \return The processed node.
|
deba@2017
|
537 |
///
|
deba@2017
|
538 |
/// \warning The queue must not be empty!
|
deba@2017
|
539 |
Node processNextNode() {
|
deba@2017
|
540 |
Node node = queue.back();
|
deba@2017
|
541 |
queue.pop_back();
|
deba@2025
|
542 |
if ((*_node_order)[node] == -2) {
|
deba@2017
|
543 |
Edge edge = prepare(node);
|
deba@2025
|
544 |
Node source = graph->source(edge);
|
deba@2025
|
545 |
while ((*_node_order)[source] != -1) {
|
deba@2025
|
546 |
if ((*_node_order)[source] >= 0) {
|
deba@2025
|
547 |
edge = contract(source);
|
deba@2017
|
548 |
} else {
|
deba@2025
|
549 |
edge = prepare(source);
|
deba@2017
|
550 |
}
|
deba@2025
|
551 |
source = graph->source(edge);
|
deba@2017
|
552 |
}
|
deba@2025
|
553 |
finalize(edge);
|
deba@2017
|
554 |
level_stack.clear();
|
deba@2017
|
555 |
}
|
deba@2017
|
556 |
return node;
|
deba@2017
|
557 |
}
|
deba@2017
|
558 |
|
deba@2017
|
559 |
/// \brief Returns the number of the nodes to be processed.
|
deba@2017
|
560 |
///
|
deba@2017
|
561 |
/// Returns the number of the nodes to be processed.
|
deba@2017
|
562 |
int queueSize() const {
|
deba@2017
|
563 |
return queue.size();
|
deba@2017
|
564 |
}
|
deba@2017
|
565 |
|
deba@2017
|
566 |
/// \brief Returns \c false if there are nodes to be processed.
|
deba@2017
|
567 |
///
|
deba@2017
|
568 |
/// Returns \c false if there are nodes to be processed.
|
deba@2017
|
569 |
bool emptyQueue() const {
|
deba@2017
|
570 |
return queue.empty();
|
deba@2017
|
571 |
}
|
deba@2017
|
572 |
|
deba@2017
|
573 |
/// \brief Executes the algorithm.
|
deba@2017
|
574 |
///
|
deba@2017
|
575 |
/// Executes the algorithm.
|
deba@2017
|
576 |
///
|
deba@2017
|
577 |
/// \pre init() must be called and at least one node should be added
|
deba@2017
|
578 |
/// with addSource() before using this function.
|
deba@2017
|
579 |
///
|
deba@2017
|
580 |
///\note mca.start() is just a shortcut of the following code.
|
deba@2017
|
581 |
///\code
|
deba@2017
|
582 |
///while (!mca.emptyQueue()) {
|
deba@2017
|
583 |
/// mca.processNextNode();
|
deba@2017
|
584 |
///}
|
deba@2017
|
585 |
///\endcode
|
deba@2017
|
586 |
void start() {
|
deba@2017
|
587 |
while (!emptyQueue()) {
|
deba@2017
|
588 |
processNextNode();
|
deba@2017
|
589 |
}
|
deba@2017
|
590 |
}
|
deba@2017
|
591 |
|
deba@2017
|
592 |
/// \brief Runs %MinCostArborescence algorithm from node \c s.
|
deba@2017
|
593 |
///
|
deba@2017
|
594 |
/// This method runs the %MinCostArborescence algorithm from
|
deba@2017
|
595 |
/// a root node \c s.
|
deba@2017
|
596 |
///
|
deba@2017
|
597 |
///\note mca.run(s) is just a shortcut of the following code.
|
deba@2017
|
598 |
///\code
|
deba@2017
|
599 |
///mca.init();
|
deba@2017
|
600 |
///mca.addSource(s);
|
deba@2017
|
601 |
///mca.start();
|
deba@2017
|
602 |
///\endcode
|
deba@2017
|
603 |
void run(Node node) {
|
deba@2017
|
604 |
init();
|
deba@2017
|
605 |
addSource(node);
|
deba@2017
|
606 |
start();
|
deba@2017
|
607 |
}
|
deba@2017
|
608 |
|
deba@2017
|
609 |
///@}
|
deba@2017
|
610 |
|
deba@2017
|
611 |
protected:
|
deba@2017
|
612 |
|
deba@2017
|
613 |
void initStructures() {
|
deba@2025
|
614 |
if (!_pred) {
|
deba@2025
|
615 |
local_pred = true;
|
deba@2025
|
616 |
_pred = Traits::createPredMap(*graph);
|
deba@2017
|
617 |
}
|
deba@2025
|
618 |
if (!_arborescence) {
|
deba@2025
|
619 |
local_arborescence = true;
|
deba@2025
|
620 |
_arborescence = Traits::createArborescenceMap(*graph);
|
deba@2025
|
621 |
}
|
deba@2025
|
622 |
if (!_edge_order) {
|
deba@2025
|
623 |
_edge_order = new EdgeOrder(*graph);
|
deba@2025
|
624 |
}
|
deba@2025
|
625 |
if (!_node_order) {
|
deba@2025
|
626 |
_node_order = new NodeOrder(*graph);
|
deba@2017
|
627 |
}
|
deba@2017
|
628 |
if (!_cost_edges) {
|
deba@2017
|
629 |
_cost_edges = new CostEdgeMap(*graph);
|
deba@2017
|
630 |
}
|
deba@2025
|
631 |
if (!_heap_cross_ref) {
|
deba@2025
|
632 |
_heap_cross_ref = new HeapCrossRef(*graph, -1);
|
deba@2025
|
633 |
}
|
deba@2025
|
634 |
if (!_heap) {
|
deba@2025
|
635 |
_heap = new Heap(*_heap_cross_ref);
|
deba@2025
|
636 |
}
|
deba@2017
|
637 |
}
|
deba@2017
|
638 |
|
deba@2017
|
639 |
void destroyStructures() {
|
deba@2025
|
640 |
if (local_arborescence) {
|
deba@2025
|
641 |
delete _arborescence;
|
deba@2025
|
642 |
}
|
deba@2025
|
643 |
if (local_pred) {
|
deba@2025
|
644 |
delete _pred;
|
deba@2025
|
645 |
}
|
deba@2624
|
646 |
if (_edge_order) {
|
deba@2025
|
647 |
delete _edge_order;
|
deba@2025
|
648 |
}
|
deba@2025
|
649 |
if (_node_order) {
|
deba@2025
|
650 |
delete _node_order;
|
deba@2017
|
651 |
}
|
deba@2624
|
652 |
if (_cost_edges) {
|
deba@2017
|
653 |
delete _cost_edges;
|
deba@2017
|
654 |
}
|
deba@2624
|
655 |
if (_heap) {
|
deba@2025
|
656 |
delete _heap;
|
deba@2025
|
657 |
}
|
deba@2624
|
658 |
if (_heap_cross_ref) {
|
deba@2025
|
659 |
delete _heap_cross_ref;
|
deba@2017
|
660 |
}
|
deba@2017
|
661 |
}
|
deba@2017
|
662 |
|
deba@2017
|
663 |
Edge prepare(Node node) {
|
deba@2017
|
664 |
std::vector<Node> nodes;
|
deba@2025
|
665 |
(*_node_order)[node] = _dual_node_list.size();
|
deba@2025
|
666 |
StackLevel level;
|
deba@2025
|
667 |
level.node_level = _dual_node_list.size();
|
deba@2025
|
668 |
_dual_node_list.push_back(node);
|
deba@2017
|
669 |
for (InEdgeIt it(*graph, node); it != INVALID; ++it) {
|
deba@2017
|
670 |
Edge edge = it;
|
deba@2025
|
671 |
Node source = graph->source(edge);
|
deba@2017
|
672 |
Value value = (*cost)[it];
|
deba@2025
|
673 |
if (source == node || (*_node_order)[source] == -3) continue;
|
deba@2025
|
674 |
if ((*_cost_edges)[source].edge == INVALID) {
|
deba@2025
|
675 |
(*_cost_edges)[source].edge = edge;
|
deba@2025
|
676 |
(*_cost_edges)[source].value = value;
|
deba@2025
|
677 |
nodes.push_back(source);
|
deba@2017
|
678 |
} else {
|
deba@2025
|
679 |
if ((*_cost_edges)[source].value > value) {
|
deba@2025
|
680 |
(*_cost_edges)[source].edge = edge;
|
deba@2025
|
681 |
(*_cost_edges)[source].value = value;
|
deba@2017
|
682 |
}
|
deba@2017
|
683 |
}
|
deba@2017
|
684 |
}
|
deba@2017
|
685 |
CostEdge minimum = (*_cost_edges)[nodes[0]];
|
deba@2385
|
686 |
for (int i = 1; i < int(nodes.size()); ++i) {
|
deba@2017
|
687 |
if ((*_cost_edges)[nodes[i]].value < minimum.value) {
|
deba@2017
|
688 |
minimum = (*_cost_edges)[nodes[i]];
|
deba@2017
|
689 |
}
|
deba@2017
|
690 |
}
|
deba@2025
|
691 |
_edge_order->set(minimum.edge, _dual_variables.size());
|
deba@2025
|
692 |
DualVariable var(_dual_node_list.size() - 1,
|
deba@2025
|
693 |
_dual_node_list.size(), minimum.value);
|
deba@2025
|
694 |
_dual_variables.push_back(var);
|
deba@2385
|
695 |
for (int i = 0; i < int(nodes.size()); ++i) {
|
deba@2017
|
696 |
(*_cost_edges)[nodes[i]].value -= minimum.value;
|
deba@2017
|
697 |
level.edges.push_back((*_cost_edges)[nodes[i]]);
|
deba@2017
|
698 |
(*_cost_edges)[nodes[i]].edge = INVALID;
|
deba@2017
|
699 |
}
|
deba@2017
|
700 |
level_stack.push_back(level);
|
deba@2017
|
701 |
return minimum.edge;
|
deba@2017
|
702 |
}
|
deba@2017
|
703 |
|
deba@2025
|
704 |
Edge contract(Node node) {
|
deba@2025
|
705 |
int node_bottom = bottom(node);
|
deba@2017
|
706 |
std::vector<Node> nodes;
|
deba@2017
|
707 |
while (!level_stack.empty() &&
|
deba@2017
|
708 |
level_stack.back().node_level >= node_bottom) {
|
deba@2385
|
709 |
for (int i = 0; i < int(level_stack.back().edges.size()); ++i) {
|
deba@2017
|
710 |
Edge edge = level_stack.back().edges[i].edge;
|
deba@2025
|
711 |
Node source = graph->source(edge);
|
deba@2017
|
712 |
Value value = level_stack.back().edges[i].value;
|
deba@2025
|
713 |
if ((*_node_order)[source] >= node_bottom) continue;
|
deba@2025
|
714 |
if ((*_cost_edges)[source].edge == INVALID) {
|
deba@2025
|
715 |
(*_cost_edges)[source].edge = edge;
|
deba@2025
|
716 |
(*_cost_edges)[source].value = value;
|
deba@2025
|
717 |
nodes.push_back(source);
|
deba@2017
|
718 |
} else {
|
deba@2025
|
719 |
if ((*_cost_edges)[source].value > value) {
|
deba@2025
|
720 |
(*_cost_edges)[source].edge = edge;
|
deba@2025
|
721 |
(*_cost_edges)[source].value = value;
|
deba@2017
|
722 |
}
|
deba@2017
|
723 |
}
|
deba@2017
|
724 |
}
|
deba@2017
|
725 |
level_stack.pop_back();
|
deba@2017
|
726 |
}
|
deba@2017
|
727 |
CostEdge minimum = (*_cost_edges)[nodes[0]];
|
deba@2385
|
728 |
for (int i = 1; i < int(nodes.size()); ++i) {
|
deba@2017
|
729 |
if ((*_cost_edges)[nodes[i]].value < minimum.value) {
|
deba@2017
|
730 |
minimum = (*_cost_edges)[nodes[i]];
|
deba@2017
|
731 |
}
|
deba@2017
|
732 |
}
|
deba@2025
|
733 |
_edge_order->set(minimum.edge, _dual_variables.size());
|
deba@2025
|
734 |
DualVariable var(node_bottom, _dual_node_list.size(), minimum.value);
|
deba@2025
|
735 |
_dual_variables.push_back(var);
|
deba@2017
|
736 |
StackLevel level;
|
deba@2017
|
737 |
level.node_level = node_bottom;
|
deba@2385
|
738 |
for (int i = 0; i < int(nodes.size()); ++i) {
|
deba@2017
|
739 |
(*_cost_edges)[nodes[i]].value -= minimum.value;
|
deba@2017
|
740 |
level.edges.push_back((*_cost_edges)[nodes[i]]);
|
deba@2017
|
741 |
(*_cost_edges)[nodes[i]].edge = INVALID;
|
deba@2017
|
742 |
}
|
deba@2017
|
743 |
level_stack.push_back(level);
|
deba@2017
|
744 |
return minimum.edge;
|
deba@2017
|
745 |
}
|
deba@2017
|
746 |
|
deba@2025
|
747 |
int bottom(Node node) {
|
deba@2017
|
748 |
int k = level_stack.size() - 1;
|
deba@2025
|
749 |
while (level_stack[k].node_level > (*_node_order)[node]) {
|
deba@2017
|
750 |
--k;
|
deba@2017
|
751 |
}
|
deba@2017
|
752 |
return level_stack[k].node_level;
|
deba@2017
|
753 |
}
|
deba@2017
|
754 |
|
deba@2025
|
755 |
void finalize(Edge edge) {
|
deba@2025
|
756 |
Node node = graph->target(edge);
|
deba@2025
|
757 |
_heap->push(node, (*_edge_order)[edge]);
|
deba@2025
|
758 |
_pred->set(node, edge);
|
deba@2025
|
759 |
while (!_heap->empty()) {
|
deba@2025
|
760 |
Node source = _heap->top();
|
deba@2025
|
761 |
_heap->pop();
|
deba@2025
|
762 |
_node_order->set(source, -1);
|
deba@2025
|
763 |
for (OutEdgeIt it(*graph, source); it != INVALID; ++it) {
|
deba@2025
|
764 |
if ((*_edge_order)[it] < 0) continue;
|
deba@2025
|
765 |
Node target = graph->target(it);
|
deba@2025
|
766 |
switch(_heap->state(target)) {
|
deba@2025
|
767 |
case Heap::PRE_HEAP:
|
deba@2025
|
768 |
_heap->push(target, (*_edge_order)[it]);
|
deba@2025
|
769 |
_pred->set(target, it);
|
deba@2025
|
770 |
break;
|
deba@2025
|
771 |
case Heap::IN_HEAP:
|
deba@2025
|
772 |
if ((*_edge_order)[it] < (*_heap)[target]) {
|
deba@2025
|
773 |
_heap->decrease(target, (*_edge_order)[it]);
|
deba@2025
|
774 |
_pred->set(target, it);
|
deba@2025
|
775 |
}
|
deba@2025
|
776 |
break;
|
deba@2025
|
777 |
case Heap::POST_HEAP:
|
deba@2025
|
778 |
break;
|
deba@2017
|
779 |
}
|
deba@2017
|
780 |
}
|
deba@2025
|
781 |
_arborescence->set((*_pred)[source], true);
|
deba@2017
|
782 |
}
|
deba@2017
|
783 |
}
|
deba@2017
|
784 |
|
deba@2017
|
785 |
};
|
deba@2017
|
786 |
|
deba@2017
|
787 |
/// \ingroup spantree
|
deba@2017
|
788 |
///
|
deba@2017
|
789 |
/// \brief Function type interface for MinCostArborescence algorithm.
|
deba@2017
|
790 |
///
|
deba@2017
|
791 |
/// Function type interface for MinCostArborescence algorithm.
|
deba@2017
|
792 |
/// \param graph The Graph that the algorithm runs on.
|
deba@2017
|
793 |
/// \param cost The CostMap of the edges.
|
deba@2017
|
794 |
/// \param source The source of the arborescence.
|
deba@2017
|
795 |
/// \retval arborescence The bool EdgeMap which stores the arborescence.
|
deba@2017
|
796 |
/// \return The cost of the arborescence.
|
deba@2017
|
797 |
///
|
deba@2017
|
798 |
/// \sa MinCostArborescence
|
deba@2017
|
799 |
template <typename Graph, typename CostMap, typename ArborescenceMap>
|
deba@2017
|
800 |
typename CostMap::Value minCostArborescence(const Graph& graph,
|
deba@2017
|
801 |
const CostMap& cost,
|
deba@2017
|
802 |
typename Graph::Node source,
|
deba@2017
|
803 |
ArborescenceMap& arborescence) {
|
deba@2017
|
804 |
typename MinCostArborescence<Graph, CostMap>
|
deba@2017
|
805 |
::template DefArborescenceMap<ArborescenceMap>
|
deba@2017
|
806 |
::Create mca(graph, cost);
|
deba@2017
|
807 |
mca.arborescenceMap(arborescence);
|
deba@2017
|
808 |
mca.run(source);
|
deba@2025
|
809 |
return mca.arborescenceValue();
|
deba@2017
|
810 |
}
|
deba@2017
|
811 |
|
deba@2017
|
812 |
}
|
deba@2017
|
813 |
|
deba@2017
|
814 |
#endif
|