Coverage Report

Created: 2026-05-30 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/connectivity/components.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2003-2012  Gabor Csardi <csardi.gabor@gmail.com>
4
   334 Harvard street, Cambridge, MA 02139 USA
5
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 2 of the License, or
9
   (at your option) any later version.
10
11
   This program is distributed in the hope that it will be useful,
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
   GNU General Public License for more details.
15
16
   You should have received a copy of the GNU General Public License
17
   along with this program; if not, write to the Free Software
18
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
   02110-1301 USA
20
21
*/
22
23
#include "igraph_components.h"
24
25
#include "igraph_adjlist.h"
26
#include "igraph_bitset.h"
27
#include "igraph_dqueue.h"
28
#include "igraph_interface.h"
29
#include "igraph_progress.h"
30
#include "igraph_stack.h"
31
#include "igraph_structural.h"
32
#include "igraph_vector.h"
33
34
#include "core/interruption.h"
35
#include "operators/subgraph.h"
36
37
static igraph_error_t igraph_i_connected_components_weak(
38
    const igraph_t *graph, igraph_vector_int_t *membership,
39
    igraph_vector_int_t *csize, igraph_int_t *no
40
);
41
static igraph_error_t igraph_i_connected_components_strong(
42
    const igraph_t *graph, igraph_vector_int_t *membership,
43
    igraph_vector_int_t *csize, igraph_int_t *no
44
);
45
46
/**
47
 * \ingroup structural
48
 * \function igraph_connected_components
49
 * \brief Calculates the (weakly or strongly) connected components in a graph.
50
 *
51
 * When computing strongly connected components, the components will be
52
 * indexed in topological order. In other words, vertex \c v is reachable
53
 * from vertex \c u precisely when <code>membership[u] &lt;= membership[v]</code>.
54
 *
55
 * \param graph The graph object to analyze.
56
 * \param membership For every vertex the ID of its component is given.
57
 *    The vector has to be preinitialized and will be resized as needed.
58
 *    Alternatively this argument can be \c NULL, in which case it is ignored.
59
 * \param csize For every component it gives its size, the order being defined
60
 *    by the component IDs. The vector must be preinitialized and will be
61
 *    resized as needed. Alternatively this argument can be \c NULL, in which
62
 *    case it is ignored.
63
 * \param no Pointer to an integer, if not \c NULL then the number of
64
 *    components will be stored here.
65
 * \param mode For directed graph this specifies whether to calculate
66
 *    weakly or strongly connected components. Possible values:
67
 *    \clist
68
 *    \cli IGRAPH_WEAK
69
 *       Compute weakly connected components, i.e. ignore edge directions.
70
 *    \cli IGRAPH_STRONG
71
 *       Compute strongly connnected components, i.e. consider edge directions.
72
 *    \endclist
73
 *    This parameter is ignored for undirected graphs.
74
 * \return Error code.
75
 *
76
 * Time complexity: O(|V|+|E|), where |V| and |E| are the number of vertices
77
 * and edges in the graph.
78
 *
79
 * \example examples/simple/igraph_contract_vertices.c
80
 */
81
82
igraph_error_t igraph_connected_components(
83
    const igraph_t *graph, igraph_vector_int_t *membership,
84
    igraph_vector_int_t *csize, igraph_int_t *no, igraph_connectedness_t mode
85
53.1k
) {
86
53.1k
    if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) {
87
27.9k
        return igraph_i_connected_components_weak(graph, membership, csize, no);
88
27.9k
    } else if (mode == IGRAPH_STRONG) {
89
25.2k
        return igraph_i_connected_components_strong(graph, membership, csize, no);
90
25.2k
    }
91
92
0
    IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL);
93
0
}
94
95
static igraph_error_t igraph_i_connected_components_weak(
96
    const igraph_t *graph, igraph_vector_int_t *membership,
97
    igraph_vector_int_t *csize, igraph_int_t *no
98
27.9k
) {
99
100
27.9k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
101
27.9k
    igraph_int_t no_of_components;
102
27.9k
    igraph_bitset_t already_added;
103
27.9k
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
104
27.9k
    igraph_vector_int_t neis = IGRAPH_VECTOR_NULL;
105
106
    /* Memory for result, csize is dynamically allocated */
107
27.9k
    if (membership) {
108
2.91k
        IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes));
109
2.91k
    }
110
27.9k
    if (csize) {
111
10.4k
        igraph_vector_int_clear(csize);
112
10.4k
    }
113
114
    /* Try to make use of cached information. */
115
27.9k
    if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) &&
116
14.5k
        igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED)) {
117
        /* If we know that the graph is weakly connected from the cache,
118
         * we can return the result right away. We keep in mind that
119
         * the null graph is considered disconnected, therefore any connected
120
         * graph has precisely one component. */
121
2.27k
        if (membership) {
122
            /* All vertices are members of the same component,
123
             * component number 0. */
124
865
            igraph_vector_int_null(membership);
125
865
        }
126
2.27k
        if (csize) {
127
            /* The size of the single component is the same as the vertex count. */
128
1.35k
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, no_of_nodes));
129
1.35k
        }
130
2.27k
        if (no) {
131
            /* There is one component. */
132
1.78k
            *no = 1;
133
1.78k
        }
134
2.27k
        return IGRAPH_SUCCESS;
135
2.27k
    }
136
137
25.6k
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
138
25.6k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10);
139
25.6k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
140
141
    /* The algorithm */
142
143
25.6k
    no_of_components = 0;
144
2.66M
    for (igraph_int_t first_node = 0; first_node < no_of_nodes; ++first_node) {
145
2.63M
        igraph_int_t act_component_size;
146
147
2.63M
        if (IGRAPH_BIT_TEST(already_added, first_node)) {
148
397k
            continue;
149
397k
        }
150
2.23M
        IGRAPH_ALLOW_INTERRUPTION();
151
152
2.23M
        IGRAPH_BIT_SET(already_added, first_node);
153
2.23M
        act_component_size = 1;
154
2.23M
        if (membership) {
155
334k
            VECTOR(*membership)[first_node] = no_of_components;
156
334k
        }
157
2.23M
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, first_node));
158
159
4.87M
        while ( !igraph_dqueue_int_empty(&q) ) {
160
2.63M
            igraph_int_t act_node = igraph_dqueue_int_pop(&q);
161
2.63M
            IGRAPH_CHECK(igraph_neighbors(
162
2.63M
                graph, &neis, act_node, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
163
2.63M
            ));
164
2.63M
            igraph_int_t nei_count = igraph_vector_int_size(&neis);
165
3.87M
            for (igraph_int_t i = 0; i < nei_count; i++) {
166
1.23M
                igraph_int_t neighbor = VECTOR(neis)[i];
167
1.23M
                if (IGRAPH_BIT_TEST(already_added, neighbor)) {
168
836k
                    continue;
169
836k
                }
170
397k
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
171
397k
                IGRAPH_BIT_SET(already_added, neighbor);
172
397k
                act_component_size++;
173
397k
                if (membership) {
174
46.9k
                    VECTOR(*membership)[neighbor] = no_of_components;
175
46.9k
                }
176
397k
            }
177
2.63M
        }
178
179
2.23M
        no_of_components++;
180
2.23M
        if (csize) {
181
1.37M
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size));
182
1.37M
        }
183
2.23M
    }
184
185
    /* Cleaning up */
186
187
25.6k
    if (no) {
188
18.6k
        *no = no_of_components;
189
18.6k
    }
190
191
    /* Clean up */
192
25.6k
    igraph_bitset_destroy(&already_added);
193
25.6k
    igraph_dqueue_int_destroy(&q);
194
25.6k
    igraph_vector_int_destroy(&neis);
195
25.6k
    IGRAPH_FINALLY_CLEAN(3);
196
197
    /* Update cache */
198
25.6k
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, no_of_components == 1);
199
200
25.6k
    return IGRAPH_SUCCESS;
201
25.6k
}
202
203
static igraph_error_t igraph_i_connected_components_strong(
204
    const igraph_t *graph, igraph_vector_int_t *membership,
205
    igraph_vector_int_t *csize, igraph_int_t *no
206
27.0k
) {
207
27.0k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
208
27.0k
    igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL;
209
27.0k
    igraph_int_t num_seen;
210
27.0k
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
211
27.0k
    igraph_int_t no_of_components = 0;
212
27.0k
    igraph_vector_int_t out = IGRAPH_VECTOR_NULL;
213
27.0k
    igraph_adjlist_t adjlist;
214
215
    /* Memory for result, csize is dynamically allocated */
216
27.0k
    if (membership) {
217
25.2k
        IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes));
218
25.2k
    }
219
27.0k
    if (csize) {
220
2.12k
        igraph_vector_int_clear(csize);
221
2.12k
    }
222
223
    /* Try to make use of cached information. */
224
27.0k
    if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED) &&
