Coverage Report

Created: 2026-03-07 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/flow/st-cuts.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2010-2021  The igraph development team
4
5
   This program is free software; you can redistribute it and/or modify
6
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; either version 2 of the License, or
8
   (at your option) any later version.
9
10
   This program is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
   GNU General Public License for more details.
14
15
   You should have received a copy of the GNU General Public License
16
   along with this program; if not, write to the Free Software
17
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
   02110-1301 USA
19
20
*/
21
22
#include "igraph_flow.h"
23
24
#include "igraph_adjlist.h"
25
#include "igraph_bitset.h"
26
#include "igraph_constants.h"
27
#include "igraph_constructors.h"
28
#include "igraph_components.h"
29
#include "igraph_cycles.h"
30
#include "igraph_error.h"
31
#include "igraph_interface.h"
32
#include "igraph_operators.h"
33
#include "igraph_stack.h"
34
#include "igraph_visitor.h"
35
36
#include "core/estack.h"
37
#include "core/marked_queue.h"
38
#include "flow/flow_internal.h"
39
#include "graph/attributes.h"
40
#include "math/safe_intop.h"
41
42
typedef igraph_error_t igraph_provan_shier_pivot_t(const igraph_t *graph,
43
        const igraph_marked_queue_int_t *S,
44
        const igraph_estack_t *T,
45
        igraph_int_t source,
46
        igraph_int_t target,
47
        igraph_int_t *v,
48
        igraph_vector_int_t *Isv,
49
        void *arg);
50
51
/**
52
 * \function igraph_even_tarjan_reduction
53
 * \brief Even-Tarjan reduction of a graph.
54
 *
55
 * A digraph is created with twice as many vertices and edges. For each
56
 * original vertex \c i, two vertices <code>i' = i</code> and
57
 * <code>i'' = i' + n</code> are created,
58
 * with a directed edge from <code>i'</code> to <code>i''</code>.
59
 * For each original directed edge from \c i to \c j, two new edges are created,
60
 * from <code>i'</code> to <code>j''</code> and from <code>i''</code>
61
 * to <code>j'</code>.
62
 *
63
 * </para><para>This reduction is used in the paper (observation 2):
64
 * Arkady Kanevsky: Finding all minimum-size separating vertex sets in
65
 * a graph, Networks 23, 533--541, 1993.
66
 *
67
 * </para><para>The original paper where this reduction was conceived is
68
 * Shimon Even and R. Endre Tarjan: Network Flow and Testing Graph
69
 * Connectivity, SIAM J. Comput., 4(4), 507–518.
70
 * https://doi.org/10.1137/0204043
71
 *
72
 * \param graph A graph. Although directness is not checked, this function
73
 *        is commonly used only on directed graphs.
74
 * \param graphbar Pointer to a new directed graph that will contain the
75
 *        reduction, with twice as many vertices and edges.
76
 * \param capacity Pointer to an initialized vector or a null pointer. If
77
 *        not a null pointer, then it will be filled the capacity from
78
 *        the reduction: the first |E| elements are 1, the remaining |E|
79
 *        are equal to |V| (which is used to indicate infinity).
80
 * \return Error code.
81
 *
82
 * Time complexity: O(|E|+|V|).
83
 *
84
 * \example examples/simple/even_tarjan.c
85
 */
86
87
igraph_error_t igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar,
88
0
        igraph_vector_t *capacity) {
89
90
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
91
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
92
93
0
    igraph_int_t new_no_of_nodes;
94
0
    igraph_int_t new_no_of_edges = no_of_edges * 2;
95
96
0
    igraph_vector_int_t edges;
97
0
    igraph_int_t edgeptr = 0, capptr = 0;
98
0
    igraph_int_t i;
99
100
0
    IGRAPH_SAFE_MULT(no_of_nodes, 2, &new_no_of_nodes);
101
0
    IGRAPH_SAFE_ADD(new_no_of_edges, no_of_nodes, &new_no_of_edges);
102
103
    /* To ensure the size of the edges vector will not overflow. */
104
0
    if (new_no_of_edges > IGRAPH_ECOUNT_MAX) {
105
0
        IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW);
106
0
    }
107
108
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, new_no_of_edges * 2);
109
110
0
    if (capacity) {
111
0
        IGRAPH_CHECK(igraph_vector_resize(capacity, new_no_of_edges));
112
0
    }
113
114
    /* Every vertex 'i' is replaced by two vertices, i' and i'' */
115
    /* id[i'] := id[i] ; id[i''] := id[i] + no_of_nodes */
116
117
    /* One edge for each original vertex, for i, we add (i',i'') */
118
0
    for (i = 0; i < no_of_nodes; i++) {
119
0
        VECTOR(edges)[edgeptr++] = i;
120
0
        VECTOR(edges)[edgeptr++] = i + no_of_nodes;
121
0
        if (capacity) {
122
0
            VECTOR(*capacity)[capptr++] = 1.0;
123
0
        }
124
0
    }
125
126
    /* Two news edges for each original edge
127
       (from,to) becomes (from'',to'), (to'',from') */
128
0
    for (i = 0; i < no_of_edges; i++) {
129
0
        igraph_int_t from = IGRAPH_FROM(graph, i);
130
0
        igraph_int_t to = IGRAPH_TO(graph, i);
131
0
        VECTOR(edges)[edgeptr++] = from + no_of_nodes;
132
0
        VECTOR(edges)[edgeptr++] = to;
133
0
        VECTOR(edges)[edgeptr++] = to + no_of_nodes;
134
0
        VECTOR(edges)[edgeptr++] = from;
135
0
        if (capacity) {
136
0
            VECTOR(*capacity)[capptr++] = no_of_nodes; /* TODO: should be Inf */
137
0
            VECTOR(*capacity)[capptr++] = no_of_nodes; /* TODO: should be Inf */
138
0
        }
139
0
    }
140
141
0
    IGRAPH_CHECK(igraph_create(graphbar, &edges, new_no_of_nodes,
142
0
                               IGRAPH_DIRECTED));
143
144
0
    igraph_vector_int_destroy(&edges);
145
0
    IGRAPH_FINALLY_CLEAN(1);
146
147
0
    return IGRAPH_SUCCESS;
148
0
}
149
150
static igraph_error_t igraph_i_residual_graph(const igraph_t *graph,
151
        const igraph_vector_t *capacity,
152
        igraph_t *residual,
153
        igraph_vector_t *residual_capacity,
154
        const igraph_vector_t *flow,
155
0
        igraph_vector_int_t *tmp) {
156
157
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
158
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
159
0
    igraph_int_t i, no_new_edges = 0;
160
0
    igraph_int_t edgeptr = 0, capptr = 0;
161
162
0
    for (i = 0; i < no_of_edges; i++) {
163
0
        if (VECTOR(*flow)[i] < VECTOR(*capacity)[i]) {
164
0
            no_new_edges++;
165
0
        }
166
0
    }
167
168
0
    IGRAPH_CHECK(igraph_vector_int_resize(tmp, no_new_edges * 2));
169
0
    if (residual_capacity) {
170
0
        IGRAPH_CHECK(igraph_vector_resize(residual_capacity, no_new_edges));
171
0
    }
172
173
0
    for (i = 0; i < no_of_edges; i++) {
174
0
        igraph_real_t c = VECTOR(*capacity)[i] - VECTOR(*flow)[i];
175
0
        if (c > 0) {
176
0
            igraph_int_t from = IGRAPH_FROM(graph, i);
177
0
            igraph_int_t to = IGRAPH_TO(graph, i);
178
0
            VECTOR(*tmp)[edgeptr++] = from;
179
0
            VECTOR(*tmp)[edgeptr++] = to;
180
0
            if (residual_capacity) {
181
0
                VECTOR(*residual_capacity)[capptr++] = c;
182
0
            }
183
0
        }
184
0
    }
185
186
0
    IGRAPH_CHECK(igraph_create(residual, tmp, no_of_nodes,
187
0
                               IGRAPH_DIRECTED));
188
189
0
    return IGRAPH_SUCCESS;
190
0
}
191
192
igraph_error_t igraph_residual_graph(const igraph_t *graph,
193
                                     const igraph_vector_t *capacity,
194
                                     igraph_t *residual,
195
                                     igraph_vector_t *residual_capacity,
196
0
                                     const igraph_vector_t *flow) {
197
198
0
    igraph_vector_int_t tmp;
199
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
200
201
0
    if (igraph_vector_size(capacity) != no_of_edges) {
202
0
        IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL);
203
0
    }
204
0
    if (igraph_vector_size(flow) != no_of_edges) {
205
0
        IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL);
206
0
    }
207
208
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0);
209
210
0
    IGRAPH_CHECK(igraph_i_residual_graph(graph, capacity, residual,
211
0
                                         residual_capacity, flow, &tmp));
212
213
0
    igraph_vector_int_destroy(&tmp);
214
0
    IGRAPH_FINALLY_CLEAN(1);
215
216
0
    return IGRAPH_SUCCESS;
