Coverage Report

Created: 2025-07-11 06:24

/src/igraph/src/connectivity/components.c
Line
Count
Source (jump to first uncovered line)
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_integer_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_integer_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_integer_t *no, igraph_connectedness_t mode
85
4.19k
) {
86
4.19k
    if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) {
87
4.19k
        return igraph_i_connected_components_weak(graph, membership, csize, no);
88
4.19k
    } else if (mode == IGRAPH_STRONG) {
89
0
        return igraph_i_connected_components_strong(graph, membership, csize, no);
90
0
    }
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_integer_t *no
98
4.19k
) {
99
100
4.19k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
101
4.19k
    igraph_integer_t no_of_components;
102
4.19k
    igraph_bitset_t already_added;
103
4.19k
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
104
4.19k
    igraph_vector_int_t neis = IGRAPH_VECTOR_NULL;
105
106
    /* Memory for result, csize is dynamically allocated */
107
4.19k
    if (membership) {
108
1.67k
        IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes));
109
1.67k
    }
110
4.19k
    if (csize) {
111
4.19k
        igraph_vector_int_clear(csize);
112
4.19k
    }
113
114
    /* Try to make use of cached information. */
115
4.19k
    if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) &&
116
4.19k
        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
351
        if (membership) {
122
            /* All vertices are members of the same component,
123
             * component number 0. */
124
137
            igraph_vector_int_null(membership);
125
137
        }
126
351
        if (csize) {
127
            /* The size of the single component is the same as the vertex count. */
128
351
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, no_of_nodes));
129
351
        }
130
351
        if (no) {
131
            /* There is one component. */
132
137
            *no = 1;
133
137
        }
134
351
        return IGRAPH_SUCCESS;
135
351
    }
136
137
3.84k
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
138
3.84k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10);
139
3.84k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
140
141
    /* The algorithm */
142
143
3.84k
    no_of_components = 0;
144
686k
    for (igraph_integer_t first_node = 0; first_node < no_of_nodes; ++first_node) {
145
682k
        igraph_integer_t act_component_size;
146
147
682k
        if (IGRAPH_BIT_TEST(already_added, first_node)) {
148
74.0k
            continue;
149
74.0k
        }
150
608k
        IGRAPH_ALLOW_INTERRUPTION();
151
152
608k
        IGRAPH_BIT_SET(already_added, first_node);
153
608k
        act_component_size = 1;
154
608k
        if (membership) {
155
247k
            VECTOR(*membership)[first_node] = no_of_components;
156
247k
        }
157
608k
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, first_node));
158
159
1.29M
        while ( !igraph_dqueue_int_empty(&q) ) {
160
682k
            igraph_integer_t act_node = igraph_dqueue_int_pop(&q);
161
682k
            IGRAPH_CHECK(igraph_neighbors(
162
682k
                graph, &neis, act_node, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
163
682k
            ));
164
682k
            igraph_integer_t nei_count = igraph_vector_int_size(&neis);
165
881k
            for (igraph_integer_t i = 0; i < nei_count; i++) {
166
198k
                igraph_integer_t neighbor = VECTOR(neis)[i];
167
198k
                if (IGRAPH_BIT_TEST(already_added, neighbor)) {
168
124k
                    continue;
169
124k
                }
170
74.0k
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
171
74.0k
                IGRAPH_BIT_SET(already_added, neighbor);
172
74.0k
                act_component_size++;
173
74.0k
                if (membership) {
174
33.7k
                    VECTOR(*membership)[neighbor] = no_of_components;
175
33.7k
                }
176
74.0k
            }
177
682k
        }
178
179
608k
        no_of_components++;
180
608k
        if (csize) {
181
608k
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size));
182
608k
        }
183
608k
    }
184
185
    /* Cleaning up */
186
187
3.84k
    if (no) {
188
1.53k
        *no = no_of_components;
189
1.53k
    }
190
191
    /* Clean up */
192
3.84k
    igraph_bitset_destroy(&already_added);
193
3.84k
    igraph_dqueue_int_destroy(&q);
194
3.84k
    igraph_vector_int_destroy(&neis);
195
3.84k
    IGRAPH_FINALLY_CLEAN(3);
196
197
    /* Update cache */
198
3.84k
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, no_of_components == 1);
199
200
3.84k
    return IGRAPH_SUCCESS;
201
3.84k
}
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_integer_t *no
206
0
) {
207
0
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
208
0
    igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL;
209
0
    igraph_integer_t num_seen;
210
0
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
211
0
    igraph_integer_t no_of_components = 0;
212
0
    igraph_vector_int_t out = IGRAPH_VECTOR_NULL;
213
0
    igraph_adjlist_t adjlist;
214
215
    /* Memory for result, csize is dynamically allocated */
216
0
    if (membership) {
217
0
        IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes));
218
0
    }
219
0
    if (csize) {
220
0
        igraph_vector_int_clear(csize);
221
0
    }
222
223
    /* Try to make use of cached information. */
224
0
    if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED) &&
225
0
        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
0
        if (membership) {
231
            /* All vertices are members of the same component,
232
             * component number 0. */
233
0
            igraph_vector_int_null(membership);
234
0
        }
235
0
        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
0
        if (no) {
240
            /* There is one component. */
241
0
            *no = 1;
242
0
        }
243
0
        return IGRAPH_SUCCESS;
244
0
    }
245
246
    /* The result */
247
248
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes);
249
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0);
250
0
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
251
252
0
    IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes));
253
254
0
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
255
0
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
256
257
0
    num_seen = 0;
258
0
    for (igraph_integer_t i = 0; i < no_of_nodes; i++) {
259
0
        const igraph_vector_int_t *tmp;
260
261
0
        IGRAPH_ALLOW_INTERRUPTION();
262
263
0
        tmp = igraph_adjlist_get(&adjlist, i);
264
0
        if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) {
265
0
            continue;
266
0
        }
