gravatar
alpar (Alpar Juttner)
alpar@cs.elte.hu
Port Suurballe algorithm from svn -r3512
0 2 3
default
5 files changed with 708 insertions and 1 deletions:
↑ Collapse diff ↑
Ignore white space 6 line context
1
/* -*- C++ -*-
2
 *
3
 * This file is a part of LEMON, a generic C++ optimization library
4
 *
5
 * Copyright (C) 2003-2008
6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
8
 *
9
 * Permission to use, modify and distribute this software is granted
10
 * provided that this copyright notice appears in all copies. For
11
 * precise terms see the accompanying LICENSE file.
12
 *
13
 * This software is provided "AS IS" with no warranty of any kind,
14
 * express or implied, and with no claim as to its suitability for any
15
 * purpose.
16
 *
17
 */
18

	
19
#ifndef LEMON_SUURBALLE_H
20
#define LEMON_SUURBALLE_H
21

	
22
///\ingroup shortest_path
23
///\file
24
///\brief An algorithm for finding arc-disjoint paths between two
25
/// nodes having minimum total length.
26

	
27
#include <vector>
28
#include <lemon/bin_heap.h>
29
#include <lemon/path.h>
30

	
31
namespace lemon {
32

	
33
  /// \addtogroup shortest_path
34
  /// @{
35

	
36
  /// \brief Implementation of an algorithm for finding arc-disjoint
37
  /// paths between two nodes having minimum total length.
38
  ///
39
  /// \ref lemon::Suurballe "Suurballe" implements an algorithm for
40
  /// finding arc-disjoint paths having minimum total length (cost)
41
  /// from a given source node to a given target node in a directed
42
  /// digraph.
43
  ///
44
  /// In fact, this implementation is the specialization of the
45
  /// \ref CapacityScaling "successive shortest path" algorithm.
46
  ///
47
  /// \tparam Digraph The directed digraph type the algorithm runs on.
48
  /// \tparam LengthMap The type of the length (cost) map.
49
  ///
50
  /// \warning Length values should be \e non-negative \e integers.
51
  ///
52
  /// \note For finding node-disjoint paths this algorithm can be used
53
  /// with \ref SplitDigraphAdaptor.
54
  ///
55
  /// \author Attila Bernath and Peter Kovacs
56
  
57
  template < typename Digraph, 
58
             typename LengthMap = typename Digraph::template ArcMap<int> >
59
  class Suurballe
60
  {
61
    TEMPLATE_DIGRAPH_TYPEDEFS(Digraph);
62

	
63
    typedef typename LengthMap::Value Length;
64
    typedef ConstMap<Arc, int> ConstArcMap;
65
    typedef typename Digraph::template NodeMap<Arc> PredMap;
66

	
67
  public:
68

	
69
    /// The type of the flow map.
70
    typedef typename Digraph::template ArcMap<int> FlowMap;
71
    /// The type of the potential map.
72
    typedef typename Digraph::template NodeMap<Length> PotentialMap;
73
    /// The type of the path structures.
74
    typedef SimplePath<Digraph> Path;
75

	
76
  private:
77
  
78
    /// \brief Special implementation of the \ref Dijkstra algorithm
79
    /// for finding shortest paths in the residual network.
80
    ///
81
    /// \ref ResidualDijkstra is a special implementation of the
82
    /// \ref Dijkstra algorithm for finding shortest paths in the
83
    /// residual network of the digraph with respect to the reduced arc
84
    /// lengths and modifying the node potentials according to the
85
    /// distance of the nodes.
86
    class ResidualDijkstra
87
    {
88
      typedef typename Digraph::template NodeMap<int> HeapCrossRef;
89
      typedef BinHeap<Length, HeapCrossRef> Heap;
90

	
91
    private:
92

	
93
      // The directed digraph the algorithm runs on
94
      const Digraph &_graph;
95

	
96
      // The main maps
97
      const FlowMap &_flow;
98
      const LengthMap &_length;
99
      PotentialMap &_potential;
100

	
101
      // The distance map
102
      PotentialMap _dist;
103
      // The pred arc map
104
      PredMap &_pred;
105
      // The processed (i.e. permanently labeled) nodes
106
      std::vector<Node> _proc_nodes;
107
      
108
      Node _s;
109
      Node _t;
110

	
111
    public:
112

	
113
      /// Constructor.
114
      ResidualDijkstra( const Digraph &digraph,
115
                        const FlowMap &flow,
116
                        const LengthMap &length,
117
                        PotentialMap &potential,
118
                        PredMap &pred,
119
                        Node s, Node t ) :
120
        _graph(digraph), _flow(flow), _length(length), _potential(potential),
121
        _dist(digraph), _pred(pred), _s(s), _t(t) {}
122

	
123
      /// \brief Runs the algorithm. Returns \c true if a path is found
124
      /// from the source node to the target node.
125
      bool run() {
126
        HeapCrossRef heap_cross_ref(_graph, Heap::PRE_HEAP);
127
        Heap heap(heap_cross_ref);
128
        heap.push(_s, 0);
129
        _pred[_s] = INVALID;
130
        _proc_nodes.clear();
131

	
132
        // Processing nodes
133
        while (!heap.empty() && heap.top() != _t) {
134
          Node u = heap.top(), v;
135
          Length d = heap.prio() + _potential[u], nd;
136
          _dist[u] = heap.prio();
137
          heap.pop();
138
          _proc_nodes.push_back(u);
139

	
140
          // Traversing outgoing arcs
141
          for (OutArcIt e(_graph, u); e != INVALID; ++e) {
142
            if (_flow[e] == 0) {
143
              v = _graph.target(e);
144
              switch(heap.state(v)) {
145
              case Heap::PRE_HEAP:
146
                heap.push(v, d + _length[e] - _potential[v]);
147
                _pred[v] = e;
148
                break;
149
              case Heap::IN_HEAP:
150
                nd = d + _length[e] - _potential[v];
151
                if (nd < heap[v]) {
152
                  heap.decrease(v, nd);
153
                  _pred[v] = e;
154
                }
155
                break;
156
              case Heap::POST_HEAP:
157
                break;
158
              }
159
            }
160
          }
161

	
162
          // Traversing incoming arcs
163
          for (InArcIt e(_graph, u); e != INVALID; ++e) {
164
            if (_flow[e] == 1) {
165
              v = _graph.source(e);
166
              switch(heap.state(v)) {
167
              case Heap::PRE_HEAP:
168
                heap.push(v, d - _length[e] - _potential[v]);
169
                _pred[v] = e;
170
                break;
171
              case Heap::IN_HEAP:
172
                nd = d - _length[e] - _potential[v];
173
                if (nd < heap[v]) {
174
                  heap.decrease(v, nd);
175
                  _pred[v] = e;
176
                }
177
                break;
178
              case Heap::POST_HEAP:
179
                break;
180
              }
181
            }
182
          }
183
        }
184
        if (heap.empty()) return false;
185

	
186
        // Updating potentials of processed nodes
187
        Length t_dist = heap.prio();
188
        for (int i = 0; i < int(_proc_nodes.size()); ++i)
189
          _potential[_proc_nodes[i]] += _dist[_proc_nodes[i]] - t_dist;
190
        return true;
191
      }
192

	
193
    }; //class ResidualDijkstra
194

	
195
  private:
196

	
197
    // The directed digraph the algorithm runs on
198
    const Digraph &_graph;
199
    // The length map
200
    const LengthMap &_length;
201
    
202
    // Arc map of the current flow
203
    FlowMap *_flow;
204
    bool _local_flow;
205
    // Node map of the current potentials
206
    PotentialMap *_potential;
207
    bool _local_potential;
208

	
209
    // The source node
210
    Node _source;
211
    // The target node
212
    Node _target;
213

	
214
    // Container to store the found paths
215
    std::vector< SimplePath<Digraph> > paths;
216
    int _path_num;
217

	
218
    // The pred arc map
219
    PredMap _pred;
220
    // Implementation of the Dijkstra algorithm for finding augmenting
221
    // shortest paths in the residual network
222
    ResidualDijkstra *_dijkstra;
223

	
224
  public:
225

	
226
    /// \brief Constructor.
227
    ///
228
    /// Constructor.
229
    ///
230
    /// \param digraph The directed digraph the algorithm runs on.
231
    /// \param length The length (cost) values of the arcs.
232
    /// \param s The source node.
233
    /// \param t The target node.
234
    Suurballe( const Digraph &digraph,
235
               const LengthMap &length,
236
               Node s, Node t ) :
237
      _graph(digraph), _length(length), _flow(0), _local_flow(false),
238
      _potential(0), _local_potential(false), _source(s), _target(t),
239
      _pred(digraph) {}
240

	
241
    /// Destructor.
242
    ~Suurballe() {
243
      if (_local_flow) delete _flow;
244
      if (_local_potential) delete _potential;
245
      delete _dijkstra;
246
    }
247

	
248
    /// \brief Sets the flow map.
249
    ///
250
    /// Sets the flow map.
251
    ///
252
    /// The found flow contains only 0 and 1 values. It is the union of
253
    /// the found arc-disjoint paths.
254
    ///
255
    /// \return \c (*this)
256
    Suurballe& flowMap(FlowMap &map) {
257
      if (_local_flow) {
258
        delete _flow;
259
        _local_flow = false;
260
      }
261
      _flow = &map;
262
      return *this;
263
    }
264

	
265
    /// \brief Sets the potential map.
266
    ///
267
    /// Sets the potential map.
268
    ///
269
    /// The potentials provide the dual solution of the underlying 
270
    /// minimum cost flow problem.
271
    ///
272
    /// \return \c (*this)
273
    Suurballe& potentialMap(PotentialMap &map) {
274
      if (_local_potential) {
275
        delete _potential;
276
        _local_potential = false;
277
      }
278
      _potential = &map;
279
      return *this;
280
    }
281

	
282
    /// \name Execution control
283
    /// The simplest way to execute the algorithm is to call the run()
284
    /// function.
285
    /// \n
286
    /// If you only need the flow that is the union of the found
287
    /// arc-disjoint paths, you may call init() and findFlow().
288

	
289
    /// @{
290

	
291
    /// \brief Runs the algorithm.
292
    ///
293
    /// Runs the algorithm.
294
    ///
295
    /// \param k The number of paths to be found.
296
    ///
297
    /// \return \c k if there are at least \c k arc-disjoint paths
298
    /// from \c s to \c t. Otherwise it returns the number of
299
    /// arc-disjoint paths found.
300
    ///
301
    /// \note Apart from the return value, <tt>s.run(k)</tt> is just a
302
    /// shortcut of the following code.
303
    /// \code
304
    ///   s.init();
305
    ///   s.findFlow(k);
306
    ///   s.findPaths();
307
    /// \endcode
308
    int run(int k = 2) {
309
      init();
310
      findFlow(k);
311
      findPaths();
312
      return _path_num;
313
    }
314

	
315
    /// \brief Initializes the algorithm.
316
    ///
317
    /// Initializes the algorithm.
318
    void init() {
319
      // Initializing maps
320
      if (!_flow) {
321
        _flow = new FlowMap(_graph);
322
        _local_flow = true;
323
      }
324
      if (!_potential) {
325
        _potential = new PotentialMap(_graph);
326
        _local_potential = true;
327
      }
328
      for (ArcIt e(_graph); e != INVALID; ++e) (*_flow)[e] = 0;
329
      for (NodeIt n(_graph); n != INVALID; ++n) (*_potential)[n] = 0;
330

	
331
      _dijkstra = new ResidualDijkstra( _graph, *_flow, _length, 
332
                                        *_potential, _pred,
333
                                        _source, _target );
334
    }
335

	
336
    /// \brief Executes the successive shortest path algorithm to find
337
    /// an optimal flow.
338
    ///
339
    /// Executes the successive shortest path algorithm to find a
340
    /// minimum cost flow, which is the union of \c k or less
341
    /// arc-disjoint paths.
342
    ///
343
    /// \return \c k if there are at least \c k arc-disjoint paths
344
    /// from \c s to \c t. Otherwise it returns the number of
345
    /// arc-disjoint paths found.
346
    ///
347
    /// \pre \ref init() must be called before using this function.
348
    int findFlow(int k = 2) {
349
      // Finding shortest paths
350
      _path_num = 0;
351
      while (_path_num < k) {
352
        // Running Dijkstra
353
        if (!_dijkstra->run()) break;
354
        ++_path_num;
355

	
356
        // Setting the flow along the found shortest path
357
        Node u = _target;
358
        Arc e;
359
        while ((e = _pred[u]) != INVALID) {
360
          if (u == _graph.target(e)) {
361
            (*_flow)[e] = 1;
362
            u = _graph.source(e);
363
          } else {
364
            (*_flow)[e] = 0;
365
            u = _graph.target(e);
366
          }
367
        }
368
      }
369
      return _path_num;
370
    }
371
    
372
    /// \brief Computes the paths from the flow.
373
    ///
374
    /// Computes the paths from the flow.
375
    ///
376
    /// \pre \ref init() and \ref findFlow() must be called before using
377
    /// this function.
378
    void findPaths() {
379
      // Creating the residual flow map (the union of the paths not
380
      // found so far)
381
      FlowMap res_flow(_graph);
382
      for(ArcIt a(_graph);a!=INVALID;++a) res_flow[a]=(*_flow)[a];
383

	
384
      paths.clear();
385
      paths.resize(_path_num);
386
      for (int i = 0; i < _path_num; ++i) {
387
        Node n = _source;
388
        while (n != _target) {
389
          OutArcIt e(_graph, n);
390
          for ( ; res_flow[e] == 0; ++e) ;
391
          n = _graph.target(e);
392
          paths[i].addBack(e);
393
          res_flow[e] = 0;
394
        }
395
      }
396
    }
397

	
398
    /// @}
399

	
400
    /// \name Query Functions
401
    /// The result of the algorithm can be obtained using these
402
    /// functions.
403
    /// \n The algorithm should be executed before using them.
404

	
405
    /// @{
406

	
407
    /// \brief Returns a const reference to the arc map storing the
408
    /// found flow.
409
    ///
410
    /// Returns a const reference to the arc map storing the flow that
411
    /// is the union of the found arc-disjoint paths.
412
    ///
413
    /// \pre \ref run() or findFlow() must be called before using this
414
    /// function.
415
    const FlowMap& flowMap() const {
416
      return *_flow;
417
    }
418

	
419
    /// \brief Returns a const reference to the node map storing the
420
    /// found potentials (the dual solution).
421
    ///
422
    /// Returns a const reference to the node map storing the found
423
    /// potentials that provide the dual solution of the underlying 
424
    /// minimum cost flow problem.
425
    ///
426
    /// \pre \ref run() or findFlow() must be called before using this
427
    /// function.
428
    const PotentialMap& potentialMap() const {
429
      return *_potential;
430
    }
431

	
432
    /// \brief Returns the flow on the given arc.
433
    ///
434
    /// Returns the flow on the given arc.
435
    /// It is \c 1 if the arc is involved in one of the found paths,
436
    /// otherwise it is \c 0.
437
    ///
438
    /// \pre \ref run() or findFlow() must be called before using this
439
    /// function.
440
    int flow(const Arc& arc) const {
441
      return (*_flow)[arc];
442
    }
443

	
444
    /// \brief Returns the potential of the given node.
445
    ///
446
    /// Returns the potential of the given node.
447
    ///
448
    /// \pre \ref run() or findFlow() must be called before using this
449
    /// function.
450
    Length potential(const Node& node) const {
451
      return (*_potential)[node];
452
    }
453

	
454
    /// \brief Returns the total length (cost) of the found paths (flow).
455
    ///
456
    /// Returns the total length (cost) of the found paths (flow).
457
    /// The complexity of the function is \f$ O(e) \f$.
458
    ///
459
    /// \pre \ref run() or findFlow() must be called before using this
460
    /// function.
461
    Length totalLength() const {
462
      Length c = 0;
463
      for (ArcIt e(_graph); e != INVALID; ++e)
464
        c += (*_flow)[e] * _length[e];
465
      return c;
466
    }
467

	
468
    /// \brief Returns the number of the found paths.
469
    ///
470
    /// Returns the number of the found paths.
471
    ///
472
    /// \pre \ref run() or findFlow() must be called before using this
473
    /// function.
474
    int pathNum() const {
475
      return _path_num;
476
    }
477

	
478
    /// \brief Returns a const reference to the specified path.
479
    ///
480
    /// Returns a const reference to the specified path.
481
    ///
482
    /// \param i The function returns the \c i-th path.
483
    /// \c i must be between \c 0 and <tt>%pathNum()-1</tt>.
484
    ///
485
    /// \pre \ref run() or findPaths() must be called before using this
486
    /// function.
487
    Path path(int i) const {
488
      return paths[i];
489
    }
490

	
491
    /// @}
492

	
493
  }; //class Suurballe
494

	
495
  ///@}
496

	
497
} //namespace lemon
498

	
499
#endif //LEMON_SUURBALLE_H
Ignore white space 6 line context
1
@nodes
2
label	supply1	supply2	supply3
3
1	0	20	27	
4
2	0	-4	0		
5
3	0	0	0	
6
4	0	0	0	
7
5	0	9	0	
8
6	0	-6	0	
9
7	0	0	0	
10
8	0	0	0	
11
9	0	3	0	
12
10	0	-2	0	
13
11	0	0	0		
14
12	0	-20	-27	
15
               
