deba@2440: /* -*- C++ -*- deba@2440: * deba@2440: * This file is a part of LEMON, a generic C++ optimization library deba@2440: * alpar@2553: * Copyright (C) 2003-2008 deba@2440: * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport deba@2440: * (Egervary Research Group on Combinatorial Optimization, EGRES). deba@2440: * deba@2440: * Permission to use, modify and distribute this software is granted deba@2440: * provided that this copyright notice appears in all copies. For deba@2440: * precise terms see the accompanying LICENSE file. deba@2440: * deba@2440: * This software is provided "AS IS" with no warranty of any kind, deba@2440: * express or implied, and with no claim as to its suitability for any deba@2440: * purpose. deba@2440: * deba@2440: */ deba@2440: deba@2440: #ifndef LEMON_CYCLE_CANCELING_H deba@2440: #define LEMON_CYCLE_CANCELING_H deba@2440: deba@2440: /// \ingroup min_cost_flow deba@2440: /// deba@2440: /// \file kpeter@2573: /// \brief Cycle-canceling algorithm for finding a minimum cost flow. deba@2440: deba@2440: #include kpeter@2509: #include kpeter@2573: #include kpeter@2573: deba@2440: #include kpeter@2573: #include kpeter@2573: #include kpeter@2556: deba@2440: namespace lemon { deba@2440: deba@2440: /// \addtogroup min_cost_flow deba@2440: /// @{ deba@2440: kpeter@2556: /// \brief Implementation of a cycle-canceling algorithm for kpeter@2556: /// finding a minimum cost flow. deba@2440: /// kpeter@2556: /// \ref CycleCanceling implements a cycle-canceling algorithm for kpeter@2556: /// finding a minimum cost flow. deba@2440: /// kpeter@2573: /// \tparam Graph The directed graph type the algorithm runs on. kpeter@2573: /// \tparam LowerMap The type of the lower bound map. kpeter@2573: /// \tparam CapacityMap The type of the capacity (upper bound) map. kpeter@2573: /// \tparam CostMap The type of the cost (length) map. kpeter@2573: /// \tparam SupplyMap The type of the supply map. deba@2440: /// deba@2440: /// \warning kpeter@2573: /// - Edge capacities and costs should be \e non-negative \e integers. kpeter@2573: /// - Supply values should be \e signed \e integers. kpeter@2581: /// - The value types of the maps should be convertible to each other. kpeter@2581: /// - \c CostMap::Value must be signed type. kpeter@2573: /// kpeter@2573: /// \note By default the \ref BellmanFord "Bellman-Ford" algorithm is kpeter@2573: /// used for negative cycle detection with limited iteration number. kpeter@2573: /// However \ref CycleCanceling also provides the "Minimum Mean kpeter@2573: /// Cycle-Canceling" algorithm, which is \e strongly \e polynomial, kpeter@2573: /// but rather slower in practice. kpeter@2573: /// To use this version of the algorithm, call \ref run() with \c true kpeter@2573: /// parameter. deba@2440: /// deba@2440: /// \author Peter Kovacs deba@2440: kpeter@2533: template < typename Graph, kpeter@2533: typename LowerMap = typename Graph::template EdgeMap, kpeter@2573: typename CapacityMap = typename Graph::template EdgeMap, kpeter@2533: typename CostMap = typename Graph::template EdgeMap, kpeter@2573: typename SupplyMap = typename Graph::template NodeMap > deba@2440: class CycleCanceling deba@2440: { kpeter@2556: GRAPH_TYPEDEFS(typename Graph); deba@2440: deba@2440: typedef typename CapacityMap::Value Capacity; deba@2440: typedef typename CostMap::Value Cost; deba@2440: typedef typename SupplyMap::Value Supply; kpeter@2556: typedef typename Graph::template EdgeMap CapacityEdgeMap; kpeter@2556: typedef typename Graph::template NodeMap SupplyNodeMap; deba@2440: deba@2440: typedef ResGraphAdaptor< const Graph, Capacity, kpeter@2556: CapacityEdgeMap, CapacityEdgeMap > ResGraph; deba@2440: typedef typename ResGraph::Node ResNode; deba@2440: typedef typename ResGraph::NodeIt ResNodeIt; deba@2440: typedef typename ResGraph::Edge ResEdge; deba@2440: typedef typename ResGraph::EdgeIt ResEdgeIt; deba@2440: deba@2440: public: deba@2440: kpeter@2556: /// The type of the flow map. kpeter@2556: typedef typename Graph::template EdgeMap FlowMap; kpeter@2581: /// The type of the potential map. kpeter@2581: typedef typename Graph::template NodeMap PotentialMap; deba@2440: kpeter@2573: private: deba@2440: kpeter@2573: /// \brief Map adaptor class for handling residual edge costs. kpeter@2573: /// kpeter@2573: /// \ref ResidualCostMap is a map adaptor class for handling kpeter@2573: /// residual edge costs. kpeter@2573: class ResidualCostMap : public MapBase deba@2440: { deba@2440: private: deba@2440: kpeter@2573: const CostMap &_cost_map; deba@2440: deba@2440: public: deba@2440: kpeter@2573: ///\e kpeter@2573: ResidualCostMap(const CostMap &cost_map) : _cost_map(cost_map) {} deba@2440: kpeter@2573: ///\e kpeter@2509: Cost operator[](const ResEdge &e) const { kpeter@2573: return ResGraph::forward(e) ? _cost_map[e] : -_cost_map[e]; deba@2440: } deba@2440: kpeter@2573: }; //class ResidualCostMap deba@2440: kpeter@2573: private: deba@2440: kpeter@2573: // The maximum number of iterations for the first execution of the kpeter@2573: // Bellman-Ford algorithm. It should be at least 2. kpeter@2573: static const int BF_FIRST_LIMIT = 2; kpeter@2573: // The iteration limit for the Bellman-Ford algorithm is multiplied kpeter@2573: // by BF_ALPHA in every round. kpeter@2573: static const double BF_ALPHA = 1.5; deba@2440: kpeter@2573: private: deba@2440: kpeter@2573: // The directed graph the algorithm runs on kpeter@2573: const Graph &_graph; kpeter@2573: // The original lower bound map kpeter@2573: const LowerMap *_lower; kpeter@2573: // The modified capacity map kpeter@2573: CapacityEdgeMap _capacity; kpeter@2573: // The original cost map kpeter@2573: const CostMap &_cost; kpeter@2573: // The modified supply map kpeter@2573: SupplyNodeMap _supply; kpeter@2573: bool _valid_supply; kpeter@2573: kpeter@2573: // Edge map of the current flow kpeter@2581: FlowMap *_flow; kpeter@2581: bool _local_flow; kpeter@2581: // Node map of the current potentials kpeter@2581: PotentialMap *_potential; kpeter@2581: bool _local_potential; kpeter@2573: kpeter@2573: // The residual graph kpeter@2581: ResGraph *_res_graph; kpeter@2573: // The residual cost map kpeter@2573: ResidualCostMap _res_cost; kpeter@2573: kpeter@2573: public: deba@2440: kpeter@2581: /// \brief General constructor (with lower bounds). deba@2440: /// kpeter@2581: /// General constructor (with lower bounds). deba@2440: /// kpeter@2573: /// \param graph The directed graph the algorithm runs on. kpeter@2573: /// \param lower The lower bounds of the edges. kpeter@2573: /// \param capacity The capacities (upper bounds) of the edges. kpeter@2573: /// \param cost The cost (length) values of the edges. kpeter@2573: /// \param supply The supply values of the nodes (signed). kpeter@2573: CycleCanceling( const Graph &graph, kpeter@2573: const LowerMap &lower, kpeter@2573: const CapacityMap &capacity, kpeter@2573: const CostMap &cost, kpeter@2573: const SupplyMap &supply ) : kpeter@2573: _graph(graph), _lower(&lower), _capacity(graph), _cost(cost), kpeter@2581: _supply(graph), _flow(0), _local_flow(false), kpeter@2581: _potential(0), _local_potential(false), _res_cost(_cost) deba@2440: { kpeter@2556: // Removing non-zero lower bounds kpeter@2573: _capacity = subMap(capacity, lower); deba@2440: Supply sum = 0; kpeter@2573: for (NodeIt n(_graph); n != INVALID; ++n) { kpeter@2573: Supply s = supply[n]; kpeter@2573: for (InEdgeIt e(_graph, n); e != INVALID; ++e) kpeter@2573: s += lower[e]; kpeter@2573: for (OutEdgeIt e(_graph, n); e != INVALID; ++e) kpeter@2573: s -= lower[e]; kpeter@2573: sum += (_supply[n] = s); deba@2440: } kpeter@2573: _valid_supply = sum == 0; deba@2440: } deba@2440: kpeter@2581: /// \brief General constructor (without lower bounds). deba@2440: /// kpeter@2581: /// General constructor (without lower bounds). deba@2440: /// kpeter@2573: /// \param graph The directed graph the algorithm runs on. kpeter@2573: /// \param capacity The capacities (upper bounds) of the edges. kpeter@2573: /// \param cost The cost (length) values of the edges. kpeter@2573: /// \param supply The supply values of the nodes (signed). kpeter@2573: CycleCanceling( const Graph &graph, kpeter@2573: const CapacityMap &capacity, kpeter@2573: const CostMap &cost, kpeter@2573: const SupplyMap &supply ) : kpeter@2573: _graph(graph), _lower(NULL), _capacity(capacity), _cost(cost), kpeter@2581: _supply(supply), _flow(0), _local_flow(false), kpeter@2581: _potential(0), _local_potential(false), _res_cost(_cost) deba@2440: { deba@2440: // Checking the sum of supply values deba@2440: Supply sum = 0; kpeter@2573: for (NodeIt n(_graph); n != INVALID; ++n) sum += _supply[n]; kpeter@2573: _valid_supply = sum == 0; deba@2440: } deba@2440: kpeter@2581: /// \brief Simple constructor (with lower bounds). deba@2440: /// kpeter@2581: /// Simple constructor (with lower bounds). deba@2440: /// kpeter@2573: /// \param graph The directed graph the algorithm runs on. kpeter@2573: /// \param lower The lower bounds of the edges. kpeter@2573: /// \param capacity The capacities (upper bounds) of the edges. kpeter@2573: /// \param cost The cost (length) values of the edges. kpeter@2573: /// \param s The source node. kpeter@2573: /// \param t The target node. kpeter@2573: /// \param flow_value The required amount of flow from node \c s kpeter@2573: /// to node \c t (i.e. the supply of \c s and the demand of \c t). kpeter@2573: CycleCanceling( const Graph &graph, kpeter@2573: const LowerMap &lower, kpeter@2573: const CapacityMap &capacity, kpeter@2573: const CostMap &cost, kpeter@2573: Node s, Node t, kpeter@2573: Supply flow_value ) : kpeter@2573: _graph(graph), _lower(&lower), _capacity(graph), _cost(cost), kpeter@2581: _supply(graph), _flow(0), _local_flow(false), kpeter@2581: _potential(0), _local_potential(false), _res_cost(_cost) deba@2440: { kpeter@2556: // Removing non-zero lower bounds kpeter@2573: _capacity = subMap(capacity, lower); kpeter@2573: for (NodeIt n(_graph); n != INVALID; ++n) { kpeter@2573: Supply sum = 0; kpeter@2573: if (n == s) sum = flow_value; kpeter@2573: if (n == t) sum = -flow_value; kpeter@2573: for (InEdgeIt e(_graph, n); e != INVALID; ++e) kpeter@2573: sum += lower[e]; kpeter@2573: for (OutEdgeIt e(_graph, n); e != INVALID; ++e) kpeter@2573: sum -= lower[e]; kpeter@2573: _supply[n] = sum; deba@2440: } kpeter@2573: _valid_supply = true; deba@2440: } deba@2440: kpeter@2581: /// \brief Simple constructor (without lower bounds). deba@2440: /// kpeter@2581: /// Simple constructor (without lower bounds). deba@2440: /// kpeter@2573: /// \param graph The directed graph the algorithm runs on. kpeter@2573: /// \param capacity The capacities (upper bounds) of the edges. kpeter@2573: /// \param cost The cost (length) values of the edges. kpeter@2573: /// \param s The source node. kpeter@2573: /// \param t The target node. kpeter@2573: /// \param flow_value The required amount of flow from node \c s kpeter@2573: /// to node \c t (i.e. the supply of \c s and the demand of \c t). kpeter@2573: CycleCanceling( const Graph &graph, kpeter@2573: const CapacityMap &capacity, kpeter@2573: const CostMap &cost, kpeter@2573: Node s, Node t, kpeter@2573: Supply flow_value ) : kpeter@2573: _graph(graph), _lower(NULL), _capacity(capacity), _cost(cost), kpeter@2581: _supply(graph, 0), _flow(0), _local_flow(false), kpeter@2581: _potential(0), _local_potential(false), _res_cost(_cost) deba@2440: { kpeter@2573: _supply[s] = flow_value; kpeter@2573: _supply[t] = -flow_value; kpeter@2573: _valid_supply = true; deba@2440: } deba@2440: kpeter@2581: /// Destructor. kpeter@2581: ~CycleCanceling() { kpeter@2581: if (_local_flow) delete _flow; kpeter@2581: if (_local_potential) delete _potential; kpeter@2581: delete _res_graph; kpeter@2581: } kpeter@2581: kpeter@2581: /// \brief Sets the flow map. kpeter@2581: /// kpeter@2581: /// Sets the flow map. kpeter@2581: /// kpeter@2581: /// \return \c (*this) kpeter@2581: CycleCanceling& flowMap(FlowMap &map) { kpeter@2581: if (_local_flow) { kpeter@2581: delete _flow; kpeter@2581: _local_flow = false; kpeter@2581: } kpeter@2581: _flow = ↦ kpeter@2581: return *this; kpeter@2581: } kpeter@2581: kpeter@2581: /// \brief Sets the potential map. kpeter@2581: /// kpeter@2581: /// Sets the potential map. kpeter@2581: /// kpeter@2581: /// \return \c (*this) kpeter@2581: CycleCanceling& potentialMap(PotentialMap &map) { kpeter@2581: if (_local_potential) { kpeter@2581: delete _potential; kpeter@2581: _local_potential = false; kpeter@2581: } kpeter@2581: _potential = ↦ kpeter@2581: return *this; kpeter@2581: } kpeter@2581: kpeter@2581: /// \name Execution control kpeter@2581: /// The only way to execute the algorithm is to call the run() kpeter@2581: /// function. kpeter@2581: kpeter@2581: /// @{ kpeter@2581: kpeter@2556: /// \brief Runs the algorithm. kpeter@2556: /// kpeter@2556: /// Runs the algorithm. kpeter@2556: /// kpeter@2573: /// \param min_mean_cc Set this parameter to \c true to run the kpeter@2573: /// "Minimum Mean Cycle-Canceling" algorithm, which is strongly kpeter@2573: /// polynomial, but rather slower in practice. kpeter@2573: /// kpeter@2556: /// \return \c true if a feasible flow can be found. kpeter@2573: bool run(bool min_mean_cc = false) { kpeter@2573: return init() && start(min_mean_cc); kpeter@2556: } kpeter@2556: kpeter@2581: /// @} kpeter@2581: kpeter@2581: /// \name Query Functions kpeter@2581: /// The result of the algorithm can be obtained using these kpeter@2581: /// functions. kpeter@2581: /// \n run() must be called before using them. kpeter@2581: kpeter@2581: /// @{ kpeter@2581: kpeter@2573: /// \brief Returns a const reference to the edge map storing the kpeter@2573: /// found flow. deba@2440: /// kpeter@2573: /// Returns a const reference to the edge map storing the found flow. deba@2440: /// deba@2440: /// \pre \ref run() must be called before using this function. deba@2440: const FlowMap& flowMap() const { kpeter@2581: return *_flow; kpeter@2581: } kpeter@2581: kpeter@2581: /// \brief Returns a const reference to the node map storing the kpeter@2581: /// found potentials (the dual solution). kpeter@2581: /// kpeter@2581: /// Returns a const reference to the node map storing the found kpeter@2581: /// potentials (the dual solution). kpeter@2581: /// kpeter@2581: /// \pre \ref run() must be called before using this function. kpeter@2581: const PotentialMap& potentialMap() const { kpeter@2581: return *_potential; kpeter@2581: } kpeter@2581: kpeter@2581: /// \brief Returns the flow on the edge. kpeter@2581: /// kpeter@2581: /// Returns the flow on the edge. kpeter@2581: /// kpeter@2581: /// \pre \ref run() must be called before using this function. kpeter@2581: Capacity flow(const Edge& edge) const { kpeter@2581: return (*_flow)[edge]; kpeter@2581: } kpeter@2581: kpeter@2581: /// \brief Returns the potential of the node. kpeter@2581: /// kpeter@2581: /// Returns the potential of the node. kpeter@2581: /// kpeter@2581: /// \pre \ref run() must be called before using this function. kpeter@2581: Cost potential(const Node& node) const { kpeter@2581: return (*_potential)[node]; deba@2440: } deba@2440: deba@2440: /// \brief Returns the total cost of the found flow. deba@2440: /// deba@2440: /// Returns the total cost of the found flow. The complexity of the deba@2440: /// function is \f$ O(e) \f$. deba@2440: /// deba@2440: /// \pre \ref run() must be called before using this function. deba@2440: Cost totalCost() const { deba@2440: Cost c = 0; kpeter@2573: for (EdgeIt e(_graph); e != INVALID; ++e) kpeter@2581: c += (*_flow)[e] * _cost[e]; deba@2440: return c; deba@2440: } deba@2440: kpeter@2581: /// @} kpeter@2581: kpeter@2573: private: deba@2440: kpeter@2556: /// Initializes the algorithm. deba@2440: bool init() { kpeter@2573: if (!_valid_supply) return false; deba@2440: kpeter@2581: // Initializing flow and potential maps kpeter@2581: if (!_flow) { kpeter@2581: _flow = new FlowMap(_graph); kpeter@2581: _local_flow = true; kpeter@2581: } kpeter@2581: if (!_potential) { kpeter@2581: _potential = new PotentialMap(_graph); kpeter@2581: _local_potential = true; kpeter@2581: } kpeter@2581: kpeter@2581: _res_graph = new ResGraph(_graph, _capacity, *_flow); kpeter@2581: kpeter@2573: // Finding a feasible flow using Circulation kpeter@2556: Circulation< Graph, ConstMap, CapacityEdgeMap, kpeter@2556: SupplyMap > kpeter@2581: circulation( _graph, constMap(Capacity(0)), _capacity, kpeter@2573: _supply ); kpeter@2581: return circulation.flowMap(*_flow).run(); deba@2440: } deba@2440: kpeter@2573: bool start(bool min_mean_cc) { kpeter@2573: if (min_mean_cc) kpeter@2581: startMinMean(); kpeter@2573: else kpeter@2581: start(); kpeter@2581: kpeter@2581: // Handling non-zero lower bounds kpeter@2581: if (_lower) { kpeter@2581: for (EdgeIt e(_graph); e != INVALID; ++e) kpeter@2581: (*_flow)[e] += (*_lower)[e]; kpeter@2581: } kpeter@2581: return true; kpeter@2573: } kpeter@2573: kpeter@2573: /// \brief Executes the algorithm using \ref BellmanFord. kpeter@2573: /// kpeter@2573: /// Executes the algorithm using the \ref BellmanFord kpeter@2573: /// "Bellman-Ford" algorithm for negative cycle detection with kpeter@2573: /// successively larger limit for the number of iterations. kpeter@2581: void start() { kpeter@2581: typename BellmanFord::PredMap pred(*_res_graph); kpeter@2581: typename ResGraph::template NodeMap visited(*_res_graph); deba@2440: std::vector cycle; kpeter@2573: int node_num = countNodes(_graph); deba@2440: kpeter@2573: int length_bound = BF_FIRST_LIMIT; deba@2440: bool optimal = false; deba@2440: while (!optimal) { kpeter@2581: BellmanFord bf(*_res_graph, _res_cost); kpeter@2556: bf.predMap(pred); kpeter@2556: bf.init(0); kpeter@2556: int iter_num = 0; kpeter@2556: bool cycle_found = false; kpeter@2556: while (!cycle_found) { kpeter@2556: int curr_iter_num = iter_num + length_bound <= node_num ? kpeter@2556: length_bound : node_num - iter_num; kpeter@2556: iter_num += curr_iter_num; kpeter@2556: int real_iter_num = curr_iter_num; kpeter@2556: for (int i = 0; i < curr_iter_num; ++i) { kpeter@2556: if (bf.processNextWeakRound()) { kpeter@2556: real_iter_num = i; kpeter@2556: break; kpeter@2556: } kpeter@2556: } kpeter@2556: if (real_iter_num < curr_iter_num) { kpeter@2581: // Optimal flow is found kpeter@2556: optimal = true; kpeter@2581: // Setting node potentials kpeter@2581: for (NodeIt n(_graph); n != INVALID; ++n) kpeter@2581: (*_potential)[n] = bf.dist(n); kpeter@2556: break; kpeter@2556: } else { kpeter@2556: // Searching for node disjoint negative cycles kpeter@2581: for (ResNodeIt n(*_res_graph); n != INVALID; ++n) kpeter@2556: visited[n] = 0; kpeter@2556: int id = 0; kpeter@2581: for (ResNodeIt n(*_res_graph); n != INVALID; ++n) { kpeter@2556: if (visited[n] > 0) continue; kpeter@2556: visited[n] = ++id; kpeter@2556: ResNode u = pred[n] == INVALID ? kpeter@2581: INVALID : _res_graph->source(pred[n]); kpeter@2556: while (u != INVALID && visited[u] == 0) { kpeter@2556: visited[u] = id; kpeter@2556: u = pred[u] == INVALID ? kpeter@2581: INVALID : _res_graph->source(pred[u]); kpeter@2556: } kpeter@2556: if (u != INVALID && visited[u] == id) { kpeter@2556: // Finding the negative cycle kpeter@2556: cycle_found = true; kpeter@2556: cycle.clear(); kpeter@2556: ResEdge e = pred[u]; kpeter@2556: cycle.push_back(e); kpeter@2581: Capacity d = _res_graph->rescap(e); kpeter@2581: while (_res_graph->source(e) != u) { kpeter@2581: cycle.push_back(e = pred[_res_graph->source(e)]); kpeter@2581: if (_res_graph->rescap(e) < d) kpeter@2581: d = _res_graph->rescap(e); kpeter@2556: } kpeter@2573: kpeter@2556: // Augmenting along the cycle kpeter@2573: for (int i = 0; i < int(cycle.size()); ++i) kpeter@2581: _res_graph->augment(cycle[i], d); kpeter@2556: } kpeter@2556: } kpeter@2556: } deba@2440: kpeter@2556: if (!cycle_found) kpeter@2573: length_bound = int(length_bound * BF_ALPHA); kpeter@2556: } deba@2440: } deba@2440: } deba@2440: kpeter@2573: /// \brief Executes the algorithm using \ref MinMeanCycle. kpeter@2573: /// kpeter@2573: /// Executes the algorithm using \ref MinMeanCycle for negative kpeter@2573: /// cycle detection. kpeter@2581: void startMinMean() { deba@2440: typedef Path ResPath; kpeter@2581: MinMeanCycle mmc(*_res_graph, _res_cost); deba@2440: ResPath cycle; deba@2440: deba@2440: mmc.cyclePath(cycle).init(); deba@2440: if (mmc.findMinMean()) { kpeter@2556: while (mmc.cycleLength() < 0) { kpeter@2556: // Finding the cycle kpeter@2556: mmc.findCycle(); deba@2440: kpeter@2556: // Finding the largest flow amount that can be augmented kpeter@2556: // along the cycle kpeter@2556: Capacity delta = 0; kpeter@2556: for (typename ResPath::EdgeIt e(cycle); e != INVALID; ++e) { kpeter@2581: if (delta == 0 || _res_graph->rescap(e) < delta) kpeter@2581: delta = _res_graph->rescap(e); kpeter@2556: } deba@2440: kpeter@2556: // Augmenting along the cycle kpeter@2556: for (typename ResPath::EdgeIt e(cycle); e != INVALID; ++e) kpeter@2581: _res_graph->augment(e, delta); deba@2440: kpeter@2556: // Finding the minimum cycle mean for the modified residual kpeter@2556: // graph kpeter@2556: mmc.reset(); kpeter@2556: if (!mmc.findMinMean()) break; kpeter@2556: } deba@2440: } deba@2440: kpeter@2581: // Computing node potentials kpeter@2581: BellmanFord bf(*_res_graph, _res_cost); kpeter@2581: bf.init(0); bf.start(); kpeter@2581: for (NodeIt n(_graph); n != INVALID; ++n) kpeter@2581: (*_potential)[n] = bf.dist(n); deba@2440: } deba@2440: deba@2440: }; //class CycleCanceling deba@2440: deba@2440: ///@} deba@2440: deba@2440: } //namespace lemon deba@2440: deba@2440: #endif //LEMON_CYCLE_CANCELING_H