267
268
0
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, i));
269
0
        while (!igraph_dqueue_int_empty(&q)) {
270
0
            igraph_integer_t act_node = igraph_dqueue_int_back(&q);
271
0
            tmp = igraph_adjlist_get(&adjlist, act_node);
272
0
            if (VECTOR(next_nei)[act_node] == 0) {
273
                /* this is the first time we've met this vertex */
274
0
                VECTOR(next_nei)[act_node]++;
275
0
            } 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
0
                igraph_integer_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1];
278
0
                if (VECTOR(next_nei)[neighbor] == 0) {
279
0
                    IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
280
0
                }
281
0
                VECTOR(next_nei)[act_node]++;
282
0
            } else {
283
                /* we've met this vertex and it has no more children */
284
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node));
285
0
                igraph_dqueue_int_pop_back(&q);
286
0
                num_seen++;
287
288
0
                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
0
            }
295
0
        } /* while q */
296
0
    }  /* for */
297
298
0
    IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL);
299
300
0
    igraph_adjlist_destroy(&adjlist);
301
0
    IGRAPH_FINALLY_CLEAN(1);
302
303
0
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
304
0
    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
0
    igraph_vector_int_null(&next_nei);             /* mark already added vertices */
310
0
    num_seen = 0;
311
312
0
    while (!igraph_vector_int_empty(&out)) {
313
0
        igraph_integer_t act_component_size;
314
0
        igraph_integer_t grandfather = igraph_vector_int_pop_back(&out);
315
316
0
        if (VECTOR(next_nei)[grandfather] != 0) {
317
0
            continue;
318
0
        }
319
0
        VECTOR(next_nei)[grandfather] = 1;
320
0
        act_component_size = 1;
321
0
        if (membership) {
322
0
            VECTOR(*membership)[grandfather] = no_of_components;
323
0
        }
324
0
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather));
325
326
0
        num_seen++;
327
0
        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
0
        while (!igraph_dqueue_int_empty(&q)) {
335
0
            igraph_integer_t act_node = igraph_dqueue_int_pop_back(&q);
336
0
            const igraph_vector_int_t *tmp = igraph_adjlist_get(&adjlist, act_node);
337
0
            const igraph_integer_t n = igraph_vector_int_size(tmp);
338
0
            for (igraph_integer_t i = 0; i < n; i++) {
339
0
                igraph_integer_t neighbor = VECTOR(*tmp)[i];
340
0
                if (VECTOR(next_nei)[neighbor] != 0) {
341
0
                    continue;
342
0
                }
343
0
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
344
0
                VECTOR(next_nei)[neighbor] = 1;
345
0
                act_component_size++;
346
0
                if (membership) {
347
0
                    VECTOR(*membership)[neighbor] = no_of_components;
348
0
                }
349
350
0
                num_seen++;
351
0
                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
0
            }
358
0
        }
359
360
0
        no_of_components++;
361
0
        if (csize) {
362
0
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size));
363
0
        }
364
0
    }
365
366
0
    IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL);
367
368
0
    if (no) {
369
0
        *no = no_of_components;
370
0
    }
371
372
    /* Clean up */
373
0
    igraph_adjlist_destroy(&adjlist);
374
0
    igraph_vector_int_destroy(&out);
375
0
    igraph_dqueue_int_destroy(&q);
376
0
    igraph_vector_int_destroy(&next_nei);
377
0
    IGRAPH_FINALLY_CLEAN(4);
378
379
    /* Update cache */
380
0
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, no_of_components == 1);
381
0
    if (no_of_components == 1) {
382
0
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true);
383
0
    }
384
385
0
    return IGRAPH_SUCCESS;
386
0
}
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
1.37k
                        igraph_connectedness_t mode) {
434
435
1.37k
    igraph_cached_property_t prop;
436
1.37k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
437
1.37k
    igraph_integer_t no;
438
439
1.37k
    if (!igraph_is_directed(graph)) {
440
1.37k
        mode = IGRAPH_WEAK;
441
1.37k
    }
442
443
1.37k
    switch (mode) {
444
1.37k
        case IGRAPH_WEAK:
445
1.37k
            prop = IGRAPH_PROP_IS_WEAKLY_CONNECTED;
446
1.37k
            break;
447
448
0
        case IGRAPH_STRONG:
449
0
            prop = IGRAPH_PROP_IS_STRONGLY_CONNECTED;
450
0
            break;
451
452
0
        default:
453
0
            IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL);
454
1.37k
    }
455
456
1.37k
    IGRAPH_RETURN_IF_CACHED_BOOL(graph, prop, res);
457
458
1.37k
    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
0
        *res = false;
462
1.37k
    } else if (no_of_nodes == 1) {
463
0
        *res = true;
464
1.37k
    } else if (mode == IGRAPH_WEAK) {
465
1.37k
        IGRAPH_CHECK(igraph_i_is_connected_weak(graph, res));
466
1.37k
    } 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
0
        if (igraph_ecount(graph) < no_of_nodes) {
470
0
            *res = false;
471
0
        } else {
472
0
            IGRAPH_CHECK(igraph_i_connected_components_strong(graph, NULL, NULL, &no));
473
0
            *res = (no == 1);
474
0
        }
475
0
    }
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
1.37k
    return IGRAPH_SUCCESS;
482
1.37k
}
483
484
1.37k
static igraph_error_t igraph_i_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) {
485
1.37k
    const igraph_integer_t no_of_nodes = igraph_vcount(graph);
486
1.37k
    const igraph_integer_t no_of_edges = igraph_ecount(graph);
487
1.37k
    igraph_integer_t added_count;
488
1.37k
    igraph_bitset_t already_added;
489
1.37k
    igraph_vector_int_t neis;
490
1.37k
    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
1.37k
    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
1.37k
    if (no_of_edges < no_of_nodes - 1) {
501
1.16k
        *res = false;
502
1.16k
        goto exit;
503
1.16k
    }
504
505
206
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
506
206
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 10);
507
206
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
508
509
    /* Try to find at least two components */
510
206
    IGRAPH_BIT_SET(already_added, 0);