217
0
}
218
219
static igraph_error_t igraph_i_reverse_residual_graph(const igraph_t *graph,
220
        const igraph_vector_t *capacity,
221
        igraph_t *residual,
222
        const igraph_vector_t *flow,
223
0
        igraph_vector_int_t *tmp) {
224
225
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
226
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
227
0
    igraph_int_t i, no_new_edges = 0;
228
0
    igraph_int_t edgeptr = 0;
229
230
0
    for (i = 0; i < no_of_edges; i++) {
231
0
        igraph_real_t cap = capacity ? VECTOR(*capacity)[i] : 1.0;
232
0
        if (VECTOR(*flow)[i] > 0) {
233
0
            no_new_edges++;
234
0
        }
235
0
        if (VECTOR(*flow)[i] < cap) {
236
0
            no_new_edges++;
237
0
        }
238
0
    }
239
240
0
    IGRAPH_CHECK(igraph_vector_int_resize(tmp, no_new_edges * 2));
241
242
0
    for (i = 0; i < no_of_edges; i++) {
243
0
        igraph_int_t from = IGRAPH_FROM(graph, i);
244
0
        igraph_int_t to = IGRAPH_TO(graph, i);
245
0
        igraph_real_t cap = capacity ? VECTOR(*capacity)[i] : 1.0;
246
0
        if (VECTOR(*flow)[i] > 0) {
247
0
            VECTOR(*tmp)[edgeptr++] = from;
248
0
            VECTOR(*tmp)[edgeptr++] = to;
249
0
        }
250
0
        if (VECTOR(*flow)[i] < cap) {
251
0
            VECTOR(*tmp)[edgeptr++] = to;
252
0
            VECTOR(*tmp)[edgeptr++] = from;
253
0
        }
254
0
    }
255
256
0
    IGRAPH_CHECK(igraph_create(residual, tmp, no_of_nodes,
257
0
                               IGRAPH_DIRECTED));
258
259
0
    return IGRAPH_SUCCESS;
260
0
}
261
262
igraph_error_t igraph_reverse_residual_graph(const igraph_t *graph,
263
        const igraph_vector_t *capacity,
264
        igraph_t *residual,
265
0
        const igraph_vector_t *flow) {
266
0
    igraph_vector_int_t tmp;
267
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
268
269
0
    if (capacity && igraph_vector_size(capacity) != no_of_edges) {
270
0
        IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL);
271
0
    }
272
0
    if (igraph_vector_size(flow) != no_of_edges) {
273
0
        IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL);
274
0
    }
275
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0);
276
277
0
    IGRAPH_CHECK(igraph_i_reverse_residual_graph(graph, capacity, residual,
278
0
                 flow, &tmp));
279
280
0
    igraph_vector_int_destroy(&tmp);
281
0
    IGRAPH_FINALLY_CLEAN(1);
282
283
0
    return IGRAPH_SUCCESS;
284
0
}
285
286
typedef struct igraph_i_dbucket_t {
287
    igraph_vector_int_t head;
288
    igraph_vector_int_t next;
289
} igraph_i_dbucket_t;
290
291
1.77k
static igraph_error_t igraph_i_dbucket_init(igraph_i_dbucket_t *buck, igraph_int_t size) {
292
1.77k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&buck->head, size);
293
1.77k
    IGRAPH_CHECK(igraph_vector_int_init(&buck->next, size));
294
1.77k
    IGRAPH_FINALLY_CLEAN(1);
295
1.77k
    return IGRAPH_SUCCESS;
296
1.77k
}
297
298
1.77k
static void igraph_i_dbucket_destroy(igraph_i_dbucket_t *buck) {
299
1.77k
    igraph_vector_int_destroy(&buck->head);
300
1.77k
    igraph_vector_int_destroy(&buck->next);
301
1.77k
}
302
303
static igraph_error_t igraph_i_dbucket_insert(igraph_i_dbucket_t *buck, igraph_int_t bid,
304
22.1k
        igraph_int_t elem) {
305
    /* Note: we can do this, since elem is not in any buckets */
306
22.1k
    VECTOR(buck->next)[elem] = VECTOR(buck->head)[bid];
307
22.1k
    VECTOR(buck->head)[bid] = elem + 1;
308
22.1k
    return IGRAPH_SUCCESS;
309
22.1k
}
310
311
static igraph_bool_t igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck,
312
44.2k
        igraph_int_t bid) {
313
44.2k
    return VECTOR(buck->head)[bid] == 0;
314
44.2k
}
315
316
22.1k
static igraph_int_t igraph_i_dbucket_delete(igraph_i_dbucket_t *buck, igraph_int_t bid) {
317
22.1k
    igraph_int_t elem = VECTOR(buck->head)[bid] - 1;
318
22.1k
    VECTOR(buck->head)[bid] = VECTOR(buck->next)[elem];
319
22.1k
    return elem;
320
22.1k
}
321
322
static igraph_error_t igraph_i_dominator_LINK(igraph_int_t v, igraph_int_t w,
323
22.1k
        igraph_vector_int_t *ancestor) {
324
22.1k
    VECTOR(*ancestor)[w] = v + 1;
325
22.1k
    return IGRAPH_SUCCESS;
326
22.1k
}
327
328
/* TODO: don't always reallocate path */
329
330
static igraph_error_t igraph_i_dominator_COMPRESS(igraph_int_t v,
331
        igraph_vector_int_t *ancestor,
332
        igraph_vector_int_t *label,
333
26.0k
        igraph_vector_int_t *semi) {
334
26.0k
    igraph_stack_int_t path;
335
26.0k
    igraph_int_t w = v;
336
26.0k
    igraph_int_t top, pretop;
337
338
26.0k
    IGRAPH_CHECK(igraph_stack_int_init(&path, 10));
339
26.0k
    IGRAPH_FINALLY(igraph_stack_int_destroy, &path);
340
341
64.5k
    while (VECTOR(*ancestor)[w] != 0) {
342
38.4k
        IGRAPH_CHECK(igraph_stack_int_push(&path, w));
343
38.4k
        w = VECTOR(*ancestor)[w] - 1;
344
38.4k
    }
345
346
26.0k
    top = igraph_stack_int_pop(&path);
347
38.4k
    while (!igraph_stack_int_empty(&path)) {
348
12.4k
        pretop = igraph_stack_int_pop(&path);
349
350
12.4k
        if (VECTOR(*semi)[VECTOR(*label)[top]] <
351
12.4k
            VECTOR(*semi)[VECTOR(*label)[pretop]]) {
352
9.53k
            VECTOR(*label)[pretop] = VECTOR(*label)[top];
353
9.53k
        }
354
12.4k
        VECTOR(*ancestor)[pretop] = VECTOR(*ancestor)[top];
355
356
12.4k
        top = pretop;
357
12.4k
    }
358
359
26.0k
    igraph_stack_int_destroy(&path);
360
26.0k
    IGRAPH_FINALLY_CLEAN(1);
361
362
26.0k
    return IGRAPH_SUCCESS;
363
26.0k
}
364
365
static igraph_int_t igraph_i_dominator_EVAL(igraph_int_t v,
366
        igraph_vector_int_t *ancestor,
367
        igraph_vector_int_t *label,
368
55.1k
        igraph_vector_int_t *semi) {
369
55.1k
    if (VECTOR(*ancestor)[v] == 0) {
370
29.1k
        return v;
371
29.1k
    } else {
372
26.0k
        igraph_i_dominator_COMPRESS(v, ancestor, label, semi);
373
26.0k
        return VECTOR(*label)[v];
374
26.0k
    }
375
55.1k
}
376
377
/* TODO: implement the faster version. */
378
379
/**
380
 * \function igraph_dominator_tree
381
 * \brief Calculates the dominator tree of a flowgraph.
382
 *
383
 * A flowgraph is a directed graph with a distinguished start (or
384
 * root) vertex r, such that for any vertex v, there is a path from r
385
 * to v. A vertex v dominates another vertex w (not equal to v), if
386
 * every path from r to w contains v. Vertex v is the immediate
387
 * dominator or w, v=idom(w), if v dominates w and every other
388
 * dominator of w dominates v. The edges {(idom(w), w)| w is not r}
389
 * form a directed tree, rooted at r, called the dominator tree of the
390
 * graph. Vertex v dominates vertex w if and only if v is an ancestor
391
 * of w in the dominator tree.
392
 *
393
 * </para><para>This function implements the Lengauer-Tarjan algorithm
394
 * to construct the dominator tree of a directed graph. For details
395
 * please see Thomas Lengauer, Robert Endre Tarjan: A fast algorithm
396
 * for finding dominators in a flowgraph, ACM Transactions on
397
 * Programming Languages and Systems (TOPLAS) I/1, 121--141, 1979.
398
 * https://doi.org/10.1145/357062.357071
399
 *
400
 * \param graph A directed graph. If it is not a flowgraph, and it
401
 *        contains some vertices not reachable from the root vertex,
402
 *        then these vertices will be collected in the \p leftout
403
 *        vector.
404
 * \param root The ID of the root (or source) vertex, this will be the
405
 *        root of the tree.
406
 * \param dom Pointer to an initialized vector or a null pointer. If
407
 *        not a null pointer, then the immediate dominator of each
408
 *        vertex will be stored here. For vertices that are not
409
 *        reachable from the root, <code>-2</code> is stored here. For
410
 *        the root vertex itself, <code>-1</code> is added.
411
 * \param domtree Pointer to an \em uninitialized <type>igraph_t</type>,
412
 *        or \c NULL. If not a null pointer, then the dominator tree
413
 *        is returned here. The graph contains the vertices that are unreachable
414
 *        from the root (if any), these will be isolates.
415
 *        Graph and vertex attributes are preserved, but edge attributes
416
 *        are discarded.
417
 * \param leftout Pointer to an initialized vector object, or \c NULL. If
418
 *        not \c NULL, then the IDs of the vertices that are unreachable
419
 *        from the root vertex (and thus not part of the dominator
420
 *        tree) are stored here.
421
 * \param mode Constant, must be \c IGRAPH_IN or \c IGRAPH_OUT. If it
422
 *        is \c IGRAPH_IN, then all directions are considered as
423
 *        opposite to the original one in the input graph.
424
 * \return Error code.
425
 *
426
 * Time complexity: very close to O(|E|+|V|), linear in the number of
427
 * edges and vertices. More precisely, it is O(|V|+|E|alpha(|E|,|V|)),
428
 * where alpha(|E|,|V|) is a functional inverse of Ackermann's
429
 * function.
430
 *
431
 * \example examples/simple/dominator_tree.c
432
 */