225
2.06k
        igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED)) {
226
        /* If we know that the graph is strongly connected from the cache,
227
         * we can return the result right away. We keep in mind that
228
         * the null graph is considered disconnected, therefore any connected
229
         * graph has precisely one component. */
230
59
        if (membership) {
231
            /* All vertices are members of the same component,
232
             * component number 0. */
233
59
            igraph_vector_int_null(membership);
234
59
        }
235
59
        if (csize) {
236
            /* The size of the single component is the same as the vertex count. */
237
0
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, no_of_nodes));
238
0
        }
239
59
        if (no) {
240
            /* There is one component. */
241
59
            *no = 1;
242
59
        }
243
59
        return IGRAPH_SUCCESS;
244
59
    }
245
246
    /* The result */
247
248
26.9k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes);
249
26.9k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0);
250
26.9k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
251
252
26.9k
    IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes));
253
254
26.9k
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
255
26.9k
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
256
257
26.9k
    num_seen = 0;
258
1.68M
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
259
1.65M
        const igraph_vector_int_t *tmp;
260
261
1.65M
        IGRAPH_ALLOW_INTERRUPTION();
262
263
1.65M
        tmp = igraph_adjlist_get(&adjlist, i);
264
1.65M
        if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) {
265
520k
            continue;
266
520k
        }
267
268
1.13M
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, i));
269
7.37M
        while (!igraph_dqueue_int_empty(&q)) {
270
6.24M
            igraph_int_t act_node = igraph_dqueue_int_back(&q);
271
6.24M
            tmp = igraph_adjlist_get(&adjlist, act_node);
272
6.24M
            if (VECTOR(next_nei)[act_node] == 0) {
273
                /* this is the first time we've met this vertex */
274
1.65M
                VECTOR(next_nei)[act_node]++;
275
4.58M
            } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) {
276
                /* we've already met this vertex but it has more children */
277
2.93M
                igraph_int_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1];
278
2.93M
                if (VECTOR(next_nei)[neighbor] == 0) {
279
520k
                    IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
280
520k
                }
281
2.93M
                VECTOR(next_nei)[act_node]++;
282
2.93M
            } else {
283
                /* we've met this vertex and it has no more children */
284
1.65M
                IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node));
285
1.65M
                igraph_dqueue_int_pop_back(&q);
286
1.65M
                num_seen++;
287
288
1.65M
                if (num_seen % 10000 == 0) {
289
                    /* time to report progress and allow the user to interrupt */
290
0
                    IGRAPH_PROGRESS("Strongly connected components: ",
291
0
                                    num_seen * 50.0 / no_of_nodes, NULL);
292
0
                    IGRAPH_ALLOW_INTERRUPTION();
293
0
                }
294
1.65M
            }
295
6.24M
        } /* while q */
296
1.13M
    }  /* for */
297
298
26.9k
    IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL);
299
300
26.9k
    igraph_adjlist_destroy(&adjlist);
301
26.9k
    IGRAPH_FINALLY_CLEAN(1);
302
303
26.9k
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
304
26.9k
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
305
306
    /* OK, we've the 'out' values for the nodes, let's use them in
307
       decreasing order with the help of a heap */
308
309
26.9k
    igraph_vector_int_null(&next_nei);             /* mark already added vertices */
310
26.9k
    num_seen = 0;
311
312
1.68M
    while (!igraph_vector_int_empty(&out)) {
313
1.65M
        igraph_int_t act_component_size;
314
1.65M
        igraph_int_t grandfather = igraph_vector_int_pop_back(&out);
315
316
1.65M
        if (VECTOR(next_nei)[grandfather] != 0) {
317
424k
            continue;
318
424k
        }
319
1.23M
        VECTOR(next_nei)[grandfather] = 1;
320
1.23M
        act_component_size = 1;
321
1.23M
        if (membership) {
322
1.18M
            VECTOR(*membership)[grandfather] = no_of_components;
323
1.18M
        }
324
1.23M
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather));
325
326
1.23M
        num_seen++;
327
1.23M
        if (num_seen % 10000 == 0) {
328
            /* time to report progress and allow the user to interrupt */
329
0
            IGRAPH_PROGRESS("Strongly connected components: ",
330
0
                            50.0 + num_seen * 50.0 / no_of_nodes, NULL);
331
0
            IGRAPH_ALLOW_INTERRUPTION();
332
0
        }
333
334
2.88M
        while (!igraph_dqueue_int_empty(&q)) {
335
1.65M
            igraph_int_t act_node = igraph_dqueue_int_pop_back(&q);
336
1.65M
            const igraph_vector_int_t *tmp = igraph_adjlist_get(&adjlist, act_node);
337
1.65M
            const igraph_int_t n = igraph_vector_int_size(tmp);
338
4.58M
            for (igraph_int_t i = 0; i < n; i++) {
339
2.93M
                igraph_int_t neighbor = VECTOR(*tmp)[i];
340
2.93M
                if (VECTOR(next_nei)[neighbor] != 0) {
341
2.50M
                    continue;
342
2.50M
                }
343
424k
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
344
424k
                VECTOR(next_nei)[neighbor] = 1;
345
424k
                act_component_size++;
346
424k
                if (membership) {
347
384k
                    VECTOR(*membership)[neighbor] = no_of_components;
348
384k
                }
349
350
424k
                num_seen++;
351
424k
                if (num_seen % 10000 == 0) {
352
                    /* time to report progress and allow the user to interrupt */
353
0
                    IGRAPH_PROGRESS("Strongly connected components: ",
354
0
                                    50.0 + num_seen * 50.0 / no_of_nodes, NULL);
355
0
                    IGRAPH_ALLOW_INTERRUPTION();
356
0
                }
357
424k
            }
358
1.65M
        }
359
360
1.23M
        no_of_components++;
361
1.23M
        if (csize) {
362
340k
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size));
363
340k
        }
364
1.23M
    }
365
366
26.9k
    IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL);
367
368
26.9k
    if (no) {
369
26.9k
        *no = no_of_components;
370
26.9k
    }
371
372
    /* Clean up */
373
26.9k
    igraph_adjlist_destroy(&adjlist);
374
26.9k
    igraph_vector_int_destroy(&out);
375
26.9k
    igraph_dqueue_int_destroy(&q);
376
26.9k
    igraph_vector_int_destroy(&next_nei);
377
26.9k
    IGRAPH_FINALLY_CLEAN(4);
378
379
    /* Update cache */
380
26.9k
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, no_of_components == 1);
381
26.9k
    if (no_of_components == 1) {
382
888
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true);
383
888
    }
384
385
26.9k
    return IGRAPH_SUCCESS;
386
26.9k
}
387
388
static igraph_error_t igraph_i_is_connected_weak(const igraph_t *graph, igraph_bool_t *res);
389
390
/**
391
 * \ingroup structural
392
 * \function igraph_is_connected
393
 * \brief Decides whether the graph is (weakly or strongly) connected.
394
 *
395
 * A graph is considered connected when any of its vertices is reachable
396
 * from any other. A directed graph with this property is called
397
 * \em strongly connected. A directed graph that would be connected when
398
 * ignoring the directions of its edges is called \em weakly connected.
399
 *
400
 * </para><para>
401
 * A graph with zero vertices (i.e. the null graph) is \em not connected by
402
 * definition. This behaviour changed in igraph 0.9; earlier versions assumed
403
 * that the null graph is connected. See the following issue on Github for the
404
 * argument that led us to change the definition:
405
 * https://github.com/igraph/igraph/issues/1539
406
 *
407
 * </para><para>
408
 * The return value of this function is cached in the graph itself, separately
409
 * for weak and strong connectivity. Calling the function multiple times with
410
 * no modifications to the graph in between will return a cached value in O(1)
411
 * time.
412
 *
413
 * \param graph The graph object to analyze.
414
 * \param res Pointer to a Boolean variable, the result will be stored
415
 *        here.
416
 * \param mode For a directed graph this specifies whether to calculate
417
 *        weak or strong connectedness. Possible values:
418
 *        \c IGRAPH_WEAK,
419
 *        \c IGRAPH_STRONG. This argument is
420
 *        ignored for undirected graphs.
421
 * \return Error code:
422
 *        \c IGRAPH_EINVAL: invalid mode argument.
423
 *
424
 * \sa \ref igraph_connected_components() to find the connected components,
425
 * \ref igraph_is_biconnected() to check if the graph is 2-vertex-connected.
426
 *
427
 * Time complexity: O(|V|+|E|), the
428
 * number of vertices
429
 * plus the number of edges in the graph.
430
 */
431
432
igraph_error_t igraph_is_connected(const igraph_t *graph, igraph_bool_t *res,
433
34.6k
                        igraph_connectedness_t mode) {
434
435
34.6k
    igraph_cached_property_t prop;
436
34.6k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
437
34.6k
    igraph_int_t no;
438
439
34.6k
    if (!igraph_is_directed(graph)) {
440
31.8k
        mode = IGRAPH_WEAK;
441
31.8k
    }
442
443
34.6k
    switch (mode) {
444
31.8k
        case IGRAPH_WEAK:
445
31.8k
            prop = IGRAPH_PROP_IS_WEAKLY_CONNECTED;
446
31.8k
            break;
447
448
2.79k
        case IGRAPH_STRONG:
449
2.79k
            prop = IGRAPH_PROP_IS_STRONGLY_CONNECTED;
450
2.79k
            break;
451
452
0
        default:
453
0
            IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL);
454
34.6k
    }