511
206
    IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0));
512
513
206
    added_count = 1;
514
2.49k
    while (! igraph_dqueue_int_empty(&q)) {
515
2.39k
        IGRAPH_ALLOW_INTERRUPTION();
516
517
2.39k
        const igraph_integer_t actnode = igraph_dqueue_int_pop(&q);
518
519
2.39k
        IGRAPH_CHECK(igraph_neighbors(
520
2.39k
            graph, &neis, actnode, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
521
2.39k
        ));
522
2.39k
        const igraph_integer_t nei_count = igraph_vector_int_size(&neis);
523
524
9.89k
        for (igraph_integer_t i = 0; i < nei_count; i++) {
525
7.60k
            const igraph_integer_t neighbor = VECTOR(neis)[i];
526
7.60k
            if (IGRAPH_BIT_TEST(already_added, neighbor)) {
527
5.05k
                continue;
528
5.05k
            }
529
530
2.54k
            IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
531
2.54k
            added_count++;
532
2.54k
            IGRAPH_BIT_SET(already_added, neighbor);
533
534
2.54k
            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
99
                goto done;
538
99
            }
539
2.54k
        }
540
2.39k
    }
541
542
206
done:
543
    /* Connected? */
544
206
    *res = (added_count == no_of_nodes);
545
546
206
    igraph_bitset_destroy(&already_added);
547
206
    igraph_dqueue_int_destroy(&q);
548
206
    igraph_vector_int_destroy(&neis);
549
206
    IGRAPH_FINALLY_CLEAN(3);
550
551
1.37k
exit:
552
1.37k
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, *res);
553
1.37k
    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
1.37k
    return IGRAPH_SUCCESS;
560
206
}
561
562
static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph,
563
                                   igraph_graph_list_t *components,
564
                                   igraph_integer_t maxcompno, igraph_integer_t minelements);
565
566
static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph,
567
                                     igraph_graph_list_t *components,
568
                                     igraph_integer_t maxcompno, igraph_integer_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
1.67k
                     igraph_integer_t maxcompno, igraph_integer_t minelements) {
606
1.67k
    if (!igraph_is_directed(graph)) {
607
1.67k
        mode = IGRAPH_WEAK;
608
1.67k
    }
609
610
1.67k
    switch (mode) {
611
1.67k
    case IGRAPH_WEAK:
612
1.67k
        return igraph_i_decompose_weak(graph, components, maxcompno, minelements);
613
0
    case IGRAPH_STRONG:
614
0
        return igraph_i_decompose_strong(graph, components, maxcompno, minelements);
615
0
    default:
616
0
        IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL);
617
1.67k
    }
618
1.67k
}
619
620
static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph,
621
                                   igraph_graph_list_t *components,
622
1.67k
                                   igraph_integer_t maxcompno, igraph_integer_t minelements) {
623
624
1.67k
    igraph_integer_t actstart;
625
1.67k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
626
1.67k
    igraph_integer_t resco = 0;   /* number of graphs created so far */
627
1.67k
    igraph_bitset_t already_added;
628
1.67k
    igraph_dqueue_int_t q;
629
1.67k
    igraph_vector_int_t verts;
630
1.67k
    igraph_vector_int_t neis;
631
1.67k
    igraph_vector_int_t vids_old2new;
632
1.67k
    igraph_integer_t i;
633
1.67k
    igraph_t newg;
634
635
636
1.67k
    if (maxcompno < 0) {
637
1.67k
        maxcompno = IGRAPH_INTEGER_MAX;
638
1.67k
    }
639
640
1.67k
    igraph_graph_list_clear(components);
641
642
    /* already_added keeps track of what nodes made it into a graph already */
643
1.67k
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
644
1.67k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
645
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0);
646
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
647
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes);
648
1.67k
    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
286k
    for (actstart = 0; resco < maxcompno && actstart < no_of_nodes; actstart++) {
657
658
284k
        if (IGRAPH_BIT_TEST(already_added, actstart)) {
659
36.9k
            continue;
660
36.9k
        }
661
247k
        IGRAPH_ALLOW_INTERRUPTION();
662
663
247k
        igraph_vector_int_clear(&verts);
664
665
        /* add the node itself */
666
247k
        IGRAPH_BIT_SET(already_added, actstart);
667
247k
        IGRAPH_CHECK(igraph_vector_int_push_back(&verts, actstart));
668
247k
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, actstart));
669
670
        /* add the neighbors, recursively */
671
532k
        while (!igraph_dqueue_int_empty(&q) ) {
672
            /* pop from the queue of this component */
673
284k
            igraph_integer_t actvert = igraph_dqueue_int_pop(&q);
674
284k
            IGRAPH_CHECK(igraph_neighbors(
675
284k
                graph, &neis, actvert, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
676
284k
            ));
677
284k
            igraph_integer_t nei_count = igraph_vector_int_size(&neis);
678
            /* iterate over the neighbors */
679
380k
            for (i = 0; i < nei_count; i++) {
680
96.1k
                igraph_integer_t neighbor = VECTOR(neis)[i];
681
96.1k
                if (IGRAPH_BIT_TEST(already_added, neighbor)) {
682
59.2k
                    continue;
683
59.2k
                }
684
                /* add neighbor */
685
36.9k
                IGRAPH_BIT_SET(already_added, neighbor);
686
687
                /* recursion: append neighbor to the queues */
688
36.9k
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
689
36.9k
                IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor));
690
36.9k
            }
691
284k
        }
692
693
        /* ok, we have a component */
694
247k
        if (igraph_vector_int_size(&verts) < minelements) {
695
246k
            continue;
696
246k
        }
697
698
1.71k
        IGRAPH_CHECK(igraph_i_induced_subgraph_map(
699
1.71k
            graph, &newg, igraph_vss_vector(&verts),
700
1.71k
            IGRAPH_SUBGRAPH_AUTO, &vids_old2new,
701
1.71k
            /* invmap = */ NULL, /* map_is_prepared = */ true
702
1.71k
        ));