433
434
igraph_error_t igraph_dominator_tree(const igraph_t *graph,
435
                                     igraph_int_t root,
436
                                     igraph_vector_int_t *dom,
437
                                     igraph_t *domtree,
438
                                     igraph_vector_int_t *leftout,
439
1.77k
                                     igraph_neimode_t mode) {
440
441
1.77k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
442
443
1.77k
    igraph_adjlist_t succ, pred;
444
1.77k
    igraph_vector_int_t parent;
445
1.77k
    igraph_vector_int_t semi;    /* +1 always */
446
1.77k
    igraph_vector_int_t vertex;   /* +1 always */
447
1.77k
    igraph_i_dbucket_t bucket;
448
1.77k
    igraph_vector_int_t ancestor;
449
1.77k
    igraph_vector_int_t label;
450
451
1.77k
    igraph_neimode_t invmode = IGRAPH_REVERSE_MODE(mode);
452
453
1.77k
    igraph_vector_int_t vdom, *mydom = dom;
454
455
1.77k
    igraph_int_t component_size = 0;
456
457
1.77k
    if (root < 0 || root >= no_of_nodes) {
458
0
        IGRAPH_ERROR("Invalid root vertex ID for dominator tree.",
459
0
                     IGRAPH_EINVVID);
460
0
    }
461
462
1.77k
    if (!igraph_is_directed(graph)) {
463
0
        IGRAPH_ERROR("Dominator tree of an undirected graph requested.",
464
0
                     IGRAPH_EINVAL);
465
0
    }
466
467
1.77k
    if (mode == IGRAPH_ALL) {
468
0
        IGRAPH_ERROR("Invalid neighbor mode for dominator tree.",
469
0
                     IGRAPH_EINVAL);
470
0
    }
471
472
1.77k
    if (dom) {
473
1.77k
        IGRAPH_CHECK(igraph_vector_int_resize(dom, no_of_nodes));
474
1.77k
    } else {
475
0
        mydom = &vdom;
476
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(mydom, no_of_nodes);
477
0
    }
478
1.77k
    igraph_vector_int_fill(mydom, -2);
479
480
1.77k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&parent, no_of_nodes);
481
1.77k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&semi, no_of_nodes);
482
1.77k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex, no_of_nodes);
483
1.77k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&ancestor, no_of_nodes);
484
1.77k
    IGRAPH_CHECK(igraph_vector_int_init_range(&label, 0, no_of_nodes));
485
1.77k
    IGRAPH_FINALLY(igraph_vector_int_destroy, &label);
486
1.77k
    IGRAPH_CHECK(igraph_adjlist_init(graph, &succ, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
487
1.77k
    IGRAPH_FINALLY(igraph_adjlist_destroy, &succ);
488
1.77k
    IGRAPH_CHECK(igraph_adjlist_init(graph, &pred, invmode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
489
1.77k
    IGRAPH_FINALLY(igraph_adjlist_destroy, &pred);
490
1.77k
    IGRAPH_CHECK(igraph_i_dbucket_init(&bucket, no_of_nodes));
491
1.77k
    IGRAPH_FINALLY(igraph_i_dbucket_destroy, &bucket);
492
493
    /* DFS first, to set semi, vertex and parent, step 1 */
494
495
1.77k
    IGRAPH_CHECK(igraph_dfs(graph, root, mode, /*unreachable=*/ false,
496
1.77k
                            /*order=*/ &vertex,
497
1.77k
                            /*order_out=*/ NULL, /*parents=*/ &parent,
498
1.77k
                            /*dist=*/ NULL, /*in_callback=*/ NULL,
499
1.77k
                            /*out_callback=*/ NULL, /*extra=*/ NULL));
500
501
308k
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
502
306k
        if (VECTOR(vertex)[i] >= 0) {
503
23.8k
            igraph_int_t t = VECTOR(vertex)[i];
504
23.8k
            VECTOR(semi)[t] = component_size + 1;
505
23.8k
            VECTOR(vertex)[component_size] = t + 1;
506
23.8k
            component_size++;
507
23.8k
        }
508
306k
    }
509
1.77k
    if (leftout) {
510
1.77k
        igraph_int_t n = no_of_nodes - component_size;
511
1.77k
        igraph_int_t p = 0, j;
512
1.77k
        IGRAPH_CHECK(igraph_vector_int_resize(leftout, n));
513
307k
        for (j = 0; j < no_of_nodes && p < n; j++) {
514
305k
            if (VECTOR(parent)[j] < -1) {
515
282k
                VECTOR(*leftout)[p++] = j;
516
282k
            }
517
305k
        }
518
1.77k
    }
519
520
    /* We need to go over 'pred' because it should contain only the
521
       edges towards the target vertex. */
522
308k
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
523
306k
        igraph_vector_int_t *v = igraph_adjlist_get(&pred, i);
524
306k
        igraph_int_t n = igraph_vector_int_size(v);
525
374k
        for (igraph_int_t j = 0; j < n; ) {
526
67.6k
            igraph_int_t v2 = VECTOR(*v)[j];
527
67.6k
            if (VECTOR(parent)[v2] >= -1) {
528
42.5k
                j++;
529
42.5k
            } else {
530
25.0k
                VECTOR(*v)[j] = VECTOR(*v)[n - 1];
531
25.0k
                igraph_vector_int_pop_back(v);
532
25.0k
                n--;
533
25.0k
            }
534
67.6k
        }
535
306k
    }
536
537
    /* Now comes the main algorithm, steps 2 & 3 */
538
539
23.8k
    for (igraph_int_t i = component_size - 1; i > 0; i--) {
540
22.1k
        igraph_int_t w = VECTOR(vertex)[i] - 1;
541
22.1k
        igraph_vector_int_t *predw = igraph_adjlist_get(&pred, w);
542
22.1k
        igraph_int_t j, n = igraph_vector_int_size(predw);
543
55.1k
        for (j = 0; j < n; j++) {
544
33.0k
            igraph_int_t v = VECTOR(*predw)[j];
545
33.0k
            igraph_int_t u = igraph_i_dominator_EVAL(v, &ancestor, &label, &semi);
546
33.0k
            if (VECTOR(semi)[u] < VECTOR(semi)[w]) {
547
22.8k
                VECTOR(semi)[w] = VECTOR(semi)[u];
548
22.8k
            }
549
33.0k
        }
550
22.1k
        igraph_i_dbucket_insert(&bucket, VECTOR(vertex)[ VECTOR(semi)[w] - 1 ] - 1, w);
551
22.1k
        igraph_i_dominator_LINK(VECTOR(parent)[w], w, &ancestor);
552
44.2k
        while (!igraph_i_dbucket_empty(&bucket, VECTOR(parent)[w])) {
553
22.1k
            igraph_int_t v = igraph_i_dbucket_delete(&bucket, VECTOR(parent)[w]);
554
22.1k
            igraph_int_t u = igraph_i_dominator_EVAL(v, &ancestor, &label, &semi);
555
22.1k
            VECTOR(*mydom)[v] = VECTOR(semi)[u] < VECTOR(semi)[v] ? u :
556
22.1k
                                VECTOR(parent)[w];
557
22.1k
        }
558
22.1k
    }
559
560
    /* Finally, step 4 */
561
562
23.8k
    for (igraph_int_t i = 1; i < component_size; i++) {
563
22.1k
        igraph_int_t w = VECTOR(vertex)[i] - 1;
564
22.1k
        if (VECTOR(*mydom)[w] != VECTOR(vertex)[VECTOR(semi)[w] - 1] - 1) {
565
531
            VECTOR(*mydom)[w] = VECTOR(*mydom)[VECTOR(*mydom)[w]];
566
531
        }
567
22.1k
    }
568
1.77k
    VECTOR(*mydom)[root] = -1;
569
570
1.77k
    igraph_i_dbucket_destroy(&bucket);
571
1.77k
    igraph_adjlist_destroy(&pred);
572
1.77k
    igraph_adjlist_destroy(&succ);
573
1.77k
    igraph_vector_int_destroy(&label);
574
1.77k
    igraph_vector_int_destroy(&ancestor);
575
1.77k
    igraph_vector_int_destroy(&vertex);
576
1.77k
    igraph_vector_int_destroy(&semi);
577
1.77k
    igraph_vector_int_destroy(&parent);
578
1.77k
    IGRAPH_FINALLY_CLEAN(8);
579
580
1.77k
    if (domtree) {
581
0
        igraph_vector_int_t edges;
582
0
        igraph_int_t ptr = 0;
583
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, component_size * 2 - 2);
584
0
        for (igraph_int_t i = 0; i < no_of_nodes; i++) {
585
0
            if (i != root && VECTOR(*mydom)[i] >= 0) {
586
0
                if (mode == IGRAPH_OUT) {
587
0
                    VECTOR(edges)[ptr++] = VECTOR(*mydom)[i];
588
0
                    VECTOR(edges)[ptr++] = i;
589
0
                } else {
590
0
                    VECTOR(edges)[ptr++] = i;
591
0
                    VECTOR(edges)[ptr++] = VECTOR(*mydom)[i];
592
0
                }
593
0
            }
594
0
        }
595
0
        IGRAPH_CHECK(igraph_create(domtree, &edges, no_of_nodes,
596
0
                                   IGRAPH_DIRECTED));
597
0
        igraph_vector_int_destroy(&edges);
598
0
        IGRAPH_FINALLY_CLEAN(1);
599
600
0
        IGRAPH_CHECK(igraph_i_attribute_copy(domtree, graph, true, true, /*edges=*/ false));
601
0
    }
602
603
1.77k
    if (!dom) {
604
0
        igraph_vector_int_destroy(&vdom);
605
0
        IGRAPH_FINALLY_CLEAN(1);
606
0
    }
607
608
1.77k
    return IGRAPH_SUCCESS;
609
1.77k
}
610
611
typedef struct igraph_i_all_st_cuts_minimal_dfs_data_t {
612
    igraph_stack_int_t *stack;
613
    igraph_bitset_t *nomark;
614
    const igraph_bitset_t *GammaX;
615
    igraph_int_t root;
616
    const igraph_vector_int_t *map;
617
} igraph_i_all_st_cuts_minimal_dfs_data_t;
618
619
static igraph_error_t igraph_i_all_st_cuts_minimal_dfs_incb(
620
    const igraph_t *graph,
621
    igraph_int_t vid,
622
    igraph_int_t dist,
623
0
    void *extra) {
624
625
0
    igraph_i_all_st_cuts_minimal_dfs_data_t *data = extra;
626
0
    igraph_stack_int_t *stack = data->stack;
627
0
    igraph_bitset_t *nomark = data->nomark;
628
0
    const igraph_bitset_t *GammaX = data->GammaX;
629
0
    const igraph_vector_int_t *map = data->map;
630
0
    igraph_int_t realvid = VECTOR(*map)[vid];
631
632
0
    IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist);