455
456
34.6k
    IGRAPH_RETURN_IF_CACHED_BOOL(graph, prop, res);
457
458
26.8k
    if (no_of_nodes == 0) {
459
        /* Changed in igraph 0.9; see https://github.com/igraph/igraph/issues/1539
460
         * for the reasoning behind the change */
461
4
        *res = false;
462
26.8k
    } else if (no_of_nodes == 1) {
463
106
        *res = true;
464
26.7k
    } else if (mode == IGRAPH_WEAK) {
465
23.9k
        IGRAPH_CHECK(igraph_i_is_connected_weak(graph, res));
466
23.9k
    } else {   /* mode == IGRAPH_STRONG */
467
        /* A strongly connected graph has at least as many edges as vertices,
468
         * except for the singleton graph, which is handled above. */
469
2.75k
        if (igraph_ecount(graph) < no_of_nodes) {
470
935
            *res = false;
471
1.81k
        } else {
472
1.81k
            IGRAPH_CHECK(igraph_i_connected_components_strong(graph, NULL, NULL, &no));
473
1.81k
            *res = (no == 1);
474
1.81k
        }
475
2.75k
    }
476
477
    /* Cache updates are done in igraph_i_connected_components_strong() and
478
     * igraph_i_is_connected_weak() because those might be called from other
479
     * places and we want to make use of the caching if so */
480
481
26.8k
    return IGRAPH_SUCCESS;
482
26.8k
}
483
484
23.9k
static igraph_error_t igraph_i_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) {
485
23.9k
    const igraph_int_t no_of_nodes = igraph_vcount(graph);
486
23.9k
    const igraph_int_t no_of_edges = igraph_ecount(graph);
487
23.9k
    igraph_int_t added_count;
488
23.9k
    igraph_bitset_t already_added;
489
23.9k
    igraph_vector_int_t neis;
490
23.9k
    igraph_dqueue_int_t q;
491
492
    /* By convention, the null graph is not considered connected.
493
     * See https://github.com/igraph/igraph/issues/1538 */
494
23.9k
    if (no_of_nodes == 0) {
495
0
        *res = false;
496
0
        goto exit;
497
0
    }
498
499
    /* A connected graph has at least |V| - 1 edges. */
500
23.9k
    if (no_of_edges < no_of_nodes - 1) {
501
10.1k
        *res = false;
502
10.1k
        goto exit;
503
10.1k
    }
504
505
13.7k
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
506
13.7k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 10);
507
13.7k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
508
509
    /* Try to find at least two components */
510
13.7k
    IGRAPH_BIT_SET(already_added, 0);
511
13.7k
    IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0));
512
513
13.7k
    added_count = 1;
514
118k
    while (! igraph_dqueue_int_empty(&q)) {
515
116k
        IGRAPH_ALLOW_INTERRUPTION();
516
517
116k
        const igraph_int_t actnode = igraph_dqueue_int_pop(&q);
518
519
116k
        IGRAPH_CHECK(igraph_neighbors(
520
116k
            graph, &neis, actnode, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
521
116k
        ));
522
116k
        const igraph_int_t nei_count = igraph_vector_int_size(&neis);
523
524
562k
        for (igraph_int_t i = 0; i < nei_count; i++) {
525
457k
            const igraph_int_t neighbor = VECTOR(neis)[i];
526
457k
            if (IGRAPH_BIT_TEST(already_added, neighbor)) {
527
295k
                continue;
528
295k
            }
529
530
162k
            IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
531
162k
            added_count++;
532
162k
            IGRAPH_BIT_SET(already_added, neighbor);
533
534
162k
            if (added_count == no_of_nodes) {
535
                /* We have already reached all nodes: the graph is connected.
536
                 * We can stop the traversal now. */
537
11.8k
                goto done;
538
11.8k
            }
539
162k
        }
540
116k
    }
541
542
13.7k
done:
543
    /* Connected? */
544
13.7k
    *res = (added_count == no_of_nodes);
545
546
13.7k
    igraph_bitset_destroy(&already_added);
547
13.7k
    igraph_dqueue_int_destroy(&q);
548
13.7k
    igraph_vector_int_destroy(&neis);
549
13.7k
    IGRAPH_FINALLY_CLEAN(3);
550
551
23.9k
exit:
552
23.9k
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, *res);
553
23.9k
    if (igraph_is_directed(graph) && !(*res)) {
554
        /* If the graph is not weakly connected, it is not strongly connected
555
         * either so we can also cache that */
556
0
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, *res);
557
0
    }
558
559
23.9k
    return IGRAPH_SUCCESS;
560
13.7k
}
561
562
static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph,
563
                                   igraph_graph_list_t *components,
564
                                   igraph_int_t maxcompno, igraph_int_t minelements);
565
566
static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph,
567
                                     igraph_graph_list_t *components,
568
                                     igraph_int_t maxcompno, igraph_int_t minelements);
569
570
/**
571
 * \function igraph_decompose
572
 * \brief Decomposes a graph into connected components.
573
 *
574
 * Creates a separate graph for each component of a graph. Note that the
575
 * vertex IDs in the new graphs will be different than in the original
576
 * graph, except when there is only a single component in the original graph.
577
 *
578
 * \param graph The original graph.
579
 * \param components This list of graphs will contain the individual components.
580
 *   It should be initialized before calling this function and will be resized
581
 *   to hold the graphs.
582
 * \param mode Either \c IGRAPH_WEAK or \c IGRAPH_STRONG for weakly
583
 *    and strongly connected components respectively.
584
 * \param maxcompno The maximum number of components to return. The
585
 *    first \p maxcompno components will be returned (which hold at
586
 *    least \p minelements vertices, see the next parameter), the
587
 *    others will be ignored. Supply <code>-1</code> here if you don't
588
 *    want to limit the number of components.
589
 * \param minelements The minimum number of vertices a component
590
 *    should contain in order to place it in the \p components
591
 *    vector. For example, supplying 2 here ignores isolated vertices.
592
 * \return Error code, \c IGRAPH_ENOMEM if there is not enough memory
593
 *   to perform the operation.
594
 *
595
 * Added in version 0.2.</para><para>
596
 *
597
 * Time complexity: O(|V|+|E|), the number of vertices plus the number
598
 * of edges.
599
 *
600
 * \example examples/simple/igraph_decompose.c
601
 */
602
603
igraph_error_t igraph_decompose(const igraph_t *graph, igraph_graph_list_t *components,
604
                     igraph_connectedness_t mode,
605
4.38k
                     igraph_int_t maxcompno, igraph_int_t minelements) {
606
4.38k
    if (!igraph_is_directed(graph)) {
607
2.25k
        mode = IGRAPH_WEAK;
608
2.25k
    }
609
610
4.38k
    switch (mode) {
611
2.25k
    case IGRAPH_WEAK:
612
2.25k
        return igraph_i_decompose_weak(graph, components, maxcompno, minelements);
613
2.12k
    case IGRAPH_STRONG:
614
2.12k
        return igraph_i_decompose_strong(graph, components, maxcompno, minelements);
615
0
    default:
616
0
        IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL);
617
4.38k
    }
618
4.38k
}
619
620
static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph,
621
                                   igraph_graph_list_t *components,
622
2.25k
                                   igraph_int_t maxcompno, igraph_int_t minelements) {
623
624
2.25k
    igraph_int_t actstart;
625
2.25k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
626
2.25k
    igraph_int_t resco = 0;   /* number of graphs created so far */
627
2.25k
    igraph_bitset_t already_added;
628
2.25k
    igraph_dqueue_int_t q;
629
2.25k
    igraph_vector_int_t verts;
630
2.25k
    igraph_vector_int_t neis;
631
2.25k
    igraph_vector_int_t vids_old2new;
632
2.25k
    igraph_int_t i;
633
2.25k
    igraph_t newg;
634
635
636
2.25k
    if (maxcompno < 0) {
637
2.25k
        maxcompno = IGRAPH_INTEGER_MAX;
638
2.25k
    }
639
640
2.25k
    igraph_graph_list_clear(components);
641
642
    /* already_added keeps track of what nodes made it into a graph already */
643
2.25k
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
644
2.25k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
645
2.25k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0);
646
2.25k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
647
2.25k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes);
648
2.25k
    igraph_vector_int_fill(&vids_old2new, -1);
649
650
    /* vids_old2new would have been created internally in igraph_induced_subgraph(),
651
       but it is slow if the graph is large and consists of many small components,
652
       so we create it once here and then re-use it */