703
1.71k
        IGRAPH_FINALLY(igraph_destroy, &newg);
704
1.71k
        IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg));
705
1.71k
        IGRAPH_FINALLY_CLEAN(1);  /* ownership of newg now taken by 'components' */
706
1.71k
        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
1.71k
    } /* for actstart++ */
714
715
1.67k
    igraph_vector_int_destroy(&vids_old2new);
716
1.67k
    igraph_vector_int_destroy(&neis);
717
1.67k
    igraph_vector_int_destroy(&verts);
718
1.67k
    igraph_dqueue_int_destroy(&q);
719
1.67k
    igraph_bitset_destroy(&already_added);
720
1.67k
    IGRAPH_FINALLY_CLEAN(5);
721
722
1.67k
    return IGRAPH_SUCCESS;
723
1.67k
}
724
725
static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph,
726
                                     igraph_graph_list_t *components,
727
0
                                     igraph_integer_t maxcompno, igraph_integer_t minelements) {
728
729
730
0
    igraph_integer_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
0
    igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL;
735
736
0
    igraph_integer_t i, n, num_seen;
737
0
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
738
739
0
    igraph_integer_t no_of_components = 0;
740
741
0
    igraph_vector_int_t out = IGRAPH_VECTOR_NULL;
742
0
    const igraph_vector_int_t* tmp;
743
744
0
    igraph_adjlist_t adjlist;
745
0
    igraph_vector_int_t verts;
746
0
    igraph_vector_int_t vids_old2new;
747
0
    igraph_t newg;
748
749
0
    if (maxcompno < 0) {
750
0
        maxcompno = IGRAPH_INTEGER_MAX;
751
0
    }
752
753
0
    igraph_graph_list_clear(components);
754
755
    /* The result */
756
757
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes);
758
0
    igraph_vector_int_fill(&vids_old2new, -1);
759
760
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0);
761
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes);
762
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0);
763
0
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
764
765
0
    IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes));
766
767
0
    igraph_vector_int_null(&out);
768
769
0
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
770
0
    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
0
    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
0
    for (i = 0; i < no_of_nodes; i++) {
782
0
        IGRAPH_ALLOW_INTERRUPTION();
783
784
        /* get all the 'out' neighbors of this node
785
         * NOTE: next_nei is initialized [0, 0, ...] */
786
0
        tmp = igraph_adjlist_get(&adjlist, i);
787
0
        if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) {
788
0
            continue;
789
0
        }
790
791
        /* add this node to the queue for this component */
792
0
        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
0
        while (!igraph_dqueue_int_empty(&q)) {
797
            /* this looks up but does NOT consume the queue */
798
0
            igraph_integer_t act_node = igraph_dqueue_int_back(&q);
799
800
            /* get all neighbors of this node */
801
0
            tmp = igraph_adjlist_get(&adjlist, act_node);
802
0
            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
0
                VECTOR(next_nei)[act_node]++;
806
                /* back to the queue, same vertex is up again */
807
808
0
            } 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
0
                igraph_integer_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1];
811
0
                if (VECTOR(next_nei)[neighbor] == 0) {
812
                    /* add the root of the other children to the queue */
813
0
                    IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
814
0
                }
815
0
                VECTOR(next_nei)[act_node]++;
816
0
            } else {
817
                /* we've met this vertex and it has no more children */
818
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node));
819
                /* this consumes the queue, since there's nowhere to go */
820
0
                igraph_dqueue_int_pop_back(&q);
821
0
                num_seen++;
822
823
0
                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
0
            }
830
0
        } /* while q */
831
0
    }  /* for */
832
833
0
    IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL);
834
835
0
    igraph_adjlist_destroy(&adjlist);
836
0
    IGRAPH_FINALLY_CLEAN(1);
837
838
0
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
839
0
    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
0
    igraph_vector_int_null(&next_nei);             /* mark already added vertices */
845
846
    /* number of components built */
847
0
    num_seen = 0;
848
0
    while (!igraph_vector_int_empty(&out) && no_of_components < maxcompno) {
849
        /* consume the vector from the last element */
850
0
        igraph_integer_t grandfather = igraph_vector_int_pop_back(&out);
851
852
        /* been here, done that
853
         * NOTE: next_nei is initialized as [0, 0, ...] */
854
0
        if (VECTOR(next_nei)[grandfather] != 0) {
855
0
            continue;
856
0
        }
857
858
        /* collect all the members of this component */
859
0
        igraph_vector_int_clear(&verts);
860
861
        /* this node is gone for any future components */
862
0
        VECTOR(next_nei)[grandfather] = 1;
863
864
        /* add to component */
865
0
        IGRAPH_CHECK(igraph_vector_int_push_back(&verts, grandfather));
866
0
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather));
867
868
0
        num_seen++;
869
0
        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
0
        while (!igraph_dqueue_int_empty(&q)) {
877
            /* consume the queue from this node */
878
0
            igraph_integer_t act_node = igraph_dqueue_int_pop_back(&q);
879
0
            tmp = igraph_adjlist_get(&adjlist, act_node);
880
0
            n = igraph_vector_int_size(tmp);
881
0
            for (i = 0; i < n; i++) {
882
0
                igraph_integer_t neighbor = VECTOR(*tmp)[i];
883
0
                if (VECTOR(next_nei)[neighbor] != 0) {
884
0
                    continue;
885
0
                }
886
0
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
887
0
                VECTOR(next_nei)[neighbor] = 1;
888
889
                /* add to component */
890
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor));
891
892
0
                num_seen++;
893
0
                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
0
            }
900
0
        }
901
902
        /* ok, we have a component */
903
0
        if (igraph_vector_int_size(&verts) < minelements) {
904
0
            continue;
905
0
        }
906
907
0
        IGRAPH_CHECK(igraph_i_induced_subgraph_map(
908
0
            graph, &newg, igraph_vss_vector(&verts),
909
0
            IGRAPH_SUBGRAPH_AUTO, &vids_old2new,
910
0
            /* invmap = */ 0, /* map_is_prepared = */ 1
911
0
        ));