16
@arcs
17
		cost	capacity	lower1	lower2
18
1	2	70	11		0	8
19
1	3	150	3		0	1
20
1	4	80	15		0	2
21
2	8	80	12		0	0
22
3	5	140	5		0	3
23
4	6	60	10		0	1
24
4	7	80	2		0	0
25
4	8	110	3		0	0
26
5	7	60	14		0	0
27
5	11	120	12		0	0
28
6	3	0	3		0	0
29
6	9	140	4		0	0
30
6	10	90	8		0	0
31
7	1	30	5		0	0
32
8	12	60	16		0	4
33
9	12	50	6		0	0
34
10	12	70	13		0	5
35
10	2	100	7		0	0
36
10	7	60	10		0	0
37
11	10	20	14		0	6
38
12	11	30	10		0	0
39

	
40
@attributes
41
source	1
42
target	12
43

	
44
@end
Ignore white space 6 line context
1
/* -*- C++ -*-
2
 *
3
 * This file is a part of LEMON, a generic C++ optimization library
4
 *
5
 * Copyright (C) 2003-2008
6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
8
 *
9
 * Permission to use, modify and distribute this software is granted
10
 * provided that this copyright notice appears in all copies. For
11
 * precise terms see the accompanying LICENSE file.
12
 *
13
 * This software is provided "AS IS" with no warranty of any kind,
14
 * express or implied, and with no claim as to its suitability for any
15
 * purpose.
16
 *
17
 */