653
654
    /* add a node and its neighbors at once, recursively
655
       then switch to next node that has not been added already */
656
389k
    for (actstart = 0; resco < maxcompno && actstart < no_of_nodes; actstart++) {
657
658
386k
        if (IGRAPH_BIT_TEST(already_added, actstart)) {
659
52.4k
            continue;
660
52.4k
        }
661
334k
        IGRAPH_ALLOW_INTERRUPTION();
662
663
334k
        igraph_vector_int_clear(&verts);
664
665
        /* add the node itself */
666
334k
        IGRAPH_BIT_SET(already_added, actstart);
667
334k
        IGRAPH_CHECK(igraph_vector_int_push_back(&verts, actstart));
668
334k
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, actstart));
669
670
        /* add the neighbors, recursively */
671
721k
        while (!igraph_dqueue_int_empty(&q) ) {
672
            /* pop from the queue of this component */
673
386k
            igraph_int_t actvert = igraph_dqueue_int_pop(&q);
674
386k
            IGRAPH_CHECK(igraph_neighbors(
675
386k
                graph, &neis, actvert, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
676
386k
            ));
677
386k
            igraph_int_t nei_count = igraph_vector_int_size(&neis);
678
            /* iterate over the neighbors */
679
521k
            for (i = 0; i < nei_count; i++) {
680
134k
                igraph_int_t neighbor = VECTOR(neis)[i];
681
134k
                if (IGRAPH_BIT_TEST(already_added, neighbor)) {
682
81.8k
                    continue;
683
81.8k
                }
684
                /* add neighbor */
685
52.4k
                IGRAPH_BIT_SET(already_added, neighbor);
686
687
                /* recursion: append neighbor to the queues */
688
52.4k
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
689
52.4k
                IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor));
690
52.4k
            }
691
386k
        }
692
693
        /* ok, we have a component */
694
334k
        if (igraph_vector_int_size(&verts) < minelements) {
695
332k
            continue;
696
332k
        }
697
698
2.27k
        IGRAPH_CHECK(igraph_i_induced_subgraph_map(
699
2.27k
            graph, &newg, igraph_vss_vector(&verts),
700
2.27k
            IGRAPH_SUBGRAPH_AUTO, &vids_old2new,
701
2.27k
            /* invmap = */ NULL, /* map_is_prepared = */ true
702
2.27k
        ));
703
2.27k
        IGRAPH_FINALLY(igraph_destroy, &newg);
704
2.27k
        IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg));
705
2.27k
        IGRAPH_FINALLY_CLEAN(1);  /* ownership of newg now taken by 'components' */
706
2.27k
        resco++;
707
708
        /* vids_old2new does not have to be cleaned up here; since we are doing
709
         * weak decomposition, each vertex will appear in only one of the
710
         * connected components so we won't ever touch an item in vids_old2new
711
         * if it was already set to a non-zero value in a previous component */
712
713
2.27k
    } /* for actstart++ */
714
715
2.25k
    igraph_vector_int_destroy(&vids_old2new);
716
2.25k
    igraph_vector_int_destroy(&neis);
717
2.25k
    igraph_vector_int_destroy(&verts);
718
2.25k
    igraph_dqueue_int_destroy(&q);
719
2.25k
    igraph_bitset_destroy(&already_added);
720
2.25k
    IGRAPH_FINALLY_CLEAN(5);
721
722
2.25k
    return IGRAPH_SUCCESS;
723
2.25k
}
724
725
static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph,
726
                                     igraph_graph_list_t *components,
727
2.12k
                                     igraph_int_t maxcompno, igraph_int_t minelements) {
728
729
730
2.12k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
731
732
    /* this is a heap used twice for checking what nodes have
733
     * been counted already */
734
2.12k
    igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL;
735
736
2.12k
    igraph_int_t i, n, num_seen;
737
2.12k
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
738
739
2.12k
    igraph_int_t no_of_components = 0;
740
741
2.12k
    igraph_vector_int_t out = IGRAPH_VECTOR_NULL;
742
2.12k
    const igraph_vector_int_t* tmp;
743
744
2.12k
    igraph_adjlist_t adjlist;
745
2.12k
    igraph_vector_int_t verts;
746
2.12k
    igraph_vector_int_t vids_old2new;
747
2.12k
    igraph_t newg;
748
749
2.12k
    if (maxcompno < 0) {
750
0
        maxcompno = IGRAPH_INTEGER_MAX;
751
0
    }
752
753
2.12k
    igraph_graph_list_clear(components);
754
755
    /* The result */
756
757
2.12k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes);
758
2.12k
    igraph_vector_int_fill(&vids_old2new, -1);
759
760
2.12k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0);
761
2.12k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes);
762
2.12k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0);
763
2.12k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
764
765
2.12k
    IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes));
766
767
2.12k
    igraph_vector_int_null(&out);
768
769
2.12k
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
770
2.12k
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
771
772
    /* vids_old2new would have been created internally in igraph_induced_subgraph(),
773
       but it is slow if the graph is large and consists of many small components,
774
       so we create it once here and then re-use it */
775
776
    /* number of components seen */
777
2.12k
    num_seen = 0;
778
    /* populate the 'out' vector by browsing a node and following up
779
       all its neighbors recursively, then switching to the next
780
       unassigned node */
781
364k
    for (i = 0; i < no_of_nodes; i++) {
782
362k
        IGRAPH_ALLOW_INTERRUPTION();
783
784
        /* get all the 'out' neighbors of this node
785
         * NOTE: next_nei is initialized [0, 0, ...] */
786
362k
        tmp = igraph_adjlist_get(&adjlist, i);
787
362k
        if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) {
788
33.4k
            continue;
789
33.4k
        }
790
791
        /* add this node to the queue for this component */
792
328k
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, i));
793
794
        /* consume the tree from this node ("root") recursively
795
         * until there is no more */
796
1.13M
        while (!igraph_dqueue_int_empty(&q)) {
797
            /* this looks up but does NOT consume the queue */
798
803k
            igraph_int_t act_node = igraph_dqueue_int_back(&q);
799
800
            /* get all neighbors of this node */
801
803k
            tmp = igraph_adjlist_get(&adjlist, act_node);
802
803k
            if (VECTOR(next_nei)[act_node] == 0) {
803
                /* this is the first time we've met this vertex,
804
                     * because next_nei is initialized [0, 0, ...] */
805
362k
                VECTOR(next_nei)[act_node]++;
806
                /* back to the queue, same vertex is up again */
807
808
440k
            } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) {
809
                /* we've already met this vertex but it has more children */
810
78.4k
                igraph_int_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1];
811
78.4k
                if (VECTOR(next_nei)[neighbor] == 0) {
812
                    /* add the root of the other children to the queue */
813
33.4k
                    IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
814
33.4k
                }
815
78.4k
                VECTOR(next_nei)[act_node]++;
816
362k
            } else {
817
                /* we've met this vertex and it has no more children */
818
362k
                IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node));
819
                /* this consumes the queue, since there's nowhere to go */
820
362k
                igraph_dqueue_int_pop_back(&q);
821
362k
                num_seen++;
822
823
362k
                if (num_seen % 10000 == 0) {
824
                    /* time to report progress and allow the user to interrupt */
825
0
                    IGRAPH_PROGRESS("Strongly connected components: ",
826
0
                                    num_seen * 50.0 / no_of_nodes, NULL);
827
0
                    IGRAPH_ALLOW_INTERRUPTION();
828
0
                }
829
362k
            }
830
803k
        } /* while q */
831
328k
    }  /* for */
832
833
2.12k
    IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL);
834
835
2.12k
    igraph_adjlist_destroy(&adjlist);
836
2.12k
    IGRAPH_FINALLY_CLEAN(1);
837
838
2.12k
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
839
2.12k
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
840
841
    /* OK, we've the 'out' values for the nodes, let's use them in
842
     * decreasing order with the help of the next_nei heap */
843
844
2.12k
    igraph_vector_int_null(&next_nei);             /* mark already added vertices */
845
846
    /* number of components built */
847
2.12k
    num_seen = 0;
848
364k
    while (!igraph_vector_int_empty(&out) && no_of_components < maxcompno) {
849
        /* consume the vector from the last element */
850
362k
        igraph_int_t grandfather = igraph_vector_int_pop_back(&out);
851
852
        /* been here, done that
853
         * NOTE: next_nei is initialized as [0, 0, ...] */
854
362k
        if (VECTOR(next_nei)[grandfather] != 0) {
855
21.3k
            continue;
856
21.3k
        }
857
858
        /* collect all the members of this component */
859
340k
        igraph_vector_int_clear(&verts);
860
861
        /* this node is gone for any future components */
862
340k
        VECTOR(next_nei)[grandfather] = 1;
863
864
        /* add to component */
865
340k
        IGRAPH_CHECK(igraph_vector_int_push_back(&verts, grandfather));
866
340k
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather));
867
868
340k
        num_seen++;