912
0
        IGRAPH_FINALLY(igraph_destroy, &newg);
913
0
        IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg));
914
0
        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
0
        n = igraph_vector_int_size(&verts);
922
0
        for (i = 0; i < n; i++) {
923
0
            VECTOR(vids_old2new)[VECTOR(verts)[i]] = 0;
924
0
        }
925
926
0
        no_of_components++;
927
0
    }
928
929
0
    IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL);
930
931
    /* Clean up, return */
932
933
0
    igraph_vector_int_destroy(&vids_old2new);
934
0
    igraph_vector_int_destroy(&verts);
935
0
    igraph_adjlist_destroy(&adjlist);
936
0
    igraph_vector_int_destroy(&out);
937
0
    igraph_dqueue_int_destroy(&q);
938
0
    igraph_vector_int_destroy(&next_nei);
939
0
    IGRAPH_FINALLY_CLEAN(6);
940
941
0
    return IGRAPH_SUCCESS;
942
943
0
}
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
0
igraph_error_t igraph_articulation_points(const igraph_t *graph, igraph_vector_int_t *res) {
970
971
0
    return igraph_biconnected_components(graph, NULL, NULL, NULL, NULL, res);
972
0
}
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_integer_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
1.67k
                                  igraph_vector_int_t *articulation_points) {
1038
1039
1.67k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
1040
1.67k
    igraph_vector_int_t nextptr;
1041
1.67k
    igraph_vector_int_t num, low;
1042
1.67k
    igraph_bitset_t found;
1043
1.67k
    igraph_vector_int_t *adjedges;
1044
1.67k
    igraph_stack_int_t path;
1045
1.67k
    igraph_stack_int_t edgestack;
1046
1.67k
    igraph_inclist_t inclist;
1047
1.67k
    igraph_integer_t counter, rootdfs = 0;
1048
1.67k
    igraph_vector_int_t vertex_added;
1049
1.67k
    igraph_integer_t comps = 0;
1050
1.67k
    igraph_vector_int_list_t *mycomponents = components, vcomponents;
1051
1052
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes);
1053
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes);
1054
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes);
1055
1.67k
    IGRAPH_BITSET_INIT_FINALLY(&found, no_of_nodes);
1056
1057
1.67k
    IGRAPH_STACK_INT_INIT_FINALLY(&path, 100);
1058
1.67k
    IGRAPH_STACK_INT_INIT_FINALLY(&edgestack, 100);
1059
1060
1.67k
    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
1061
1.67k
    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
1062
1063
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_added, no_of_nodes);
1064
1065
1.67k
    if (no) {
1066
1.67k
        *no = 0;
1067
1.67k
    }
1068
1.67k
    if (tree_edges) {
1069
0
        igraph_vector_int_list_clear(tree_edges);
1070
0
    }
1071
1.67k
    if (components) {
1072
1.67k
        igraph_vector_int_list_clear(components);
1073
1.67k
    }
1074
1.67k
    if (component_edges) {
1075
1.67k
        igraph_vector_int_list_clear(component_edges);
1076
1.67k
    }
1077
1.67k
    if (articulation_points) {
1078
1.67k
        igraph_vector_int_clear(articulation_points);
1079
1.67k
    }
1080
1.67k
    if (component_edges && !components) {
1081
0
        mycomponents = &vcomponents;
1082
0
        IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(mycomponents, 0);
1083
0
    }