633
634
0
    if (IGRAPH_BIT_TEST(*GammaX, realvid)) {
635
0
        if (!igraph_stack_int_empty(stack)) {
636
0
            igraph_int_t top = igraph_stack_int_top(stack);
637
0
            IGRAPH_BIT_SET(*nomark, top); /* we just found a smaller one */
638
0
        }
639
0
        IGRAPH_CHECK(igraph_stack_int_push(stack, realvid));
640
0
    }
641
642
0
    return IGRAPH_SUCCESS;
643
0
}
644
645
static igraph_error_t igraph_i_all_st_cuts_minimal_dfs_outcb(
646
    const igraph_t *graph,
647
    igraph_int_t vid,
648
    igraph_int_t dist,
649
0
    void *extra) {
650
0
    igraph_i_all_st_cuts_minimal_dfs_data_t *data = extra;
651
0
    igraph_stack_int_t *stack = data->stack;
652
0
    const igraph_vector_int_t *map = data->map;
653
0
    igraph_int_t realvid = VECTOR(*map)[vid];
654
655
0
    IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist);
656
657
0
    if (!igraph_stack_int_empty(stack) &&
658
0
        igraph_stack_int_top(stack) == realvid) {
659
0
        igraph_stack_int_pop(stack);
660
0
    }
661
662
0
    return IGRAPH_SUCCESS;
663
0
}
664
665
static igraph_error_t igraph_i_all_st_cuts_minimal(const igraph_t *graph,
666
        const igraph_t *domtree,
667
        igraph_int_t root,
668
        const igraph_marked_queue_int_t *X,
669
        const igraph_bitset_t *GammaX,
670
        const igraph_vector_int_t *invmap,
671
0
        igraph_vector_int_t *minimal) {
672
673
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
674
0
    igraph_stack_int_t stack;
675
0
    igraph_bitset_t nomark;
676
0
    igraph_i_all_st_cuts_minimal_dfs_data_t data;
677
0
    igraph_int_t i;
678
679
0
    IGRAPH_UNUSED(X);
680
681
0
    IGRAPH_STACK_INT_INIT_FINALLY(&stack, 10);
682
0
    IGRAPH_BITSET_INIT_FINALLY(&nomark, no_of_nodes);
683
684
0
    data.stack = &stack;
685
0
    data.nomark = &nomark;
686
0
    data.GammaX = GammaX;
687
0
    data.root = root;
688
0
    data.map = invmap;
689
690
    /* We mark all GammaX elements as minimal first.
691
       TODO: actually, we could just use GammaX to return the minimal
692
       elements. */
693
0
    igraph_bitset_not(&nomark, GammaX);
694
695
    /* We do a reverse DFS from root. If, along a path we find a GammaX
696
       vertex after (=below) another GammaX vertex, we mark the higher
697
       one as non-minimal. */
698
699
0
    IGRAPH_CHECK(igraph_dfs(domtree, root, IGRAPH_IN,
700
0
                            /*unreachable=*/ false, /*order=*/ NULL,
701
0
                            /*order_out=*/ NULL, /*parents=*/ NULL,
702
0
                            /*dist=*/ NULL,
703
0
                            /*in_callback=*/ igraph_i_all_st_cuts_minimal_dfs_incb,
704
0
                            /*out_callback=*/ igraph_i_all_st_cuts_minimal_dfs_outcb,
705
0
                            /*extra=*/ &data));
706
707
0
    igraph_vector_int_clear(minimal);
708
0
    for (i = 0; i < no_of_nodes; i++) {
709
0
        if (!IGRAPH_BIT_TEST(nomark, i)) {
710
0
            IGRAPH_CHECK(igraph_vector_int_push_back(minimal, i));
711
0
        }
712
0
    }
713
714
0
    igraph_bitset_destroy(&nomark);
715
0
    igraph_stack_int_destroy(&stack);
716
0
    IGRAPH_FINALLY_CLEAN(2);
717
718
0
    return IGRAPH_SUCCESS;
719
0
}
720
721
/* not 'static' because used in igraph_all_st_cuts.c test program */
722
igraph_error_t igraph_i_all_st_cuts_pivot(
723
    const igraph_t *graph, const igraph_marked_queue_int_t *S,
724
    const igraph_estack_t *T, igraph_int_t source, igraph_int_t target,
725
    igraph_int_t *v, igraph_vector_int_t *Isv, void *arg
726
0
) {
727
728
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
729
0
    igraph_t Sbar;
730
0
    igraph_vector_int_t Sbar_map, Sbar_invmap;
731
0
    igraph_vector_int_t keep;
732
0
    igraph_t domtree;
733
0
    igraph_vector_int_t leftout;
734
0
    igraph_int_t i, nomin, n;
735
0
    igraph_int_t root;
736
0
    igraph_vector_int_t M;
737
0
    igraph_bitset_t GammaS;
738
0
    igraph_vector_int_t Nuv;
739
0
    igraph_vector_int_t Isv_min;
740
0
    igraph_vector_int_t GammaS_vec;
741
0
    igraph_int_t Sbar_size;
742
743
0
    IGRAPH_UNUSED(arg);
744
745
    /* We need to create the graph induced by Sbar */
746
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_map, 0);
747
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_invmap, 0);
748
749
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&keep, 0);
750
0
    for (i = 0; i < no_of_nodes; i++) {
751
0
        if (!igraph_marked_queue_int_iselement(S, i)) {
752
0
            IGRAPH_CHECK(igraph_vector_int_push_back(&keep, i));
753
0
        }
754
0
    }
755
0
    Sbar_size = igraph_vector_int_size(&keep);
756
757
0
    IGRAPH_CHECK(igraph_induced_subgraph_map(graph, &Sbar,
758
0
                 igraph_vss_vector(&keep),
759
0
                 IGRAPH_SUBGRAPH_AUTO,
760
0
                 /* map= */ &Sbar_map,
761
0
                 /* invmap= */ &Sbar_invmap));
762
0
    igraph_vector_int_destroy(&keep);
763
0
    IGRAPH_FINALLY_CLEAN(1);
764
0
    IGRAPH_FINALLY(igraph_destroy, &Sbar);
765
766
0
    root = VECTOR(Sbar_map)[target];
767
768
    /* -------------------------------------------------------------*/
769
    /* Construct the dominator tree of Sbar */
770
771
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&leftout, 0);
772
0
    IGRAPH_CHECK(igraph_dominator_tree(&Sbar, root,
773
0
                                       /*dom=*/ 0, &domtree,
774
0
                                       &leftout, IGRAPH_IN));
775
0
    IGRAPH_FINALLY(igraph_destroy, &domtree);
776
777
    /* -------------------------------------------------------------*/
778
    /* Identify the set M of minimal elements of Gamma(S) with respect
779
       to the dominator relation. */
780
781
    /* First we create GammaS */
782
    /* TODO: use the adjacency list, instead of neighbors() */
783
0
    IGRAPH_BITSET_INIT_FINALLY(&GammaS, no_of_nodes);
784
0
    if (igraph_marked_queue_int_size(S) == 0) {
785
0
        IGRAPH_BIT_SET(GammaS, VECTOR(Sbar_map)[source]);
786
0
    } else {
787
0
        for (i = 0; i < no_of_nodes; i++) {
788
0
            if (igraph_marked_queue_int_iselement(S, i)) {
789
0
                igraph_vector_int_t neis;
790
0
                igraph_int_t j;
791
0
                IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
792
0
                IGRAPH_CHECK(igraph_neighbors(
793
0
                    graph, &neis, i, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
794
0
                ));
795
0
                n = igraph_vector_int_size(&neis);
796
0
                for (j = 0; j < n; j++) {
797
0
                    igraph_int_t nei = VECTOR(neis)[j];
798
0
                    if (!igraph_marked_queue_int_iselement(S, nei)) {
799
0
                        IGRAPH_BIT_SET(GammaS, nei);
800
0
                    }
801
0
                }
802
0
                igraph_vector_int_destroy(&neis);
803
0
                IGRAPH_FINALLY_CLEAN(1);
804
0
            }
805
0
        }
806
0
    }
807
808
    /* Relabel left out vertices (set K in Provan & Shier) to
809
       correspond to node labelling of graph instead of SBar.
810
       At the same time ensure that GammaS is a proper subset of
811
       L, where L are the nodes in the dominator tree. */
812
0
    n = igraph_vector_int_size(&leftout);
813
0
    for (i = 0; i < n; i++) {
814
0
        VECTOR(leftout)[i] = VECTOR(Sbar_invmap)[VECTOR(leftout)[i]];
815
0
        IGRAPH_BIT_CLEAR(GammaS, VECTOR(leftout)[i]);
816
0
    }