869
340k
        if (num_seen % 10000 == 0) {
870
            /* time to report progress and allow the user to interrupt */
871
0
            IGRAPH_PROGRESS("Strongly connected components: ",
872
0
                            50.0 + num_seen * 50.0 / no_of_nodes, NULL);
873
0
            IGRAPH_ALLOW_INTERRUPTION();
874
0
        }
875
876
703k
        while (!igraph_dqueue_int_empty(&q)) {
877
            /* consume the queue from this node */
878
362k
            igraph_int_t act_node = igraph_dqueue_int_pop_back(&q);
879
362k
            tmp = igraph_adjlist_get(&adjlist, act_node);
880
362k
            n = igraph_vector_int_size(tmp);
881
440k
            for (i = 0; i < n; i++) {
882
78.4k
                igraph_int_t neighbor = VECTOR(*tmp)[i];
883
78.4k
                if (VECTOR(next_nei)[neighbor] != 0) {
884
57.0k
                    continue;
885
57.0k
                }
886
21.3k
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
887
21.3k
                VECTOR(next_nei)[neighbor] = 1;
888
889
                /* add to component */
890
21.3k
                IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor));
891
892
21.3k
                num_seen++;
893
21.3k
                if (num_seen % 10000 == 0) {
894
                    /* time to report progress and allow the user to interrupt */
895
0
                    IGRAPH_PROGRESS("Strongly connected components: ",
896
0
                                    50.0 + num_seen * 50.0 / no_of_nodes, NULL);
897
0
                    IGRAPH_ALLOW_INTERRUPTION();
898
0
                }
899
21.3k
            }
900
362k
        }
901
902
        /* ok, we have a component */
903
340k
        if (igraph_vector_int_size(&verts) < minelements) {
904
340k
            continue;
905
340k
        }
906
907
648
        IGRAPH_CHECK(igraph_i_induced_subgraph_map(
908
648
            graph, &newg, igraph_vss_vector(&verts),
909
648
            IGRAPH_SUBGRAPH_AUTO, &vids_old2new,
910
648
            /* invmap = */ 0, /* map_is_prepared = */ 1
911
648
        ));
912
648
        IGRAPH_FINALLY(igraph_destroy, &newg);
913
648
        IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg));
914
648
        IGRAPH_FINALLY_CLEAN(1);  /* ownership of newg now taken by 'components' */
915
916
        /* vids_old2new has to be cleaned up here because a vertex may appear
917
         * in multiple strongly connected components. Simply calling
918
         * igraph_vector_int_fill() would be an O(n) operation where n is the number
919
         * of vertices in the large graph so we cannot do that; we have to
920
         * iterate over 'verts' instead */
921
648
        n = igraph_vector_int_size(&verts);
922
22.1k
        for (i = 0; i < n; i++) {
923
21.4k
            VECTOR(vids_old2new)[VECTOR(verts)[i]] = 0;
924
21.4k
        }
925
926
648
        no_of_components++;
927
648
    }
928
929
2.12k
    IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL);
930
931
    /* Clean up, return */
932
933
2.12k
    igraph_vector_int_destroy(&vids_old2new);
934
2.12k
    igraph_vector_int_destroy(&verts);
935
2.12k
    igraph_adjlist_destroy(&adjlist);
936
2.12k
    igraph_vector_int_destroy(&out);
937
2.12k
    igraph_dqueue_int_destroy(&q);
938
2.12k
    igraph_vector_int_destroy(&next_nei);
939
2.12k
    IGRAPH_FINALLY_CLEAN(6);
940
941
2.12k
    return IGRAPH_SUCCESS;
942
943
2.12k
}
944
945
/**
946
 * \function igraph_articulation_points
947
 * \brief Finds the articulation points in a graph.
948
 *
949
 * A vertex is an articulation point if its removal increases
950
 * the number of (weakly) connected components in the graph.
951
 *
952
 * </para><para>
953
 * Note that a graph without any articulation points is not necessarily
954
 * biconnected. Counterexamples are the two-vertex complete graph as well
955
 * as empty graphs. Use \ref igraph_is_biconnected() to check whether
956
 * a graph is biconnected.
957
 *
958
 * \param graph The input graph. It will be treated as undirected.
959
 * \param res Pointer to an initialized vector, the articulation points will
960
 *   be stored here.
961
 * \return Error code.
962
 *
963
 * Time complexity: O(|V|+|E|), linear in the number of vertices and edges.
964
 *
965
 * \sa \ref igraph_biconnected_components(), \ref igraph_is_bipartite(),
966
 * \ref igraph_connected_components(), \ref igraph_bridges()
967
 */
968
969
1.60k
igraph_error_t igraph_articulation_points(const igraph_t *graph, igraph_vector_int_t *res) {
970
971
1.60k
    return igraph_biconnected_components(graph, NULL, NULL, NULL, NULL, res);
972
1.60k
}
973
974
/**
975
 * \function igraph_biconnected_components
976
 * \brief Calculates biconnected components.
977
 *
978
 * A graph is biconnected if the removal of any single vertex (and
979
 * its incident edges) does not disconnect it.
980
 *
981
 * </para><para>
982
 * A biconnected component of a graph is a maximal biconnected
983
 * subgraph of it. The biconnected components of a graph can be given
984
 * by a partition of its edges: every edge is a member of exactly
985
 * one biconnected component. Note that this is not true for
986
 * vertices: the same vertex can be part of many biconnected
987
 * components, while isolated vertices are part of none at all.
988
 *
989
 * </para><para>
990
 * Note that some authors do not consider the graph consisting of
991
 * two connected vertices as biconnected, however, igraph does.
992
 *
993
 * </para><para>
994
 * igraph does not consider components containing a single vertex only as
995
 * being biconnected. Isolated vertices will not be part of any of the
996
 * biconnected components. This means that checking whether there is a single
997
 * biconnected component is not sufficient for determining if a graph is
998
 * biconnected. Use \ref igraph_is_biconnected() for this purpose.
999
 *
1000
 * \param graph The input graph. It will be treated as undirected.
1001
 * \param no If not a \c NULL pointer, the number of biconnected components will
1002
 *     be stored here.
1003
 * \param tree_edges If not a \c NULL pointer, then the found components
1004
 *     are stored here, in a list of vectors. Every vector in the list
1005
 *     is a biconnected component, represented by its edges. More precisely,
1006
 *     a spanning tree of the biconnected component is returned.
1007
 * \param component_edges If not a \c NULL pointer, then the edges of the
1008
 *     biconnected components are stored here, in the same form as for
1009
 *     \c tree_edges.
1010
 * \param components If not a \c NULL pointer, then the vertices of the
1011
 *     biconnected components are stored here, in the same format as
1012
 *     for the previous two arguments.
1013
 * \param articulation_points If not a NULL pointer, then the
1014
 *     articulation points of the graph are stored in this vector.
1015
 *     A vertex is an articulation point if its removal increases the
1016
 *     number of (weakly) connected components in the graph.
1017
 * \return Error code.
1018
 *
1019
 * Time complexity: O(|V|+|E|), linear in the number of vertices and
1020
 * edges, but only if you do not calculate \p components and
1021
 * \p component_edges. If you calculate \p components, then it is
1022
 * quadratic in the number of vertices. If you calculate
1023
 * \p component_edges as well, then it is cubic in the number of
1024
 * vertices.
1025
 *
1026
 * \sa \ref igraph_articulation_points(), \ref igraph_is_biconnected(),
1027
 * \ref igraph_connected_components().
1028
 *
1029
 * \example examples/simple/igraph_biconnected_components.c
1030
 */
1031
1032
igraph_error_t igraph_biconnected_components(const igraph_t *graph,
1033
                                  igraph_int_t *no,
1034
                                  igraph_vector_int_list_t *tree_edges,
1035
                                  igraph_vector_int_list_t *component_edges,
1036
                                  igraph_vector_int_list_t *components,
1037
3.86k
                                  igraph_vector_int_t *articulation_points) {
1038
1039
3.86k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1040
3.86k
    igraph_vector_int_t nextptr;
1041
3.86k
    igraph_vector_int_t num, low;
1042
3.86k
    igraph_bitset_t found;
1043
3.86k
    igraph_vector_int_t *adjedges;
1044
3.86k
    igraph_stack_int_t path;
1045
3.86k
    igraph_stack_int_t edgestack;
1046
3.86k
    igraph_inclist_t inclist;
1047
3.86k
    igraph_int_t counter, rootdfs = 0;
1048
3.86k
    igraph_vector_int_t vertex_added;
1049
3.86k
    igraph_int_t comps = 0;
1050
3.86k
    igraph_vector_int_list_t *mycomponents = components, vcomponents;
1051
1052
3.86k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes);
1053
3.86k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes);
1054
3.86k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes);
1055
3.86k
    IGRAPH_BITSET_INIT_FINALLY(&found, no_of_nodes);
1056
1057
3.86k
    IGRAPH_STACK_INT_INIT_FINALLY(&path, 100);