1084
1085
286k
    for (igraph_integer_t i = 0; i < no_of_nodes; i++) {
1086
1087
284k
        if (VECTOR(low)[i] != 0) {
1088
36.9k
            continue;    /* already visited */
1089
36.9k
        }
1090
1091
247k
        IGRAPH_ALLOW_INTERRUPTION();
1092
1093
247k
        IGRAPH_CHECK(igraph_stack_int_push(&path, i));
1094
247k
        counter = 1;
1095
247k
        rootdfs = 0;
1096
247k
        VECTOR(low)[i] = VECTOR(num)[i] = counter++;
1097
653k
        while (!igraph_stack_int_empty(&path)) {
1098
405k
            igraph_integer_t n;
1099
405k
            igraph_integer_t act = igraph_stack_int_top(&path);
1100
405k
            igraph_integer_t actnext = VECTOR(nextptr)[act];
1101
1102
405k
            adjedges = igraph_inclist_get(&inclist, act);
1103
405k
            n = igraph_vector_int_size(adjedges);
1104
405k
            if (actnext < n) {
1105
                /* Step down (maybe) */
1106
121k
                igraph_integer_t edge = VECTOR(*adjedges)[actnext];
1107
121k
                igraph_integer_t nei = IGRAPH_OTHER(graph, edge, act);
1108
121k
                if (VECTOR(low)[nei] == 0) {
1109
36.9k
                    if (act == i) {
1110
9.12k
                        rootdfs++;
1111
9.12k
                    }
1112
36.9k
                    IGRAPH_CHECK(igraph_stack_int_push(&edgestack, edge));
1113
36.9k
                    IGRAPH_CHECK(igraph_stack_int_push(&path, nei));
1114
36.9k
                    VECTOR(low)[nei] = VECTOR(num)[nei] = counter++;
1115
84.1k
                } else {
1116
                    /* Update low value if needed */
1117
84.1k
                    if (VECTOR(num)[nei] < VECTOR(low)[act]) {
1118
35.6k
                        VECTOR(low)[act] = VECTOR(num)[nei];
1119
35.6k
                    }
1120
84.1k
                }
1121
121k
                VECTOR(nextptr)[act] += 1;
1122
284k
            } else {
1123
                /* Step up */
1124
284k
                igraph_stack_int_pop(&path);
1125
284k
                if (!igraph_stack_int_empty(&path)) {
1126
36.9k
                    igraph_integer_t prev = igraph_stack_int_top(&path);
1127
                    /* Update LOW value if needed */
1128
36.9k
                    if (VECTOR(low)[act] < VECTOR(low)[prev]) {
1129
4.13k
                        VECTOR(low)[prev] = VECTOR(low)[act];
1130
4.13k
                    }
1131
                    /* Check for articulation point */
1132
36.9k
                    if (VECTOR(low)[act] >= VECTOR(num)[prev]) {
1133
31.0k
                        if (articulation_points && !IGRAPH_BIT_TEST(found, prev)
1134
31.0k
                            && prev != i /* the root */) {
1135
9.22k
                            IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, prev));
1136
9.22k
                            IGRAPH_BIT_SET(found, prev);
1137
9.22k
                        }
1138
31.0k
                        if (no) {
1139
31.0k
                            *no += 1;
1140
31.0k
                        }
1141
1142
                        /*------------------------------------*/
1143
                        /* Record the biconnected component just found */
1144
31.0k
                        if (tree_edges || mycomponents) {
1145
31.0k
                            igraph_vector_int_t *v, *v2;
1146
31.0k
                            comps++;
1147
31.0k
                            if (tree_edges) {
1148
0
                                IGRAPH_CHECK(igraph_vector_int_list_push_back_new(tree_edges, &v));
1149
0
                            }
1150
31.0k
                            if (mycomponents) {
1151
31.0k
                                IGRAPH_CHECK(igraph_vector_int_list_push_back_new(mycomponents, &v2));
1152
31.0k
                            }
1153
1154
36.9k
                            while (!igraph_stack_int_empty(&edgestack)) {
1155
36.9k
                                igraph_integer_t e = igraph_stack_int_pop(&edgestack);
1156
36.9k
                                igraph_integer_t from = IGRAPH_FROM(graph, e);
1157
36.9k
                                igraph_integer_t to = IGRAPH_TO(graph, e);
1158
36.9k
                                if (tree_edges) {
1159
0
                                    IGRAPH_CHECK(igraph_vector_int_push_back(v, e));
1160
0
                                }
1161
36.9k
                                if (mycomponents) {
1162
36.9k
                                    if (VECTOR(vertex_added)[from] != comps) {
1163
33.9k
                                        VECTOR(vertex_added)[from] = comps;
1164
33.9k
                                        IGRAPH_CHECK(igraph_vector_int_push_back(v2, from));
1165
33.9k
                                    }
1166
36.9k
                                    if (VECTOR(vertex_added)[to] != comps) {
1167
34.0k
                                        VECTOR(vertex_added)[to] = comps;
1168
34.0k
                                        IGRAPH_CHECK(igraph_vector_int_push_back(v2, to));
1169
34.0k
                                    }
1170
36.9k
                                }
1171
36.9k
                                if (from == prev || to == prev) {
1172
31.0k
                                    break;
1173
31.0k
                                }
1174
36.9k
                            }
1175
1176
31.0k
                            if (component_edges) {
1177
31.0k
                                igraph_vector_int_t *nodes = igraph_vector_int_list_get_ptr(mycomponents, comps - 1);
1178
31.0k
                                igraph_integer_t ii, no_vert = igraph_vector_int_size(nodes);
1179
31.0k
                                igraph_vector_int_t *vv;
1180
1181
31.0k
                                IGRAPH_CHECK(igraph_vector_int_list_push_back_new(component_edges, &vv));
1182
99.0k
                                for (ii = 0; ii < no_vert; ii++) {
1183
68.0k
                                    igraph_integer_t vert = VECTOR(*nodes)[ii];
1184
68.0k
                                    igraph_vector_int_t *edges = igraph_inclist_get(&inclist,
1185
68.0k
                                                                 vert);
1186
68.0k
                                    igraph_integer_t j, nn = igraph_vector_int_size(edges);
1187
1.26M
                                    for (j = 0; j < nn; j++) {
1188
1.19M
                                        igraph_integer_t e = VECTOR(*edges)[j];
1189
1.19M
                                        igraph_integer_t nei = IGRAPH_OTHER(graph, e, vert);
1190
1.19M
                                        if (VECTOR(vertex_added)[nei] == comps && nei < vert) {
1191
48.0k
                                            IGRAPH_CHECK(igraph_vector_int_push_back(vv, e));
1192
48.0k
                                        }
1193
1.19M
                                    }
1194
68.0k
                                }
1195
31.0k
                            }
1196
31.0k
                        } /* record component if requested */
1197
                        /*------------------------------------*/
1198
1199
31.0k
                    }
1200
36.9k
                } /* !igraph_stack_int_empty(&path) */
1201
284k
            }
1202
1203
405k
        } /* !igraph_stack_int_empty(&path) */
1204
1205
247k
        if (articulation_points && rootdfs >= 2) {
1206
1.02k
            IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, i));
1207
1.02k
        }
1208
1209
247k
    } /* i < no_of_nodes */
1210
1211
1.67k
    if (mycomponents != components) {
1212
0
        igraph_vector_int_list_destroy(mycomponents);
1213
0
        IGRAPH_FINALLY_CLEAN(1);
1214
0
    }
1215
1216
1.67k
    igraph_vector_int_destroy(&vertex_added);
1217
1.67k
    igraph_inclist_destroy(&inclist);
1218
1.67k
    igraph_stack_int_destroy(&edgestack);
1219
1.67k
    igraph_stack_int_destroy(&path);
1220
1.67k
    igraph_bitset_destroy(&found);
1221
1.67k
    igraph_vector_int_destroy(&low);
1222
1.67k
    igraph_vector_int_destroy(&num);
1223
1.67k
    igraph_vector_int_destroy(&nextptr);
1224
1.67k
    IGRAPH_FINALLY_CLEAN(8);
1225
1226
1.67k
    return IGRAPH_SUCCESS;