18

	
19
#include <iostream>
20
#include <fstream>
21

	
22
#include <lemon/list_graph.h>
23
#include <lemon/lgf_reader.h>
24
#include <lemon/path.h>
25
#include <lemon/suurballe.h>
26

	
27
#include "test_tools.h"
28

	
29
using namespace lemon;
30

	
31
// Checks the feasibility of the flow
32
template <typename Digraph, typename FlowMap>
33
bool checkFlow( const Digraph& gr, const FlowMap& flow, 
34
                typename Digraph::Node s, typename Digraph::Node t,
35
                int value )
36
{
37
  TEMPLATE_DIGRAPH_TYPEDEFS(Digraph);
38
  for (ArcIt e(gr); e != INVALID; ++e)
39
    if (!(flow[e] == 0 || flow[e] == 1)) return false;
40

	
41
  for (NodeIt n(gr); n != INVALID; ++n) {
42
    int sum = 0;
43
    for (OutArcIt e(gr, n); e != INVALID; ++e)
44
      sum += flow[e];
45
    for (InArcIt e(gr, n); e != INVALID; ++e)
46
      sum -= flow[e];
47
    if (n == s && sum != value) return false;
48
    if (n == t && sum != -value) return false;
49
    if (n != s && n != t && sum != 0) return false;
50
  }
51

	
52
  return true;
53
}
54

	
55
// Checks the optimalitiy of the flow
56
template < typename Digraph, typename CostMap, 
57
           typename FlowMap, typename PotentialMap >