1058
3.86k
    IGRAPH_STACK_INT_INIT_FINALLY(&edgestack, 100);
1059
1060
3.86k
    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
1061
3.86k
    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
1062
1063
3.86k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_added, no_of_nodes);
1064
1065
3.86k
    if (no) {
1066
2.25k
        *no = 0;
1067
2.25k
    }
1068
3.86k
    if (tree_edges) {
1069
0
        igraph_vector_int_list_clear(tree_edges);
1070
0
    }
1071
3.86k
    if (components) {
1072
2.25k
        igraph_vector_int_list_clear(components);
1073
2.25k
    }
1074
3.86k
    if (component_edges) {
1075
2.25k
        igraph_vector_int_list_clear(component_edges);
1076
2.25k
    }
1077
3.86k
    if (articulation_points) {
1078
3.86k
        igraph_vector_int_clear(articulation_points);
1079
3.86k
    }
1080
3.86k
    if (component_edges && !components) {
1081
0
        mycomponents = &vcomponents;
1082
0
        IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(mycomponents, 0);
1083
0
    }
1084
1085
401k
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
1086
1087
397k
        if (VECTOR(low)[i] != 0) {
1088
61.4k
            continue;    /* already visited */
1089
61.4k
        }
1090
1091
335k
        IGRAPH_ALLOW_INTERRUPTION();
1092
1093
335k
        IGRAPH_CHECK(igraph_stack_int_push(&path, i));
1094
335k
        counter = 1;
1095
335k
        rootdfs = 0;
1096
335k
        VECTOR(low)[i] = VECTOR(num)[i] = counter++;
1097
928k
        while (!igraph_stack_int_empty(&path)) {
1098
592k
            igraph_int_t n;
1099
592k
            igraph_int_t act = igraph_stack_int_top(&path);
1100
592k
            igraph_int_t actnext = VECTOR(nextptr)[act];
1101
1102
592k
            adjedges = igraph_inclist_get(&inclist, act);
1103
592k
            n = igraph_vector_int_size(adjedges);
1104
592k
            if (actnext < n) {
1105
                /* Step down (maybe) */
1106
195k
                igraph_int_t edge = VECTOR(*adjedges)[actnext];
1107
195k
                igraph_int_t nei = IGRAPH_OTHER(graph, edge, act);
1108
195k
                if (VECTOR(low)[nei] == 0) {
1109
61.4k
                    if (act == i) {
1110
16.7k
                        rootdfs++;
1111
16.7k
                    }
1112
61.4k
                    IGRAPH_CHECK(igraph_stack_int_push(&edgestack, edge));
1113
61.4k
                    IGRAPH_CHECK(igraph_stack_int_push(&path, nei));
1114
61.4k
                    VECTOR(low)[nei] = VECTOR(num)[nei] = counter++;
1115
133k
                } else {
1116
                    /* Update low value if needed */
1117
133k
                    if (VECTOR(num)[nei] < VECTOR(low)[act]) {
1118
59.9k
                        VECTOR(low)[act] = VECTOR(num)[nei];
1119
59.9k
                    }
1120
133k
                }
1121
195k
                VECTOR(nextptr)[act] += 1;
1122
397k
            } else {
1123
                /* Step up */
1124
397k
                igraph_stack_int_pop(&path);
1125
397k
                if (!igraph_stack_int_empty(&path)) {
1126
61.4k
                    igraph_int_t prev = igraph_stack_int_top(&path);
1127
                    /* Update LOW value if needed */
1128
61.4k
                    if (VECTOR(low)[act] < VECTOR(low)[prev]) {
1129
6.56k
                        VECTOR(low)[prev] = VECTOR(low)[act];
1130
6.56k
                    }
1131
                    /* Check for articulation point */
1132
61.4k
                    if (VECTOR(low)[act] >= VECTOR(num)[prev]) {
1133
50.9k
                        if (articulation_points && !IGRAPH_BIT_TEST(found, prev)
1134
33.9k
                            && prev != i /* the root */) {
1135
17.1k
                            IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, prev));
1136
17.1k
                            IGRAPH_BIT_SET(found, prev);
1137
17.1k
                        }
1138
50.9k
                        if (no) {
1139
45.2k
                            *no += 1;
1140
45.2k
                        }
1141
1142
                        /*------------------------------------*/
1143
                        /* Record the biconnected component just found */
1144
50.9k
                        if (tree_edges || mycomponents) {
1145
45.2k
                            igraph_vector_int_t *v, *v2;
1146
45.2k
                            comps++;
1147
45.2k
                            if (tree_edges) {
1148
0
                                IGRAPH_CHECK(igraph_vector_int_list_push_back_new(tree_edges, &v));
1149
0
                            }
1150
45.2k
                            if (mycomponents) {
1151
45.2k
                                IGRAPH_CHECK(igraph_vector_int_list_push_back_new(mycomponents, &v2));
1152
45.2k
                            }
1153
1154
52.4k
                            while (!igraph_stack_int_empty(&edgestack)) {
1155
52.4k
                                igraph_int_t e = igraph_stack_int_pop(&edgestack);
1156
52.4k
                                igraph_int_t from = IGRAPH_FROM(graph, e);
1157
52.4k
                                igraph_int_t to = IGRAPH_TO(graph, e);
1158
52.4k
                                if (tree_edges) {
1159
0
                                    IGRAPH_CHECK(igraph_vector_int_push_back(v, e));
1160
0
                                }
1161
52.4k
                                if (mycomponents) {
1162
52.4k
                                    if (VECTOR(vertex_added)[from] != comps) {
1163
48.7k
                                        VECTOR(vertex_added)[from] = comps;
1164
48.7k
                                        IGRAPH_CHECK(igraph_vector_int_push_back(v2, from));
1165
48.7k
                                    }
1166
52.4k
                                    if (VECTOR(vertex_added)[to] != comps) {
1167
49.0k
                                        VECTOR(vertex_added)[to] = comps;
1168
49.0k
                                        IGRAPH_CHECK(igraph_vector_int_push_back(v2, to));
1169
49.0k
                                    }
1170
52.4k
                                }
1171
52.4k
                                if (from == prev || to == prev) {
1172
45.2k
                                    break;
1173
45.2k
                                }
1174
52.4k
                            }
1175
1176
45.2k
                            if (component_edges) {
1177
45.2k
                                igraph_vector_int_t *nodes = igraph_vector_int_list_get_ptr(mycomponents, comps - 1);
1178
45.2k
                                igraph_int_t ii, no_vert = igraph_vector_int_size(nodes);
1179
45.2k
                                igraph_vector_int_t *vv;
1180
1181
45.2k
                                IGRAPH_CHECK(igraph_vector_int_list_push_back_new(component_edges, &vv));
1182
143k
                                for (ii = 0; ii < no_vert; ii++) {
1183
97.7k
                                    igraph_int_t vert = VECTOR(*nodes)[ii];
1184
97.7k
                                    igraph_vector_int_t *edges = igraph_inclist_get(&inclist,
1185
97.7k
                                                                 vert);
1186
97.7k
                                    igraph_int_t j, nn = igraph_vector_int_size(edges);
1187
1.71M
                                    for (j = 0; j < nn; j++) {
1188
1.61M
                                        igraph_int_t e = VECTOR(*edges)[j];
1189
1.61M
                                        igraph_int_t nei = IGRAPH_OTHER(graph, e, vert);
1190
1.61M
                                        if (VECTOR(vertex_added)[nei] == comps && nei < vert) {
1191
67.1k
                                            IGRAPH_CHECK(igraph_vector_int_push_back(vv, e));
1192
67.1k
                                        }
1193
1.61M
                                    }
1194
97.7k
                                }
1195
45.2k
                            }
1196
45.2k
                        } /* record component if requested */
1197
                        /*------------------------------------*/
1198
1199
50.9k
                    }
1200
61.4k
                } /* !igraph_stack_int_empty(&path) */
1201
397k
            }
1202
1203
592k
        } /* !igraph_stack_int_empty(&path) */
1204
1205
335k
        if (articulation_points && rootdfs >= 2) {
1206
1.94k
            IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, i));
1207
1.94k
        }
1208
1209
335k
    } /* i < no_of_nodes */
1210
1211
3.86k
    if (mycomponents != components) {
1212
0
        igraph_vector_int_list_destroy(mycomponents);
1213
0
        IGRAPH_FINALLY_CLEAN(1);
1214
0
    }
1215
1216
3.86k
    igraph_vector_int_destroy(&vertex_added);
1217
3.86k
    igraph_inclist_destroy(&inclist);
1218
3.86k
    igraph_stack_int_destroy(&edgestack);
1219
3.86k
    igraph_stack_int_destroy(&path);
1220
3.86k
    igraph_bitset_destroy(&found);
1221
3.86k
    igraph_vector_int_destroy(&low);
1222
3.86k
    igraph_vector_int_destroy(&num);
1223
3.86k
    igraph_vector_int_destroy(&nextptr);