1227
1.67k
}
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 result 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
0
igraph_error_t igraph_is_biconnected(const igraph_t *graph, igraph_bool_t *res) {
1255
1256
0
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
1257
0
    igraph_vector_int_t nextptr;
1258
0
    igraph_vector_int_t num, low;
1259
0
    igraph_stack_int_t path;
1260
0
    igraph_lazy_adjlist_t inclist;
1261
0
    igraph_bool_t is_biconnected = true;
1262
1263
0
    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
0
        is_biconnected = false;
1267
0
        goto exit2;
1268
0
    }
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
0
    if (no_of_nodes >= 3 && (
1277
0
        (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
0
        (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) &&
1280
0
        igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST))
1281
0
    )) {
1282
0
        is_biconnected = false;
1283
0
        goto exit2;
1284
0
    }
1285
1286
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes);
1287
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes);
1288
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes);
1289
1290
0
    IGRAPH_STACK_INT_INIT_FINALLY(&path, 100);
1291
1292
0
    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE));
1293
0
    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inclist);
1294
1295
0
    const igraph_integer_t root = 0; /* start DFS from vertex 0 */
1296
0
    igraph_integer_t counter = 1;
1297
0
    igraph_integer_t rootdfs = 0;
1298
0
    IGRAPH_CHECK(igraph_stack_int_push(&path, root));
1299
0
    VECTOR(low)[root] = VECTOR(num)[root] = counter++;
1300
0
    while (!igraph_stack_int_empty(&path)) {
1301
0
        igraph_integer_t act = igraph_stack_int_top(&path);
1302
0
        igraph_integer_t actnext = VECTOR(nextptr)[act];
1303
1304
0
        const igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&inclist, act);
1305
0
        const igraph_integer_t n = igraph_vector_int_size(neis);
1306
0
        if (actnext < n) {
1307
            /* Step down (maybe) */
1308
0
            igraph_integer_t nei = VECTOR(*neis)[actnext];
1309
0
            if (VECTOR(low)[nei] == 0) {
1310
0
                if (act == root) {
1311
0
                    rootdfs++;
1312
0
                }
1313
0
                IGRAPH_CHECK(igraph_stack_int_push(&path, nei));
1314
0
                VECTOR(low)[nei] = VECTOR(num)[nei] = counter++;
1315
0
            } else {
1316
                /* Update low value if needed */
1317
0
                if (VECTOR(num)[nei] < VECTOR(low)[act]) {
1318
0
                    VECTOR(low)[act] = VECTOR(num)[nei];
1319
0
                }
1320
0
            }
1321
0
            VECTOR(nextptr)[act] += 1;
1322
0
        } else {
1323
            /* Step up */
1324
0
            igraph_stack_int_pop(&path);
1325
0
            if (!igraph_stack_int_empty(&path)) {
1326
0
                igraph_integer_t prev = igraph_stack_int_top(&path);
1327
                /* Update LOW value if needed */
1328
0
                if (VECTOR(low)[act] < VECTOR(low)[prev]) {
1329
0
                    VECTOR(low)[prev] = VECTOR(low)[act];
1330
0
                }
1331
                /* Check for articulation point */
1332
0
                if (VECTOR(low)[act] >= VECTOR(num)[prev]) {
1333
0
                    if (prev != root /* the root */) {
1334
                        /* Found an articulation point, the graph is not biconnected */
1335
0
                        is_biconnected = false;
1336
0
                        goto exit;
1337
0
                    }
1338
0
                }
1339
0
            } /* !igraph_stack_int_empty(&path) */
1340
0
        }
1341
1342
0
    } /* !igraph_stack_int_empty(&path) */
1343
1344
    /* The root is an articulation point, the graph is not biconnected */
1345
0
    if (rootdfs >= 2) {
1346
0
        is_biconnected = false;
1347
0
        goto exit;
1348
0
    }
1349
1350
    /* We did not reach all vertices, the graph is not connected */
1351
0
    if (counter <= no_of_nodes) {
1352
0
        is_biconnected = false;
1353
0
        goto exit;
1354
0
    }
1355
1356
0
exit:
1357
1358
0
    igraph_lazy_adjlist_destroy(&inclist);
1359
0
    igraph_stack_int_destroy(&path);
1360
0
    igraph_vector_int_destroy(&low);
1361
0
    igraph_vector_int_destroy(&num);
1362
0
    igraph_vector_int_destroy(&nextptr);
1363
0
    IGRAPH_FINALLY_CLEAN(5);
1364
1365
0
exit2:
1366
1367
0
    if (res) {
1368
0
        *res = is_biconnected;
1369
0
    }
1370
1371
    /* We do not touch the cache for graphs with less than three nodes because
1372
     * of all the edge cases. */
1373
0
    if (is_biconnected && no_of_nodes >= 3) {
1374
0
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true);
1375
0
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, false);
1376
0
    }
1377
1378
0
    return IGRAPH_SUCCESS;
1379
0
}
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 res 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
1.67k
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
1.67k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
1409
1.67k
    igraph_inclist_t il;
1410
1.67k
    igraph_bitset_t visited;
1411
1.67k
    igraph_vector_int_t vis; /* vis[u] time when vertex u was first visited */
1412
1.67k
    igraph_vector_int_t low; /* low[u] is the lowest visit time of vertices reachable from u */
1413
1.67k
    igraph_vector_int_t incoming_edge;
1414
1.67k
    igraph_stack_int_t su, si;
1415
1.67k
    igraph_integer_t time;
1416
1417
1.67k
    IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
1418
1.67k
    IGRAPH_FINALLY(igraph_inclist_destroy, &il);
1419
1420
1421
1.67k
    IGRAPH_BITSET_INIT_FINALLY(&visited, no_of_nodes);
1422
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vis, no_of_nodes);
1423
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes);
1424
1425
1.67k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&incoming_edge, no_of_nodes);
1426
1.67k
    igraph_vector_int_fill(&incoming_edge, -1);
1427
1428
1.67k
    IGRAPH_STACK_INT_INIT_FINALLY(&su, 0);
1429
1.67k
    IGRAPH_STACK_INT_INIT_FINALLY(&si, 0);
1430
1431
1.67k
    igraph_vector_int_clear(bridges);
1432
1433
1.67k
    time = 0;