817
818
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&M, 0);
819
0
    if (igraph_ecount(&domtree) > 0) {
820
0
        IGRAPH_CHECK(igraph_i_all_st_cuts_minimal(graph, &domtree, root, S,
821
0
                     &GammaS, &Sbar_invmap, &M));
822
0
    }
823
824
0
    igraph_vector_int_clear(Isv);
825
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&Nuv, 0);
826
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv_min, 0);
827
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&GammaS_vec, 0);
828
0
    for (i = 0; i < no_of_nodes; i++) {
829
0
        if (IGRAPH_BIT_TEST(GammaS, i)) {
830
0
            IGRAPH_CHECK(igraph_vector_int_push_back(&GammaS_vec, i));
831
0
        }
832
0
    }
833
834
0
    nomin = igraph_vector_int_size(&M);
835
0
    for (i = 0; i < nomin; i++) {
836
        /* -------------------------------------------------------------*/
837
        /* For each v in M find the set Nu(v)=dom(Sbar, v)-K
838
           Nu(v) contains all vertices that are dominated by v, for every
839
           v, this is a subtree of the dominator tree, rooted at v. The
840
           different subtrees are disjoint. */
841
0
        igraph_int_t min = VECTOR(Sbar_map)[ VECTOR(M)[i] ];
842
0
        igraph_int_t nuvsize, isvlen, j;
843
0
        IGRAPH_CHECK(igraph_dfs(&domtree, min, IGRAPH_IN,
844
0
                                /*unreachable=*/ false, /*order=*/ &Nuv,
845
0
                                /*order_out=*/ NULL, /*parents=*/ NULL, /*dist=*/ NULL,
846
0
                                /*in_callback=*/ NULL, /*out_callback=*/ NULL,
847
0
                                /*extra=*/ NULL));
848
        /* Remove the negative values from the end of the vector */
849
0
        for (nuvsize = 0; nuvsize < Sbar_size; nuvsize++) {
850
0
            igraph_int_t t = VECTOR(Nuv)[nuvsize];
851
0
            if (t >= 0) {
852
0
                VECTOR(Nuv)[nuvsize] = VECTOR(Sbar_invmap)[t];
853
0
            } else {
854
0
                break;
855
0
            }
856
0
        }
857
0
        igraph_vector_int_resize(&Nuv, nuvsize); /* shrinks, error safe */
858
859
        /* -------------------------------------------------------------*/
860
        /* By a BFS search of <Nu(v)> determine I(S,v)-K.
861
           I(S,v) contains all vertices that are in Nu(v) and that are
862
           reachable from Gamma(S) via a path in Nu(v). */
863
0
        IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ -1, /*roots=*/ &GammaS_vec,
864
0
                                /*mode=*/ IGRAPH_OUT, /*unreachable=*/ false,
865
0
                                /*restricted=*/ &Nuv,
866
0
                                /*order=*/ &Isv_min, /*rank=*/ NULL,
867
0
                                /*parents=*/ NULL, /*pred=*/ NULL, /*succ=*/ NULL,
868
0
                                /*dist=*/ NULL, /*callback=*/ NULL, /*extra=*/ NULL));
869
0
        for (isvlen = 0; isvlen < no_of_nodes; isvlen++) {
870
0
            if (VECTOR(Isv_min)[isvlen] < 0) {
871
0
                break;
872
0
            }
873
0
        }
874
0
        igraph_vector_int_resize(&Isv_min, isvlen);
875
876
        /* -------------------------------------------------------------*/
877
        /* For each c in M check whether Isv-K is included in Tbar. If
878
           such a v is found, compute Isv={x|v[Nu(v) U K]x} and return v and
879
           Isv; otherwise return Isv={}. */
880
0
        for (j = 0; j < isvlen; j++) {
881
0
            igraph_int_t u = VECTOR(Isv_min)[j];
882
0
            if (igraph_estack_iselement(T, u) || u == target) {
883
0
                break;
884
0
            }
885
0
        }
886
        /* We might have found one */
887
0
        if (j == isvlen) {
888
0
            *v = VECTOR(M)[i];
889
            /* Calculate real Isv */
890
0
            IGRAPH_CHECK(igraph_vector_int_append(&Nuv, &leftout));
891
0
            IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ *v,
892
0
                                    /*roots=*/ NULL, /*mode=*/ IGRAPH_OUT,
893
0
                                    /*unreachable=*/ false, /*restricted=*/ &Nuv,
894
0
                                    /*order=*/ &Isv_min, /*rank=*/ NULL,
895
0
                                    /*parents=*/ NULL, /*pred=*/ NULL, /*succ=*/ NULL,
896
0
                                    /*dist=*/ NULL, /*callback=*/ NULL, /*extra=*/ NULL));
897
0
            for (isvlen = 0; isvlen < no_of_nodes; isvlen++) {
898
0
                if (VECTOR(Isv_min)[isvlen] < 0) {
899
0
                    break;
900
0
                }
901
0
            }
902
0
            igraph_vector_int_resize(&Isv_min, isvlen);
903
0
            igraph_vector_int_update(Isv, &Isv_min);
904
905
0
            break;
906
0
        }
907
0
    }
908
909
0
    igraph_vector_int_destroy(&GammaS_vec);
910
0
    igraph_vector_int_destroy(&Isv_min);
911
0
    igraph_vector_int_destroy(&Nuv);
912
0
    IGRAPH_FINALLY_CLEAN(3);
913
914
0
    igraph_vector_int_destroy(&M);
915
0
    igraph_bitset_destroy(&GammaS);
916
0
    igraph_destroy(&domtree);
917
0
    igraph_vector_int_destroy(&leftout);
918
0
    igraph_destroy(&Sbar);
919
0
    igraph_vector_int_destroy(&Sbar_map);
920
0
    igraph_vector_int_destroy(&Sbar_invmap);
921
0
    IGRAPH_FINALLY_CLEAN(7);
922
923
0
    return IGRAPH_SUCCESS;
924
0
}
925
926
/* TODO: This is a temporary recursive version */
927
928
static igraph_error_t igraph_i_provan_shier_list_recursive(
929
    const igraph_t *graph, igraph_marked_queue_int_t *S,
930
    igraph_estack_t *T, igraph_int_t source, igraph_int_t target,
931
    igraph_vector_int_list_t *result, igraph_provan_shier_pivot_t *pivot,
932
    igraph_vector_int_t *Isv, void *pivot_arg
933
0
) {
934
935
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
936
0
    igraph_int_t v = 0;
937
0
    igraph_int_t i, n;
938
939
0
    pivot(graph, S, T, source, target, &v, Isv, pivot_arg);
940
941
0
    if (igraph_vector_int_empty(Isv)) {
942
0
        if (igraph_marked_queue_int_size(S) != 0 && igraph_marked_queue_int_size(S) != no_of_nodes) {
943
0
            igraph_vector_int_t *vec;
944
0
            IGRAPH_CHECK(igraph_vector_int_list_push_back_new(result, &vec));
945
0
            IGRAPH_CHECK(igraph_marked_queue_int_as_vector(S, vec));
946
0
        }
947
0
    } else {
948
        /* Add Isv to S */
949
0
        IGRAPH_CHECK(igraph_marked_queue_int_start_batch(S));
950
0
        n = igraph_vector_int_size(Isv);
951
0
        for (i = 0; i < n; i++) {
952
0
            if (!igraph_marked_queue_int_iselement(S, VECTOR(*Isv)[i])) {
953
0
                IGRAPH_CHECK(igraph_marked_queue_int_push(S, VECTOR(*Isv)[i]));
954
0
            }
955
0
        }
956
0
        igraph_vector_int_clear(Isv);
957
958
        /* Go down right in the search tree */
959
0
        IGRAPH_CHECK(igraph_i_provan_shier_list_recursive(
960
0
                         graph, S, T, source, target, result, pivot, Isv, pivot_arg));
961
962
        /* Take out Isv from S */
963
0
        igraph_marked_queue_int_pop_back_batch(S);
964
965
        /* Put v into T */
966
0
        IGRAPH_CHECK(igraph_estack_push(T, v));
967
968
        /* Go down left in the search tree */
969
0
        IGRAPH_CHECK(igraph_i_provan_shier_list_recursive(
970
0
                         graph, S, T, source, target, result, pivot, Isv, pivot_arg));
971
972
        /* Take out v from T */
973
0
        igraph_estack_pop(T);
974
975
0
    }
976
977
0
    return IGRAPH_SUCCESS;
978
0
}
979
980
static igraph_error_t igraph_provan_shier_list(
981
    const igraph_t *graph, igraph_marked_queue_int_t *S,
982
    igraph_estack_t *T, igraph_int_t source, igraph_int_t target,
983
    igraph_vector_int_list_t *result, igraph_provan_shier_pivot_t *pivot,
984
    void *pivot_arg
985
0
) {
986
0
    igraph_vector_int_t Isv;
987
988
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv, 0);
989
990
0
    IGRAPH_CHECK(igraph_i_provan_shier_list_recursive(
991
0
                     graph, S, T, source, target, result, pivot, &Isv, pivot_arg
992
0
                 ));
993
994
    /* Reverse the result to stay compatible with versions before 0.10.3 */
995
0
    IGRAPH_CHECK(igraph_vector_int_list_reverse(result));
996
997
0
    igraph_vector_int_destroy(&Isv);
998
0
    IGRAPH_FINALLY_CLEAN(1);
999
1000
0
    return IGRAPH_SUCCESS;