1224
3.86k
    IGRAPH_FINALLY_CLEAN(8);
1225
1226
3.86k
    return IGRAPH_SUCCESS;
1227
3.86k
}
1228
1229
/**
1230
 * \function igraph_is_biconnected
1231
 * \brief Checks whether a graph is biconnected.
1232
 *
1233
 * A graph is biconnected if the removal of any single vertex (and
1234
 * its incident edges) does not disconnect it.
1235
 *
1236
 * </para><para>
1237
 * igraph does not consider single-vertex graphs biconnected.
1238
 *
1239
 * </para><para>
1240
 * Note that some authors do not consider the graph consisting of
1241
 * two connected vertices as biconnected, however, igraph does.
1242
 *
1243
 * \param graph The input graph. It will be treated as undirected.
1244
 * \param res If not a \c NULL pointer, the result will be returned here.
1245
 * \return Error code.
1246
 *
1247
 * Time complexity: O(|V|+|E|), linear in the number of vertices and edges.
1248
 *
1249
 * \sa \ref igraph_articulation_points(), \ref igraph_biconnected_components().
1250
 *
1251
 * \example examples/simple/igraph_is_biconnected.c
1252
 */
1253
1254
738
igraph_error_t igraph_is_biconnected(const igraph_t *graph, igraph_bool_t *res) {
1255
1256
738
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1257
738
    igraph_vector_int_t nextptr;
1258
738
    igraph_vector_int_t num, low;
1259
738
    igraph_stack_int_t path;
1260
738
    igraph_lazy_adjlist_t inclist;
1261
738
    igraph_bool_t is_biconnected = true;
1262
1263
738
    if (no_of_nodes == 0 || no_of_nodes == 1) {
1264
        /* The null graph is not connected, hence it is not biconnected either.
1265
         * The singleton graph is not biconnected. */
1266
20
        is_biconnected = false;
1267
20
        goto exit2;
1268
20
    }
1269
1270
    /* no_of_nodes == 2 is special: if the two nodes are connected, then the
1271
     * graph is both biconnected _and_ acyclic, unlike no_of_nodes >= 3, where
1272
     * the graph is not acyclic if it is biconnected. */
1273
1274
    /* We do not touch the cache for graphs with less than three nodes because
1275
     * of all the edge cases. */
1276
718
    if (no_of_nodes >= 3 && (
1277
686
        (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) &&
1278
0
        !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED)) ||
1279
686
        (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) &&
1280
0
        igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST))
1281
686
    )) {
1282
0
        is_biconnected = false;
1283
0
        goto exit2;
1284
0
    }
1285
1286
718
    IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes);
1287
718
    IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes);
1288
718
    IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes);
1289
1290
718
    IGRAPH_STACK_INT_INIT_FINALLY(&path, 100);
1291
1292
718
    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE));
1293
718
    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inclist);
1294
1295
718
    const igraph_int_t root = 0; /* start DFS from vertex 0 */
1296
718
    igraph_int_t counter = 1;
1297
718
    igraph_int_t rootdfs = 0;
1298
718
    IGRAPH_CHECK(igraph_stack_int_push(&path, root));
1299
718
    VECTOR(low)[root] = VECTOR(num)[root] = counter++;
1300
23.2k
    while (!igraph_stack_int_empty(&path)) {
1301
22.7k
        igraph_int_t act = igraph_stack_int_top(&path);
1302
22.7k
        igraph_int_t actnext = VECTOR(nextptr)[act];
1303
1304
22.7k
        const igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&inclist, act);
1305
22.7k
        const igraph_int_t n = igraph_vector_int_size(neis);
1306
22.7k
        if (actnext < n) {
1307
            /* Step down (maybe) */
1308
16.5k
            igraph_int_t nei = VECTOR(*neis)[actnext];
1309
16.5k
            if (VECTOR(low)[nei] == 0) {
1310
7.81k
                if (act == root) {
1311
3.88k
                    rootdfs++;
1312
3.88k
                }
1313
7.81k
                IGRAPH_CHECK(igraph_stack_int_push(&path, nei));
1314
7.81k
                VECTOR(low)[nei] = VECTOR(num)[nei] = counter++;
1315
8.68k
            } else {
1316
                /* Update low value if needed */
1317
8.68k
                if (VECTOR(num)[nei] < VECTOR(low)[act]) {
1318
6.26k
                    VECTOR(low)[act] = VECTOR(num)[nei];
1319
6.26k
                }
1320
8.68k
            }
1321
16.5k
            VECTOR(nextptr)[act] += 1;
1322
16.5k
        } else {
1323
            /* Step up */
1324
6.23k
            igraph_stack_int_pop(&path);
1325
6.23k
            if (!igraph_stack_int_empty(&path)) {
1326
5.70k
                igraph_int_t prev = igraph_stack_int_top(&path);
1327
                /* Update LOW value if needed */
1328
5.70k
                if (VECTOR(low)[act] < VECTOR(low)[prev]) {
1329
1.52k
                    VECTOR(low)[prev] = VECTOR(low)[act];
1330
1.52k
                }
1331
                /* Check for articulation point */
1332
5.70k
                if (VECTOR(low)[act] >= VECTOR(num)[prev]) {
1333
3.88k
                    if (prev != root /* the root */) {
1334
                        /* Found an articulation point, the graph is not biconnected */
1335
191
                        is_biconnected = false;
1336
191
                        goto exit;
1337
191
                    }
1338
3.88k
                }
1339
5.70k
            } /* !igraph_stack_int_empty(&path) */
1340
6.23k
        }
1341
1342
22.7k
    } /* !igraph_stack_int_empty(&path) */
1343
1344
    /* The root is an articulation point, the graph is not biconnected */
1345
527
    if (rootdfs >= 2) {
1346
104
        is_biconnected = false;
1347
104
        goto exit;
1348
104
    }
1349
1350
    /* We did not reach all vertices, the graph is not connected */
1351
423
    if (counter <= no_of_nodes) {
1352
359
        is_biconnected = false;
1353
359
        goto exit;
1354
359
    }
1355
1356
718
exit:
1357
1358
718
    igraph_lazy_adjlist_destroy(&inclist);
1359
718
    igraph_stack_int_destroy(&path);
1360
718
    igraph_vector_int_destroy(&low);
1361
718
    igraph_vector_int_destroy(&num);
1362
718
    igraph_vector_int_destroy(&nextptr);
1363
718
    IGRAPH_FINALLY_CLEAN(5);
1364
1365
738
exit2:
1366
1367
738
    if (res) {
1368
738
        *res = is_biconnected;
1369
738
    }
1370
1371
    /* We do not touch the cache for graphs with less than three nodes because
1372
     * of all the edge cases. */
1373
738
    if (is_biconnected && no_of_nodes >= 3) {
1374
41
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true);
1375
41
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, false);
1376
41
    }
1377
1378
738
    return IGRAPH_SUCCESS;
1379
718
}
1380
1381
1382
/**
1383
 * \function igraph_bridges
1384
 * \brief Finds all bridges in a graph.
1385
 *
1386
 * An edge is a bridge if its removal increases the number of (weakly)
1387
 * connected components in the graph.
1388
 *
1389
 * \param graph The input graph. It will be treated as undirected.
1390
 * \param bridges Pointer to an initialized vector, the
1391
 *    bridges will be stored here as edge indices.
1392
 * \return Error code.
1393
 *
1394
 * Time complexity: O(|V|+|E|), linear in the number of vertices and edges.
1395
 *
1396
 * \sa \ref igraph_articulation_points(), \ref igraph_biconnected_components(),
1397
 * \ref igraph_connected_components()
1398
 */
1399
1400
2.25k
igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridges) {
1401
1402
    /* The algorithm is based on https://www.geeksforgeeks.org/bridge-in-a-graph/
1403
       but instead of keeping track of the parent of each vertex in the DFS tree
1404
       we keep track of its incoming edge. This is necessary to support multigraphs.
1405
       Additionally, we use explicit stacks instead of recursion to avoid
1406
       stack overflow. */
1407
1408
2.25k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1409
2.25k
    igraph_inclist_t il;
1410
2.25k
    igraph_bitset_t visited;
1411
2.25k
    igraph_vector_int_t vis; /* vis[u] time when vertex u was first visited */
1412
2.25k
    igraph_vector_int_t low; /* low[u] is the lowest visit time of vertices reachable from u */
1413
2.25k
    igraph_vector_int_t incoming_edge;
1414
2.25k
    igraph_stack_int_t su, si;
1415
2.25k
    igraph_int_t time;
1416
1417
2.25k
    IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
1418
2.25k
    IGRAPH_FINALLY(igraph_inclist_destroy, &il);
1419
1420
1421
2.25k
    IGRAPH_BITSET_INIT_FINALLY(&visited, no_of_nodes);
1422
2.25k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vis, no_of_nodes);
1423
2.25k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes);
1424
1425
2.25k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&incoming_edge, no_of_nodes);
1426
2.25k
    igraph_vector_int_fill(&incoming_edge, -1);