58
bool checkOptimality( const Digraph& gr, const CostMap& cost,
59
                      const FlowMap& flow, const PotentialMap& pi )
60
{
61
  // Checking the Complementary Slackness optimality condition
62
  TEMPLATE_DIGRAPH_TYPEDEFS(Digraph);
63
  bool opt = true;
64
  for (ArcIt e(gr); e != INVALID; ++e) {
65
    typename CostMap::Value red_cost =
66
      cost[e] + pi[gr.source(e)] - pi[gr.target(e)];
67
    opt = (flow[e] == 0 && red_cost >= 0) ||
68
          (flow[e] == 1 && red_cost <= 0);
69
    if (!opt) break;
70
  }
71
  return opt;
72
}
73

	
74
// Checks a path
75
template < typename Digraph, typename Path >
76
bool checkPath( const Digraph& gr, const Path& path,
77
                typename Digraph::Node s, typename Digraph::Node t)
78
{
79
  // Checking the Complementary Slackness optimality condition
80
  TEMPLATE_DIGRAPH_TYPEDEFS(Digraph);
81
  Node n = s;
82
  for (int i = 0; i < path.length(); ++i) {
83
    if (gr.source(path.nth(i)) != n) return false;
84
    n = gr.target(path.nth(i));
85
  }
86
  return n == t;
87
}
88

	
89

	
90
int main()
91
{
92
  DIGRAPH_TYPEDEFS(ListDigraph);
93

	
94
  // Reading the test digraph
95
  ListDigraph digraph;
96
  ListDigraph::ArcMap<int> length(digraph);
97
  Node source, target;
98

	
99
  std::string fname;
100
  if(getenv("srcdir"))
101
    fname = std::string(getenv("srcdir"));
102
  else fname = ".";
103
  fname += "/test/min_cost_flow_test.lgf";
104

	
105
  std::ifstream input(fname.c_str());
106
  check(input, "Input file '" << fname << "' not found");
107
  DigraphReader<ListDigraph>(digraph, input).
108
    arcMap("cost", length).
109
    node("source", source).
110
    node("target", target).
111
    run();
112
  input.close();
113
  
114
  // Finding 2 paths
115
  {
116
    Suurballe<ListDigraph> suurballe(digraph, length, source, target);
117
    check(suurballe.run(2) == 2, "Wrong number of paths");
118
    check(checkFlow(digraph, suurballe.flowMap(), source, target, 2),
119
          "The flow is not feasible");
120
    check(suurballe.totalLength() == 510, "The flow is not optimal");
121
    check(checkOptimality(digraph, length, suurballe.flowMap(), 
122
                          suurballe.potentialMap()),
123
          "Wrong potentials");
124
    for (int i = 0; i < suurballe.pathNum(); ++i)
125
      check(checkPath(digraph, suurballe.path(i), source, target),
126
            "Wrong path");
127
  }
128

	
129
  // Finding 3 paths
130
  {
131
    Suurballe<ListDigraph> suurballe(digraph, length, source, target);
132
    check(suurballe.run(3) == 3, "Wrong number of paths");
133
    check(checkFlow(digraph, suurballe.flowMap(), source, target, 3),
134
          "The flow is not feasible");
135
    check(suurballe.totalLength() == 1040, "The flow is not optimal");
136
    check(checkOptimality(digraph, length, suurballe.flowMap(), 
137
                          suurballe.potentialMap()),
138
          "Wrong potentials");
139
    for (int i = 0; i < suurballe.pathNum(); ++i)
140
      check(checkPath(digraph, suurballe.path(i), source, target),
141
            "Wrong path");
142
  }
143

	
144
  // Finding 5 paths (only 3 can be found)
145
  {
146
    Suurballe<ListDigraph> suurballe(digraph, length, source, target);
147
    check(suurballe.run(5) == 3, "Wrong number of paths");
148
    check(checkFlow(digraph, suurballe.flowMap(), source, target, 3),
149
          "The flow is not feasible");
150
    check(suurballe.totalLength() == 1040, "The flow is not optimal");
151
    check(checkOptimality(digraph, length, suurballe.flowMap(), 
152
                          suurballe.potentialMap()),
153
          "Wrong potentials");
154
    for (int i = 0; i < suurballe.pathNum(); ++i)
155
      check(checkPath(digraph, suurballe.path(i), source, target),
156
            "Wrong path");
157
  }
158

	
159
  return 0;
160
}
Ignore white space 6 line context
... ...
@@ -40,6 +40,7 @@
40 40
	lemon/path.h \