1001
0
}
1002
1003
/**
1004
 * \function igraph_all_st_cuts
1005
 * List all edge-cuts between two vertices in a directed graph
1006
 *
1007
 * This function lists all edge-cuts between a source and a target
1008
 * vertex. Every cut is listed exactly once. The implemented algorithm
1009
 * is described in JS Provan and DR Shier: A Paradigm for listing
1010
 * (s,t)-cuts in graphs, Algorithmica 15, 351--372, 1996.
1011
 *
1012
 * \param graph The input graph, is must be directed.
1013
 * \param cuts An initialized list of integer vectors, the cuts are stored
1014
 *        here. Each vector will contain the IDs of the edges in
1015
 *        the cut. This argument is ignored if it is a null pointer.
1016
 * \param partition1s An initialized list of integer vectors, the list of
1017
 *        vertex sets generating the actual edge cuts are stored
1018
 *        here. Each vector contains a set of vertex IDs. If X is such
1019
 *        a set, then all edges going from X to the complement of X
1020
 *        form an (s, t) edge-cut in the graph. This argument is
1021
 *        ignored if it is a null pointer.
1022
 * \param source The id of the source vertex.
1023
 * \param target The id of the target vertex.
1024
 * \return Error code.
1025
 *
1026
 * Time complexity: O(n(|V|+|E|)), where |V| is the number of
1027
 * vertices, |E| is the number of edges, and n is the number of cuts.
1028
 */
1029
1030
igraph_error_t igraph_all_st_cuts(const igraph_t *graph,
1031
                                  igraph_vector_int_list_t *cuts,
1032
                                  igraph_vector_int_list_t *partition1s,
1033
                                  igraph_int_t source,
1034
0
                                  igraph_int_t target) {
1035
1036
    /* S is a special stack, in which elements are pushed in batches.
1037
       It is then possible to remove the whole batch in one step.
1038
1039
       T is a stack with an is-element operation.
1040
       Every element is included at most once.
1041
    */
1042
1043
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1044
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
1045
0
    igraph_marked_queue_int_t S;
1046
0
    igraph_estack_t T;
1047
0
    igraph_vector_int_list_t *mypartition1s = partition1s, vpartition1s;
1048
0
    igraph_vector_int_t cut;
1049
0
    igraph_int_t i, nocuts;
1050
1051
0
    if (!igraph_is_directed(graph)) {
1052
0
        IGRAPH_ERROR("Listing all s-t cuts only implemented for "
1053
0
                     "directed graphs", IGRAPH_UNIMPLEMENTED);
1054
0
    }
1055
1056
0
    if (!partition1s) {
1057
0
        mypartition1s = &vpartition1s;
1058
0
        IGRAPH_CHECK(igraph_vector_int_list_init(mypartition1s, 0));
1059
0
        IGRAPH_FINALLY(igraph_vector_int_list_destroy, mypartition1s);
1060
0
    } else {
1061
0
        igraph_vector_int_list_clear(mypartition1s);
1062
0
    }
1063
1064
0
    IGRAPH_CHECK(igraph_marked_queue_int_init(&S, no_of_nodes));
1065
0
    IGRAPH_FINALLY(igraph_marked_queue_int_destroy, &S);
1066
0
    IGRAPH_CHECK(igraph_estack_init(&T, no_of_nodes, 0));
1067
0
    IGRAPH_FINALLY(igraph_estack_destroy, &T);
1068
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&cut, 0);
1069
1070
    /* We call it with S={}, T={} */
1071
0
    IGRAPH_CHECK(igraph_provan_shier_list(graph, &S, &T,
1072
0
                                          source, target, mypartition1s,
1073
0
                                          igraph_i_all_st_cuts_pivot,
1074
0
                                          /*pivot_arg=*/ 0));
1075
1076
0
    nocuts = igraph_vector_int_list_size(mypartition1s);
1077
1078
0
    if (cuts) {
1079
0
        igraph_vector_int_t inS;
1080
0
        IGRAPH_CHECK(igraph_vector_int_init(&inS, no_of_nodes));
1081
0
        IGRAPH_FINALLY(igraph_vector_int_destroy, &inS);
1082
0
        igraph_vector_int_list_clear(cuts);
1083
0
        IGRAPH_CHECK(igraph_vector_int_list_reserve(cuts, nocuts));
1084
0
        for (i = 0; i < nocuts; i++) {
1085
0
            igraph_vector_int_t *part = igraph_vector_int_list_get_ptr(mypartition1s, i);
1086
0
            igraph_int_t cutsize = 0;
1087
0
            igraph_int_t j, partlen = igraph_vector_int_size(part);
1088
            /* Mark elements */
1089
0
            for (j = 0; j < partlen; j++) {
1090
0
                igraph_int_t v = VECTOR(*part)[j];
1091
0
                VECTOR(inS)[v] = i + 1;
1092
0
            }
1093
            /* Check how many edges */
1094
0
            for (j = 0; j < no_of_edges; j++) {
1095
0
                igraph_int_t from = IGRAPH_FROM(graph, j);
1096
0
                igraph_int_t to = IGRAPH_TO(graph, j);
1097
0
                igraph_int_t pfrom = VECTOR(inS)[from];
1098
0
                igraph_int_t pto = VECTOR(inS)[to];
1099
0
                if (pfrom == i + 1 && pto != i + 1) {
1100
0
                    cutsize++;
1101
0
                }
1102
0
            }
1103
            /* Add the edges */
1104
0
            IGRAPH_CHECK(igraph_vector_int_resize(&cut, cutsize));
1105
0
            cutsize = 0;
1106
0
            for (j = 0; j < no_of_edges; j++) {
1107
0
                igraph_int_t from = IGRAPH_FROM(graph, j);
1108
0
                igraph_int_t to = IGRAPH_TO(graph, j);
1109
0
                igraph_int_t pfrom = VECTOR(inS)[from];
1110
0
                igraph_int_t pto = VECTOR(inS)[to];
1111
0
                if ((pfrom == i + 1 && pto != i + 1)) {
1112
0
                    VECTOR(cut)[cutsize++] = j;
1113
0
                }
1114
0
            }
1115
            /* Add the vector to 'cuts' */
1116
0
            IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(cuts, &cut));
1117
0
        }
1118
1119
0
        igraph_vector_int_destroy(&inS);
1120
0
        IGRAPH_FINALLY_CLEAN(1);
1121
0
    }
1122
1123
0
    igraph_vector_int_destroy(&cut);
1124
0
    igraph_estack_destroy(&T);
1125
0
    igraph_marked_queue_int_destroy(&S);
1126
0
    IGRAPH_FINALLY_CLEAN(3);
1127
1128
0
    if (!partition1s) {
1129
0
        igraph_vector_int_list_destroy(mypartition1s);
1130
0
        IGRAPH_FINALLY_CLEAN(1);
1131
0
    }
1132
1133
0
    return IGRAPH_SUCCESS;
1134
0
}
1135
1136
/* We need to find the minimal active elements of Sbar. I.e. all
1137
   active Sbar elements 'v', s.t. there is no other 'w' active Sbar
1138
   element from which 'v' is reachable. (Not necessarily through
1139
   active vertices.)
1140
1141
   We do so by traversing the nodes in topological sort order. The nodes that
1142
   are processed first and are not yet connected to any minimal nodes are then
1143
   marked as minimal (if active). Any subsequent nodes that are connected to
1144
   minimal nodes are then not marked as minimal.
1145
*/
1146
1147
static igraph_error_t igraph_i_all_st_mincuts_minimal(const igraph_t *residual,
1148
        const igraph_marked_queue_int_t *S,
1149
        const igraph_bitset_t *active,
1150
0
        igraph_vector_int_t *minimal) {
1151
1152
0
    igraph_int_t no_of_nodes = igraph_vcount(residual);
1153
0
    igraph_int_t i;
1154
0
    igraph_vector_int_t neis;
1155
0
    igraph_bitset_t connected_to_minimal;
1156
1157
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
1158
0
    IGRAPH_BITSET_INIT_FINALLY(&connected_to_minimal, no_of_nodes);
1159
1160
    // Clear minimal nodes, we will push back to vector as required.
1161
0
    igraph_vector_int_clear(minimal);
1162
1163
    /* We will loop over the nodes in topological sort order, where residual is
1164
     * assumed to already be in topological sort order. This way, any node that
1165
     * we encounter that is not yet connected to a minimal node and that is
1166
     * active, should be marked as minimal. Any node that is connected to a
1167
     * minimal node should not be considered minimal, irrespective of it being
1168
     * active or not.
1169
     */
1170
0
    for (i = 0; i < no_of_nodes; i++) {
1171
0
        igraph_int_t j, n;
1172
0
        IGRAPH_CHECK(igraph_neighbors(
1173
0
            residual, &neis, i, IGRAPH_IN, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
1174
0
        ));
1175
0
        n = igraph_vector_int_size(&neis);
1176
1177
        // Only consider nodes that are not in S.
1178
0
        if (!igraph_marked_queue_int_iselement(S, i)) {
1179
0
            for (j = 0; j < n; j++) {
1180
0
                igraph_int_t nei = VECTOR(neis)[j];
1181
                /* If connected to node that is connected to node that is minimal,
1182
                 * this node is also connected to node that is minimal.
1183
                 */
1184
0
                if (IGRAPH_BIT_TEST(connected_to_minimal, nei)) {
1185
0
                    IGRAPH_BIT_SET(connected_to_minimal, i);
1186
0
                    break;
1187
0
                }
1188
0
            }
1189
1190
            /* If this node is not connected to node that is minimal, and this node
1191
             * is minimal and active itself, set it as a minimal node.
1192
             */
1193
0
            if (!IGRAPH_BIT_TEST(connected_to_minimal, i) && IGRAPH_BIT_TEST(*active, i)) {
1194
1195
0
                IGRAPH_CHECK(igraph_vector_int_push_back(minimal, i));
1196
                /* Also mark this node as connected to minimal, to make sure that
1197
                 * any descendants will be marked as being connected to a minimal
1198
                 * node.
1199
                 */
1200
0
                IGRAPH_BIT_SET(connected_to_minimal, i);
1201
0
            }
1202
0
        }
1203
0
    }
1204
1205
0
    igraph_bitset_destroy(&connected_to_minimal);
1206
0
    igraph_vector_int_destroy(&neis);
1207
0
    IGRAPH_FINALLY_CLEAN(2);
1208
1209
0
    return IGRAPH_SUCCESS;
1210
0
}
1211
1212
typedef struct igraph_i_all_st_mincuts_data_t {
1213
    const igraph_bitset_t *active;
1214
} igraph_i_all_st_mincuts_data_t;
1215
1216
static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph,
1217
        const igraph_marked_queue_int_t *S,