1427
1428
2.25k
    IGRAPH_STACK_INT_INIT_FINALLY(&su, 0);
1429
2.25k
    IGRAPH_STACK_INT_INIT_FINALLY(&si, 0);
1430
1431
2.25k
    igraph_vector_int_clear(bridges);
1432
1433
2.25k
    time = 0;
1434
389k
    for (igraph_int_t start = 0; start < no_of_nodes; ++start) {
1435
386k
        if (! IGRAPH_BIT_TEST(visited, start)) {
1436
            /* Perform a DFS from 'start'.
1437
             * The top of the su stack is u, the vertex currently being visited.
1438
             * The top of the si stack is i, the index of u's neighbour that will
1439
             * be processed next. */
1440
1441
334k
            IGRAPH_CHECK(igraph_stack_int_push(&su, start));
1442
334k
            IGRAPH_CHECK(igraph_stack_int_push(&si, 0));
1443
1444
886k
            while (! igraph_stack_int_empty(&su)) {
1445
552k
                igraph_int_t u = igraph_stack_int_pop(&su);
1446
552k
                igraph_int_t i = igraph_stack_int_pop(&si);
1447
1448
552k
                if (i == 0) {
1449
                    /* We are at the first step of visiting vertex u. */
1450
1451
386k
                    IGRAPH_BIT_SET(visited, u);
1452
1453
386k
                    time += 1;
1454
1455
386k
                    VECTOR(vis)[u] = time;
1456
386k
                    VECTOR(low)[u] = time;
1457
386k
                }
1458
1459
552k
                igraph_vector_int_t *incedges = igraph_inclist_get(&il, u);
1460
1461
552k
                if (i < igraph_vector_int_size(incedges)) {
1462
165k
                    IGRAPH_CHECK(igraph_stack_int_push(&su, u));
1463
165k
                    IGRAPH_CHECK(igraph_stack_int_push(&si, i+1));
1464
1465
165k
                    igraph_int_t edge = VECTOR(*incedges)[i];
1466
165k
                    igraph_int_t v = IGRAPH_OTHER(graph, edge, u);
1467
1468
165k
                    if (! IGRAPH_BIT_TEST(visited, v)) {
1469
52.4k
                        VECTOR(incoming_edge)[v] = edge;
1470
1471
52.4k
                        IGRAPH_CHECK(igraph_stack_int_push(&su, v));
1472
52.4k
                        IGRAPH_CHECK(igraph_stack_int_push(&si, 0));
1473
112k
                    } else if (edge != VECTOR(incoming_edge)[u]) {
1474
60.5k
                        VECTOR(low)[u] = VECTOR(low)[u] < VECTOR(vis)[v] ? VECTOR(low)[u] : VECTOR(vis)[v];
1475
60.5k
                    }
1476
386k
                } else {
1477
                    /* We are done visiting vertex u, so it won't be put back on the stack.
1478
                     * We are ready to update the 'low' value of its parent w, and decide
1479
                     * whether its incoming edge is a bridge. */
1480
1481
386k
                    igraph_int_t edge = VECTOR(incoming_edge)[u];
1482
386k
                    if (edge >= 0) {
1483
52.4k
                        igraph_int_t w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */
1484
52.4k
                        VECTOR(low)[w] = VECTOR(low)[w] < VECTOR(low)[u] ? VECTOR(low)[w] : VECTOR(low)[u];
1485
52.4k
                        if (VECTOR(low)[u] > VECTOR(vis)[w]) {
1486
43.3k
                            IGRAPH_CHECK(igraph_vector_int_push_back(bridges, edge));
1487
43.3k
                        }
1488
52.4k
                    }
1489
386k
                }
1490
552k
            }
1491
334k
        }
1492
386k
    }
1493
1494
2.25k
    igraph_stack_int_destroy(&si);
1495
2.25k
    igraph_stack_int_destroy(&su);
1496
2.25k
    igraph_vector_int_destroy(&incoming_edge);
1497
2.25k
    igraph_vector_int_destroy(&low);
1498
2.25k
    igraph_vector_int_destroy(&vis);
1499
2.25k
    igraph_bitset_destroy(&visited);
1500
2.25k
    igraph_inclist_destroy(&il);
1501
2.25k
    IGRAPH_FINALLY_CLEAN(7);
1502
1503
2.25k
    return IGRAPH_SUCCESS;
1504
2.25k
}
1505
1506
/**
1507
 * \ingroup structural
1508
 * \function igraph_subcomponent
1509
 * \brief The vertices reachable from a given vertex.
1510
 *
1511
 * This function returns the set of vertices reachable from a specified
1512
 * vertex. In undirected graphs, this is simple the set of vertices within
1513
 * the same component.
1514
 *
1515
 * \param graph The graph object.
1516
 * \param res The result, vector with the IDs of the vertices reachable
1517
 *        from \p vertex.
1518
 * \param vertex The id of the vertex of which the component is
1519
 *        searched.
1520
 * \param mode Type of the component for directed graphs, possible
1521
 *        values:
1522
 *        \clist
1523
 *        \cli IGRAPH_OUT
1524
 *          the set of vertices reachable \em from the
1525
 *          \p vertex,
1526
 *        \cli IGRAPH_IN
1527
 *          the set of vertices from which the
1528
 *          \p vertex is reachable.
1529
 *        \cli IGRAPH_ALL
1530
 *          the graph is considered as an
1531
 *          undirected graph. Note that this is \em not the same
1532
 *          as the union of the previous two.
1533
 *        \endclist
1534
 * \return Error code:
1535
 *        \clist
1536
 *        \cli IGRAPH_ENOMEM
1537
 *          not enough memory for temporary data.
1538
 *        \cli IGRAPH_EINVVID
1539
 *           \p vertex is an invalid vertex ID
1540
 *        \cli IGRAPH_EINVMODE
1541
 *           invalid mode argument passed.
1542
 *        \endclist
1543
 *
1544
 * Time complexity: O(|V|+|E|),
1545
 * |V| and
1546
 * |E| are the number of vertices and
1547
 * edges in the graph.
1548
 *
1549
 * \sa \ref igraph_induced_subgraph() if you want a graph object consisting only
1550
 * a given set of vertices and the edges between them;
1551
 * \ref igraph_reachability() to efficiently compute the reachable set from \em all
1552
 * vertices; \ref igraph_neighborhood() to find vertices within a given distance.
1553
 */
1554
igraph_error_t igraph_subcomponent(
1555
    const igraph_t *graph, igraph_vector_int_t *res, igraph_int_t vertex,
1556
    igraph_neimode_t mode
1557
4.37k
) {
1558
1559
4.37k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1560
4.37k
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
1561
4.37k
    igraph_bitset_t already_added;
1562
4.37k
    igraph_int_t i, vsize;
1563
4.37k
    igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL;
1564
1565
4.37k
    if (vertex < 0 || vertex >= no_of_nodes) {
1566
0
        IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVVID);
1567
0
    }
1568
4.37k
    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
1569
2.25k
        mode != IGRAPH_ALL) {
1570
0
        IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE);
1571
0
    }
1572
1573
4.37k
    igraph_vector_int_clear(res);
1574
1575
4.37k
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
1576
4.37k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0);
1577
4.37k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
1578
1579
4.37k
    IGRAPH_CHECK(igraph_dqueue_int_push(&q, vertex));
1580
4.37k
    IGRAPH_CHECK(igraph_vector_int_push_back(res, vertex));
1581
4.37k
    IGRAPH_BIT_SET(already_added, vertex);
1582
1583
63.2k
    while (!igraph_dqueue_int_empty(&q)) {
1584
58.8k
        igraph_int_t actnode = igraph_dqueue_int_pop(&q);
1585
1586
58.8k
        IGRAPH_ALLOW_INTERRUPTION();
1587
1588
58.8k
        IGRAPH_CHECK(igraph_neighbors(graph, &tmp, actnode, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE));
1589
58.8k
        vsize = igraph_vector_int_size(&tmp);
1590
175k
        for (i = 0; i < vsize; i++) {
1591
117k
            igraph_int_t neighbor = VECTOR(tmp)[i];
1592
1593
117k
            if (IGRAPH_BIT_TEST(already_added, neighbor)) {
1594
62.6k
                continue;
1595
62.6k
            }
1596
54.4k
            IGRAPH_BIT_SET(already_added, neighbor);
1597
54.4k
            IGRAPH_CHECK(igraph_vector_int_push_back(res, neighbor));
1598
54.4k
            IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
1599
54.4k
        }
1600
58.8k
    }
1601
1602
4.37k
    igraph_dqueue_int_destroy(&q);
1603
4.37k
    igraph_vector_int_destroy(&tmp);
1604
4.37k
    igraph_bitset_destroy(&already_added);
1605
4.37k
    IGRAPH_FINALLY_CLEAN(3);
1606
1607
4.37k
    return IGRAPH_SUCCESS;
1608
4.37k
}