41 41
        lemon/random.h \
42 42
	lemon/smart_graph.h \
43
	lemon/suurballe.h \
43 44
        lemon/time_measure.h \
44 45
        lemon/tolerance.h \
45 46
	lemon/unionfind.h
Ignore white space 6 line context
1 1
EXTRA_DIST += \
2
	test/CMakeLists.txt
2
	test/CMakeLists.txt \
3
        test/min_cost_flow_test.lgf
3 4

	
4 5
noinst_HEADERS += \
5 6
	test/graph_test.h \
... ...
@@ -22,6 +23,7 @@
22 23
	test/max_matching_test \
23 24
        test/random_test \
24 25
        test/path_test \
26
        test/suurballe_test \
25 27
        test/test_tools_fail \
26 28
        test/test_tools_pass \
27 29
        test/time_measure_test \
... ...
@@ -45,6 +47,7 @@
45 47
test_maps_test_SOURCES = test/maps_test.cc
46 48
test_max_matching_test_SOURCES = test/max_matching_test.cc
47 49
test_path_test_SOURCES = test/path_test.cc
50
test_suurballe_test_SOURCES = test/suurballe_test.cc
48 51
test_random_test_SOURCES = test/random_test.cc
49 52
test_test_tools_fail_SOURCES = test/test_tools_fail.cc
50 53
test_test_tools_pass_SOURCES = test/test_tools_pass.cc
0 comments (0 inline)