1218
        const igraph_estack_t *T,
1219
        igraph_int_t source,
1220
        igraph_int_t target,
1221
        igraph_int_t *v,
1222
        igraph_vector_int_t *Isv,
1223
0
        void *arg) {
1224
1225
0
    igraph_i_all_st_mincuts_data_t *data = arg;
1226
0
    const igraph_bitset_t *active = data->active;
1227
1228
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1229
0
    igraph_int_t i, j;
1230
0
    igraph_vector_int_t keep;
1231
0
    igraph_vector_int_t M;
1232
0
    igraph_int_t nomin;
1233
1234
0
    IGRAPH_UNUSED(source); IGRAPH_UNUSED(target);
1235
1236
0
    if (igraph_marked_queue_int_size(S) == no_of_nodes) {
1237
0
        igraph_vector_int_clear(Isv);
1238
0
        return IGRAPH_SUCCESS;
1239
0
    }
1240
1241
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&keep, 0);
1242
0
    for (i = 0; i < no_of_nodes; i++) {
1243
0
        if (!igraph_marked_queue_int_iselement(S, i)) {
1244
0
            IGRAPH_CHECK(igraph_vector_int_push_back(&keep, i));
1245
0
        }
1246
0
    }
1247
1248
    /* ------------------------------------------------------------- */
1249
    /* Identify the set M of minimal elements that are active */
1250
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&M, 0);
1251
0
    IGRAPH_CHECK(igraph_i_all_st_mincuts_minimal(graph, S, active, &M));
1252
1253
    /* ------------------------------------------------------------- */
1254
    /* Now find a minimal element that is not in T */
1255
0
    igraph_vector_int_clear(Isv);
1256
0
    nomin = igraph_vector_int_size(&M);
1257
0
    for (i = 0; i < nomin; i++) {
1258
0
        igraph_int_t min = VECTOR(M)[i];
1259
0
        if (min != target)
1260
0
            if (!igraph_estack_iselement(T, min)) {
1261
0
                break;
1262
0
            }
1263
0
    }
1264
0
    if (i != nomin) {
1265
        /* OK, we found a pivot element. I(S,v) contains all elements
1266
           that can reach the pivot element */
1267
0
        igraph_vector_int_t Isv_min;
1268
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv_min, 0);
1269
0
        *v = VECTOR(M)[i];
1270
        /* TODO: restricted == keep ? */
1271
0
        IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ *v, /*roots=*/ NULL,
1272
0
                                /*mode=*/ IGRAPH_IN, /*unreachable=*/ false,
1273
0
                                /*restricted=*/ &keep, /*order=*/ &Isv_min,
1274
0
                                /*rank=*/ NULL, /*parents=*/ NULL, /*pred=*/ NULL,
1275
0
                                /*succ=*/ NULL, /*dist=*/ NULL, /*callback=*/ NULL,
1276
0
                                /*extra=*/ NULL));
1277
0
        for (j = 0; j < no_of_nodes; j++) {
1278
0
            igraph_int_t u = VECTOR(Isv_min)[j];
1279
0
            if (u < 0) {
1280
0
                break;
1281
0
            }
1282
0
            if (!igraph_marked_queue_int_iselement(S, u)) {
1283
0
                IGRAPH_CHECK(igraph_vector_int_push_back(Isv, u));
1284
0
            }
1285
0
        }
1286
0
        igraph_vector_int_destroy(&Isv_min);
1287
0
        IGRAPH_FINALLY_CLEAN(1);
1288
0
    }
1289
1290
0
    igraph_vector_int_destroy(&M);
1291
0
    igraph_vector_int_destroy(&keep);
1292
0
    IGRAPH_FINALLY_CLEAN(2);
1293
1294
0
    return IGRAPH_SUCCESS;
1295
0
}
1296
1297
/**
1298
 * \function igraph_all_st_mincuts
1299
 * \brief All minimum s-t cuts of a directed graph.
1300
 *
1301
 * This function lists all edge cuts between two vertices, in a directed graph,
1302
 * with minimum total capacity. Possibly, multiple cuts may have the same total
1303
 * capacity, although there is often only one minimum cut in weighted graphs.
1304
 * It is recommended to supply integer-values capacities. Otherwise, not all
1305
 * minimum cuts may be detected because of numerical roundoff errors.
1306
 * The implemented algorithm is described in JS Provan and DR
1307
 * Shier: A Paradigm for listing (s,t)-cuts in graphs, Algorithmica 15,
1308
 * 351--372, 1996.
1309
 *
1310
 * \param graph The input graph, it must be directed.
1311
 * \param value Pointer to a real number or \c NULL. The value of the minimum cut
1312
 *        is stored here, unless it is a null pointer.
1313
 * \param cuts Pointer to initialized list of integer vectors or \c NULL.
1314
 *        The cuts are stored here as lists of vertex IDs.
1315
 * \param partition1s Pointer to an initialized list of integer vectors or \c NULL.
1316
 *        The list of vertex sets, generating the actual edge cuts, are stored
1317
 *        here. Each vector contains a set of vertex IDs. If X is such
1318
 *        a set, then all edges going from X to the complement of X
1319
 *        form an (s,t) edge-cut in the graph.
1320
 * \param source The id of the source vertex.
1321
 * \param target The id of the target vertex.
1322
 * \param capacity Vector of edge capacities. All capacities must be
1323
 *        strictly positive. If this is a null pointer, then all edges
1324
 *        are assumed to have capacity one.
1325
 * \return Error code.
1326
 *
1327
 * Time complexity: O(n(|V|+|E|))+O(F), where |V| is the number of
1328
 * vertices, |E| is the number of edges, and n is the number of cuts;
1329
 * O(F) is the time complexity of the maximum flow algorithm, see \ref
1330
 * igraph_maxflow().
1331
 *
1332
 * \example examples/simple/igraph_all_st_mincuts.c
1333
 */
1334
1335
igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value,
1336
                                     igraph_vector_int_list_t *cuts,
1337
                                     igraph_vector_int_list_t *partition1s,
1338
                                     igraph_int_t source,
1339
                                     igraph_int_t target,
1340
0
                                     const igraph_vector_t *capacity) {
1341
1342
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1343
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
1344
0
    igraph_vector_t flow;
1345
0
    igraph_t residual;
1346
0
    igraph_vector_int_t NtoL;
1347
0
    igraph_vector_int_t cut;
1348
0
    igraph_int_t newsource, newtarget;
1349
0
    igraph_marked_queue_int_t S;
1350
0
    igraph_estack_t T;
1351
0
    igraph_i_all_st_mincuts_data_t pivot_data;
1352
0
    igraph_bitset_t VE1bool;
1353
0
    igraph_int_t i, nocuts;
1354
0
    igraph_int_t proj_nodes;
1355
0
    igraph_vector_int_t revmap_ptr, revmap_next;
1356
0
    igraph_vector_int_list_t closedsets;
1357
0
    igraph_vector_int_list_t *mypartition1s = partition1s, vpartition1s;
1358
0
    igraph_maxflow_stats_t stats;
1359
1360
    /* -------------------------------------------------------------------- */
1361
    /* Error checks */
1362
0
    if (!igraph_is_directed(graph)) {
1363
0
        IGRAPH_ERROR("s-t cuts can only be listed in directed graphs.", IGRAPH_UNIMPLEMENTED);
1364
0
    }
1365
0
    if (source < 0 || source >= no_of_nodes) {
1366
0
        IGRAPH_ERROR("Invalid source vertex.", IGRAPH_EINVVID);
1367
0
    }
1368
0
    if (target < 0 || target >= no_of_nodes) {
1369
0
        IGRAPH_ERROR("Invalid target vertex.", IGRAPH_EINVVID);
1370
0
    }
1371
0
    if (source == target) {
1372
0
        IGRAPH_ERROR("Source and target vertices are the same.", IGRAPH_EINVAL);
1373
0
    }
1374
0
    if (capacity && igraph_vector_size(capacity) != no_of_edges) {
1375
0
        IGRAPH_ERROR("Capacity vector length must agree with number of edges.", IGRAPH_EINVAL);
1376
0
    }
1377
0
    if (capacity && no_of_edges > 0 && igraph_vector_min(capacity) <= 0) {
1378
0
        IGRAPH_ERROR("Not all capacities are strictly positive.", IGRAPH_EINVAL);
1379
0
    }
1380
1381
0
    if (!partition1s) {
1382
0
        mypartition1s = &vpartition1s;
1383
0
        IGRAPH_CHECK(igraph_vector_int_list_init(mypartition1s, 0));
1384
0
        IGRAPH_FINALLY(igraph_vector_int_list_destroy, mypartition1s);
1385
0
    }
1386
1387
    /* -------------------------------------------------------------------- */
1388
    /* We need to calculate the maximum flow first */
1389
0
    IGRAPH_VECTOR_INIT_FINALLY(&flow, 0);
1390
0
    IGRAPH_CHECK(igraph_maxflow(graph, value, &flow, /*cut=*/ NULL,
1391
0
                                /*partition1=*/ NULL, /*partition2=*/ NULL,
1392
0
                                /*source=*/ source, /*target=*/ target,
1393
0
                                capacity, &stats));
1394
1395
    /* -------------------------------------------------------------------- */
1396
    /* Then we need the reverse residual graph */
1397
0
    IGRAPH_CHECK(igraph_reverse_residual_graph(graph, capacity, &residual, &flow));
1398
0
    IGRAPH_FINALLY(igraph_destroy, &residual);
1399
1400
    /* -------------------------------------------------------------------- */
1401
    /* We shrink it to its strongly connected components */
1402
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&NtoL, 0);
1403
0
    IGRAPH_CHECK(igraph_connected_components(
1404
0
                     &residual, /*membership=*/ &NtoL, /*csize=*/ NULL,
1405
0
                     /*no=*/ &proj_nodes, IGRAPH_STRONG
1406
0
                 ));