1434
286k
    for (igraph_integer_t start = 0; start < no_of_nodes; ++start) {
1435
284k
        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
247k
            IGRAPH_CHECK(igraph_stack_int_push(&su, start));
1442
247k
            IGRAPH_CHECK(igraph_stack_int_push(&si, 0));
1443
1444
653k
            while (! igraph_stack_int_empty(&su)) {
1445
405k
                igraph_integer_t u = igraph_stack_int_pop(&su);
1446
405k
                igraph_integer_t i = igraph_stack_int_pop(&si);
1447
1448
405k
                if (i == 0) {
1449
                    /* We are at the first step of visiting vertex u. */
1450
1451
284k
                    IGRAPH_BIT_SET(visited, u);
1452
1453
284k
                    time += 1;
1454
1455
284k
                    VECTOR(vis)[u] = time;
1456
284k
                    VECTOR(low)[u] = time;
1457
284k
                }
1458
1459
405k
                igraph_vector_int_t *incedges = igraph_inclist_get(&il, u);
1460
1461
405k
                if (i < igraph_vector_int_size(incedges)) {
1462
121k
                    IGRAPH_CHECK(igraph_stack_int_push(&su, u));
1463
121k
                    IGRAPH_CHECK(igraph_stack_int_push(&si, i+1));
1464
1465
121k
                    igraph_integer_t edge = VECTOR(*incedges)[i];
1466
121k
                    igraph_integer_t v = IGRAPH_OTHER(graph, edge, u);
1467
1468
121k
                    if (! IGRAPH_BIT_TEST(visited, v)) {
1469
36.9k
                        VECTOR(incoming_edge)[v] = edge;
1470
1471
36.9k
                        IGRAPH_CHECK(igraph_stack_int_push(&su, v));
1472
36.9k
                        IGRAPH_CHECK(igraph_stack_int_push(&si, 0));
1473
84.1k
                    } else if (edge != VECTOR(incoming_edge)[u]) {
1474
47.2k
                        VECTOR(low)[u] = VECTOR(low)[u] < VECTOR(vis)[v] ? VECTOR(low)[u] : VECTOR(vis)[v];
1475
47.2k
                    }
1476
284k
                } 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
284k
                    igraph_integer_t edge = VECTOR(incoming_edge)[u];
1482
284k
                    if (edge >= 0) {
1483
36.9k
                        igraph_integer_t w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */
1484
36.9k
                        VECTOR(low)[w] = VECTOR(low)[w] < VECTOR(low)[u] ? VECTOR(low)[w] : VECTOR(low)[u];
1485
36.9k
                        if (VECTOR(low)[u] > VECTOR(vis)[w]) {
1486
29.7k
                            IGRAPH_CHECK(igraph_vector_int_push_back(bridges, edge));
1487
29.7k
                        }
1488
36.9k
                    }
1489
284k
                }
1490
405k
            }
1491
247k
        }
1492
284k
    }
1493
1494
1.67k
    igraph_stack_int_destroy(&si);
1495
1.67k
    igraph_stack_int_destroy(&su);
1496
1.67k
    igraph_vector_int_destroy(&incoming_edge);
1497
1.67k
    igraph_vector_int_destroy(&low);
1498
1.67k
    igraph_vector_int_destroy(&vis);
1499
1.67k
    igraph_bitset_destroy(&visited);
1500
1.67k
    igraph_inclist_destroy(&il);
1501
1.67k
    IGRAPH_FINALLY_CLEAN(7);
1502
1503
1.67k
    return IGRAPH_SUCCESS;
1504
1.67k
}
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_integer_t vertex,
1556
    igraph_neimode_t mode
1557
1.66k
) {
1558
1559
1.66k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
1560
1.66k
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
1561
1.66k
    igraph_bitset_t already_added;
1562
1.66k
    igraph_integer_t i, vsize;
1563
1.66k
    igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL;
1564
1565
1.66k
    if (vertex < 0 || vertex >= no_of_nodes) {
1566
0
        IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVVID);
1567
0
    }
1568
1.66k
    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
1569
1.66k
        mode != IGRAPH_ALL) {
1570
0
        IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE);
1571
0
    }
1572
1573
1.66k
    igraph_vector_int_clear(res);
1574
1575
1.66k
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
1576
1.66k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0);
1577
1.66k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
1578
1579
1.66k
    IGRAPH_CHECK(igraph_dqueue_int_push(&q, vertex));
1580
1.66k
    IGRAPH_CHECK(igraph_vector_int_push_back(res, vertex));
1581
1.66k
    IGRAPH_BIT_SET(already_added, vertex);
1582
1583
26.9k
    while (!igraph_dqueue_int_empty(&q)) {
1584
25.2k
        igraph_integer_t actnode = igraph_dqueue_int_pop(&q);
1585
1586
25.2k
        IGRAPH_ALLOW_INTERRUPTION();
1587
1588
25.2k
        IGRAPH_CHECK(igraph_neighbors(graph, &tmp, actnode, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE));
1589
25.2k
        vsize = igraph_vector_int_size(&tmp);
1590
87.4k
        for (i = 0; i < vsize; i++) {
1591
62.1k
            igraph_integer_t neighbor = VECTOR(tmp)[i];
1592
1593
62.1k
            if (IGRAPH_BIT_TEST(already_added, neighbor)) {
1594
38.5k
                continue;
1595
38.5k
            }
1596
23.5k
            IGRAPH_BIT_SET(already_added, neighbor);
1597
23.5k
            IGRAPH_CHECK(igraph_vector_int_push_back(res, neighbor));
1598
23.5k
            IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
1599
23.5k
        }
1600
25.2k
    }
1601
1602
1.66k
    igraph_dqueue_int_destroy(&q);
1603
1.66k
    igraph_vector_int_destroy(&tmp);
1604
1.66k
    igraph_bitset_destroy(&already_added);
1605
1.66k
    IGRAPH_FINALLY_CLEAN(3);
1606
1607
1.66k
    return IGRAPH_SUCCESS;
1608
1.66k
}