1407
0
    IGRAPH_CHECK(igraph_contract_vertices(&residual, /*mapping=*/ &NtoL, /*vertex_comb=*/ NULL));
1408
0
    IGRAPH_CHECK(igraph_simplify(&residual, /*remove_multiple=*/ true, /*remove_loops=*/ true, /*edge_comb=*/ NULL));
1409
1410
    /* We relabel the residual graph so that it is in topological sort order. */
1411
0
    igraph_int_t no_of_nodes_residual = igraph_vcount(&residual);
1412
0
    igraph_vector_int_t order;
1413
0
    igraph_t tmpgraph;
1414
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes_residual);
1415
0
    IGRAPH_CHECK(igraph_topological_sorting(&residual, &order, IGRAPH_OUT));
1416
1417
    /* Invert order to get permutation to ensure vertices follow topological order. */
1418
0
    igraph_vector_int_t inv_order;
1419
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&inv_order, no_of_nodes_residual);
1420
0
    for (i = 0; i < no_of_nodes_residual; i++) {
1421
0
        VECTOR(inv_order)[VECTOR(order)[i]] = i;
1422
0
    }
1423
1424
    // Relabel mapping
1425
0
    for (i = 0; i < no_of_nodes; i++) {
1426
0
        VECTOR(NtoL)[i] = VECTOR(inv_order)[VECTOR(NtoL)[i]];
1427
0
    }
1428
1429
    /* Permute vertices and replace residual with tmpgraph that is topologically
1430
     * sorted.
1431
     */
1432
0
    IGRAPH_CHECK(igraph_permute_vertices(&residual, &tmpgraph, &order));
1433
1434
0
    igraph_destroy(&residual); // We first free memory from original residual graph
1435
0
    residual = tmpgraph;       // Then we replace it by allocated memory from tmpgraph
1436
1437
0
    igraph_vector_int_destroy(&inv_order);
1438
0
    igraph_vector_int_destroy(&order);
1439
0
    IGRAPH_FINALLY_CLEAN(2);
1440
1441
0
    newsource = VECTOR(NtoL)[source];
1442
0
    newtarget = VECTOR(NtoL)[target];
1443
1444
    /* TODO: handle the newsource == newtarget case */
1445
1446
    /* -------------------------------------------------------------------- */
1447
    /* Determine the active vertices in the projection */
1448
0
    IGRAPH_BITSET_INIT_FINALLY(&VE1bool, proj_nodes);
1449
0
    for (i = 0; i < no_of_edges; i++) {
1450
0
        if (VECTOR(flow)[i] > 0) {
1451
0
            igraph_int_t from = IGRAPH_FROM(graph, i);
1452
0
            igraph_int_t to = IGRAPH_TO(graph, i);
1453
0
            igraph_int_t pfrom = VECTOR(NtoL)[from];
1454
0
            igraph_int_t pto = VECTOR(NtoL)[to];
1455
0
            if (!IGRAPH_BIT_TEST(VE1bool, pfrom)) {
1456
0
                IGRAPH_BIT_SET(VE1bool, pfrom);
1457
0
            }
1458
0
            if (!IGRAPH_BIT_TEST(VE1bool, pto)) {
1459
0
                IGRAPH_BIT_SET(VE1bool, pto);
1460
0
            }
1461
0
        }
1462
0
    }
1463
1464
0
    if (cuts)        {
1465
0
        igraph_vector_int_list_clear(cuts);
1466
0
    }
1467
0
    if (partition1s) {
1468
0
        igraph_vector_int_list_clear(partition1s);
1469
0
    }
1470
1471
    /* -------------------------------------------------------------------- */
1472
    /* Everything is ready, list the cuts, using the right PIVOT
1473
       function  */
1474
0
    IGRAPH_CHECK(igraph_marked_queue_int_init(&S, no_of_nodes));
1475
0
    IGRAPH_FINALLY(igraph_marked_queue_int_destroy, &S);
1476
0
    IGRAPH_CHECK(igraph_estack_init(&T, no_of_nodes, 0));
1477
0
    IGRAPH_FINALLY(igraph_estack_destroy, &T);
1478
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&cut, 0);
1479
1480
0
    pivot_data.active = &VE1bool;
1481
1482
0
    IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&closedsets, 0);
1483
0
    IGRAPH_CHECK(igraph_provan_shier_list(&residual, &S, &T,
1484
0
                                          newsource, newtarget, &closedsets,
1485
0
                                          igraph_i_all_st_mincuts_pivot,
1486
0
                                          &pivot_data));
1487
1488
    /* Convert the closed sets in the contracted graphs to cutsets in the
1489
       original graph */
1490
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&revmap_ptr, igraph_vcount(&residual));
1491
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&revmap_next, no_of_nodes);
1492
0
    for (i = 0; i < no_of_nodes; i++) {
1493
0
        igraph_int_t id = VECTOR(NtoL)[i];
1494
0
        VECTOR(revmap_next)[i] = VECTOR(revmap_ptr)[id];
1495
0
        VECTOR(revmap_ptr)[id] = i + 1;
1496
0
    }
1497
1498
    /* Create partitions in original graph */
1499
0
    nocuts = igraph_vector_int_list_size(&closedsets);
1500
0
    igraph_vector_int_list_clear(mypartition1s);
1501
0
    IGRAPH_CHECK(igraph_vector_int_list_reserve(mypartition1s, nocuts));
1502
0
    for (i = 0; i < nocuts; i++) {
1503
0
        igraph_vector_int_t *supercut = igraph_vector_int_list_get_ptr(&closedsets, i);
1504
0
        igraph_int_t j, supercutsize = igraph_vector_int_size(supercut);
1505
1506
0
        igraph_vector_int_clear(&cut);
1507
0
        for (j = 0; j < supercutsize; j++) {
1508
0
            igraph_int_t vtx = VECTOR(*supercut)[j];
1509
0
            igraph_int_t ovtx = VECTOR(revmap_ptr)[vtx];
1510
0
            while (ovtx != 0) {
1511
0
                ovtx--;
1512
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&cut, ovtx));
1513
0
                ovtx = VECTOR(revmap_next)[ovtx];
1514
0
            }
1515
0
        }
1516
1517
0
        IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(mypartition1s, &cut));
1518
1519
        /* TODO: we could already reclaim the memory taken by 'supercut' here */
1520
0
    }
1521
1522
0
    igraph_vector_int_destroy(&revmap_next);
1523
0
    igraph_vector_int_destroy(&revmap_ptr);
1524
0
    igraph_vector_int_list_destroy(&closedsets);
1525
0
    IGRAPH_FINALLY_CLEAN(3);
1526
1527
    /* Create cuts in original graph */
1528
0
    if (cuts) {
1529
0
        igraph_vector_int_t memb;
1530
1531
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&memb, no_of_nodes);
1532
0
        IGRAPH_CHECK(igraph_vector_int_list_reserve(cuts, nocuts));
1533
1534
0
        for (i = 0; i < nocuts; i++) {
1535
0
            igraph_vector_int_t *part = igraph_vector_int_list_get_ptr(mypartition1s, i);
1536
0
            igraph_int_t j, n = igraph_vector_int_size(part);
1537
1538
0
            igraph_vector_int_clear(&cut);
1539
0
            for (j = 0; j < n; j++) {
1540
0
                igraph_int_t vtx = VECTOR(*part)[j];
1541
0
                VECTOR(memb)[vtx] = i + 1;
1542
0
            }
1543
0
            for (j = 0; j < no_of_edges; j++) {
1544
0
                if (VECTOR(flow)[j] > 0) {
1545
0
                    igraph_int_t from = IGRAPH_FROM(graph, j);
1546
0
                    igraph_int_t to = IGRAPH_TO(graph, j);
1547
0
                    if (VECTOR(memb)[from] == i + 1 && VECTOR(memb)[to] != i + 1) {
1548
0
                        IGRAPH_CHECK(igraph_vector_int_push_back(&cut, j));
1549
0
                    }
1550
0
                }
1551
0
            }
1552
1553
0
            IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(cuts, &cut));
1554
0
        }
1555
1556
0
        igraph_vector_int_destroy(&memb);
1557
0
        IGRAPH_FINALLY_CLEAN(1);
1558
0
    }
1559
1560
0
    igraph_vector_int_destroy(&cut);
1561
0
    igraph_estack_destroy(&T);
1562
0
    igraph_marked_queue_int_destroy(&S);
1563
0
    igraph_bitset_destroy(&VE1bool);
1564
0
    igraph_vector_int_destroy(&NtoL);
1565
0
    igraph_destroy(&residual);
1566
0
    igraph_vector_destroy(&flow);
1567
0
    IGRAPH_FINALLY_CLEAN(7);
1568
1569
0
    if (!partition1s) {
1570
0
        igraph_vector_int_list_destroy(mypartition1s);
1571
0
        IGRAPH_FINALLY_CLEAN(1);
1572
0
    }
1573
1574
0
    return IGRAPH_SUCCESS;
1575
0
}