Coverage Report

Created: 2026-01-05 06:05

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
643
) {
86
643
    if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) {
87
643
        return igraph_i_connected_components_weak(graph, membership, csize, no);
88
643
    } 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_int_t *no
98
643
) {
99
100
643
    igraph_int_t no_of_nodes = igraph_vcount(graph);
101
643
    igraph_int_t no_of_components;
102
643
    igraph_bitset_t already_added;
103
643
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
104
643
    igraph_vector_int_t neis = IGRAPH_VECTOR_NULL;
105
106
    /* Memory for result, csize is dynamically allocated */
107
643
    if (membership) {
108
643
        IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes));
109
643
    }
110
643
    if (csize) {
111
643
        igraph_vector_int_clear(csize);
112
643
    }
113
114
    /* Try to make use of cached information. */
115
643
    if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) &&
116
643
        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
643
        if (membership) {
122
            /* All vertices are members of the same component,
123
             * component number 0. */
124
643
            igraph_vector_int_null(membership);
125
643
        }
126
643
        if (csize) {
127
            /* The size of the single component is the same as the vertex count. */
128
643
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, no_of_nodes));
129
643
        }
130
643
        if (no) {
131
            /* There is one component. */
132
643
            *no = 1;
133
643
        }
134
643
        return IGRAPH_SUCCESS;
135
643
    }
136
137
0
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
138
0
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10);
139
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
140
141
    /* The algorithm */
142
143
0
    no_of_components = 0;
144
0
    for (igraph_int_t first_node = 0; first_node < no_of_nodes; ++first_node) {
145
0
        igraph_int_t act_component_size;
146
147
0
        if (IGRAPH_BIT_TEST(already_added, first_node)) {
148
0
            continue;
149
0
        }
150
0
        IGRAPH_ALLOW_INTERRUPTION();
151
152
0
        IGRAPH_BIT_SET(already_added, first_node);
153
0
        act_component_size = 1;
154
0
        if (membership) {
155
0
            VECTOR(*membership)[first_node] = no_of_components;
156
0
        }
157
0
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, first_node));
158
159
0
        while ( !igraph_dqueue_int_empty(&q) ) {
160
0
            igraph_int_t act_node = igraph_dqueue_int_pop(&q);
161
0
            IGRAPH_CHECK(igraph_neighbors(
162
0
                graph, &neis, act_node, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
163
0
            ));
164
0
            igraph_int_t nei_count = igraph_vector_int_size(&neis);
165
0
            for (igraph_int_t i = 0; i < nei_count; i++) {
166
0
                igraph_int_t neighbor = VECTOR(neis)[i];
167
0
                if (IGRAPH_BIT_TEST(already_added, neighbor)) {
168
0
                    continue;
169
0
                }
170
0
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
171
0
                IGRAPH_BIT_SET(already_added, neighbor);
172
0
                act_component_size++;
173
0
                if (membership) {
174
0
                    VECTOR(*membership)[neighbor] = no_of_components;
175
0
                }
176
0
            }
177
0
        }
178
179
0
        no_of_components++;
180
0
        if (csize) {
181
0
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size));
182
0
        }
183
0
    }
184
185
    /* Cleaning up */
186
187
0
    if (no) {
188
0
        *no = no_of_components;
189
0
    }
190
191
    /* Clean up */
192
0
    igraph_bitset_destroy(&already_added);
193
0
    igraph_dqueue_int_destroy(&q);
194
0
    igraph_vector_int_destroy(&neis);
195
0
    IGRAPH_FINALLY_CLEAN(3);
196
197
    /* Update cache */
198
0
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, no_of_components == 1);
199
200
0
    return IGRAPH_SUCCESS;
201
0
}
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
876
) {
207
876
    igraph_int_t no_of_nodes = igraph_vcount(graph);
208
876
    igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL;
209
876
    igraph_int_t num_seen;
210
876
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
211
876
    igraph_int_t no_of_components = 0;
212
876
    igraph_vector_int_t out = IGRAPH_VECTOR_NULL;
213
876
    igraph_adjlist_t adjlist;
214
215
    /* Memory for result, csize is dynamically allocated */
216
876
    if (membership) {
217
0
        IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes));
218
0
    }
219
876
    if (csize) {
220
0
        igraph_vector_int_clear(csize);
221
0
    }
222
223
    /* Try to make use of cached information. */
224
876
    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
876
    IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes);
249
876
    IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0);
250
876
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
251
252
876
    IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes));
253
254
876
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
255
876
    IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
256
257
876
    num_seen = 0;
258
57.8k
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
259
56.9k
        const igraph_vector_int_t *tmp;
260
261
56.9k
        IGRAPH_ALLOW_INTERRUPTION();
262
263
56.9k
        tmp = igraph_adjlist_get(&adjlist, i);
264
56.9k
        if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) {
265
41.4k
            continue;
266
41.4k
        }
267
268
15.5k
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, i));
269
1.56M
        while (!igraph_dqueue_int_empty(&q)) {
270
1.54M
            igraph_int_t act_node = igraph_dqueue_int_back(&q);
271
1.54M
            tmp = igraph_adjlist_get(&adjlist, act_node);
272
1.54M
            if (VECTOR(next_nei)[act_node] == 0) {
273
                /* this is the first time we've met this vertex */
274
56.9k
                VECTOR(next_nei)[act_node]++;
275
1.48M
            } 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
1.43M
                igraph_int_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1];
278
1.43M
                if (VECTOR(next_nei)[neighbor] == 0) {
279
41.4k
                    IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
280
41.4k
                }
281
1.43M
                VECTOR(next_nei)[act_node]++;
282
1.43M
            } else {
283
                /* we've met this vertex and it has no more children */
284
56.9k
                IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node));
285
56.9k
                igraph_dqueue_int_pop_back(&q);
286
56.9k
                num_seen++;
287
288
56.9k
                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
56.9k
            }
295
1.54M
        } /* while q */
296
15.5k
    }  /* for */
297
298
876
    IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL);
299
300
876
    igraph_adjlist_destroy(&adjlist);
301
876
    IGRAPH_FINALLY_CLEAN(1);
302
303
876
    IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE));
304
876
    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
876
    igraph_vector_int_null(&next_nei);             /* mark already added vertices */
310
876
    num_seen = 0;
311
312
57.8k
    while (!igraph_vector_int_empty(&out)) {
313
56.9k
        igraph_int_t act_component_size;
314
56.9k
        igraph_int_t grandfather = igraph_vector_int_pop_back(&out);
315
316
56.9k
        if (VECTOR(next_nei)[grandfather] != 0) {
317
35.5k
            continue;
318
35.5k
        }
319
21.4k
        VECTOR(next_nei)[grandfather] = 1;
320
21.4k
        act_component_size = 1;
321
21.4k
        if (membership) {
322
0
            VECTOR(*membership)[grandfather] = no_of_components;
323
0
        }
324
21.4k
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather));
325
326
21.4k
        num_seen++;
327
21.4k
        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
78.4k
        while (!igraph_dqueue_int_empty(&q)) {
335
56.9k
            igraph_int_t act_node = igraph_dqueue_int_pop_back(&q);
336
56.9k
            const igraph_vector_int_t *tmp = igraph_adjlist_get(&adjlist, act_node);
337
56.9k
            const igraph_int_t n = igraph_vector_int_size(tmp);
338
1.48M
            for (igraph_int_t i = 0; i < n; i++) {
339
1.43M
                igraph_int_t neighbor = VECTOR(*tmp)[i];
340
1.43M
                if (VECTOR(next_nei)[neighbor] != 0) {
341
1.39M
                    continue;
342
1.39M
                }
343
35.5k
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
344
35.5k
                VECTOR(next_nei)[neighbor] = 1;
345
35.5k
                act_component_size++;
346
35.5k
                if (membership) {
347
0
                    VECTOR(*membership)[neighbor] = no_of_components;
348
0
                }
349
350
35.5k
                num_seen++;
351
35.5k
                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
35.5k
            }
358
56.9k
        }
359
360
21.4k
        no_of_components++;
361
21.4k
        if (csize) {
362
0
            IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size));
363
0
        }
364
21.4k
    }
365
366
876
    IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL);
367
368
876
    if (no) {
369
876
        *no = no_of_components;
370
876
    }
371
372
    /* Clean up */
373
876
    igraph_adjlist_destroy(&adjlist);
374
876
    igraph_vector_int_destroy(&out);
375
876
    igraph_dqueue_int_destroy(&q);
376
876
    igraph_vector_int_destroy(&next_nei);
377
876
    IGRAPH_FINALLY_CLEAN(4);
378
379
    /* Update cache */
380
876
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, no_of_components == 1);
381
876
    if (no_of_components == 1) {
382
471
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true);
383
471
    }
384
385
876
    return IGRAPH_SUCCESS;
386
876
}
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
2.15k
                        igraph_connectedness_t mode) {
434
435
2.15k
    igraph_cached_property_t prop;
436
2.15k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
437
2.15k
    igraph_int_t no;
438
439
2.15k
    if (!igraph_is_directed(graph)) {
440
1.07k
        mode = IGRAPH_WEAK;
441
1.07k
    }
442
443
2.15k
    switch (mode) {
444
1.07k
        case IGRAPH_WEAK:
445
1.07k
            prop = IGRAPH_PROP_IS_WEAKLY_CONNECTED;
446
1.07k
            break;
447
448
1.07k
        case IGRAPH_STRONG:
449
1.07k
            prop = IGRAPH_PROP_IS_STRONGLY_CONNECTED;
450
1.07k
            break;
451
452
0
        default:
453
0
            IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL);
454
2.15k
    }
455
456
2.15k
    IGRAPH_RETURN_IF_CACHED_BOOL(graph, prop, res);
457
458
2.15k
    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
2.15k
    } else if (no_of_nodes == 1) {
463
0
        *res = true;
464
2.15k
    } else if (mode == IGRAPH_WEAK) {
465
1.07k
        IGRAPH_CHECK(igraph_i_is_connected_weak(graph, res));
466
1.07k
    } 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
1.07k
        if (igraph_ecount(graph) < no_of_nodes) {
470
203
            *res = false;
471
876
        } else {
472
876
            IGRAPH_CHECK(igraph_i_connected_components_strong(graph, NULL, NULL, &no));
473
876
            *res = (no == 1);
474
876
        }
475
1.07k
    }
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
2.15k
    return IGRAPH_SUCCESS;
482
2.15k
}
483
484
1.07k
static igraph_error_t igraph_i_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) {
485
1.07k
    const igraph_int_t no_of_nodes = igraph_vcount(graph);
486
1.07k
    const igraph_int_t no_of_edges = igraph_ecount(graph);
487
1.07k
    igraph_int_t added_count;
488
1.07k
    igraph_bitset_t already_added;
489
1.07k
    igraph_vector_int_t neis;
490
1.07k
    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.07k
    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.07k
    if (no_of_edges < no_of_nodes - 1) {
501
256
        *res = false;
502
256
        goto exit;
503
256
    }
504
505
823
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
506
823
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 10);
507
823
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
508
509
    /* Try to find at least two components */
510
823
    IGRAPH_BIT_SET(already_added, 0);
511
823
    IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0));
512
513
823
    added_count = 1;
514
28.2k
    while (! igraph_dqueue_int_empty(&q)) {
515
28.1k
        IGRAPH_ALLOW_INTERRUPTION();
516
517
28.1k
        const igraph_int_t actnode = igraph_dqueue_int_pop(&q);
518
519
28.1k
        IGRAPH_CHECK(igraph_neighbors(
520
28.1k
            graph, &neis, actnode, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
521
28.1k
        ));
522
28.1k
        const igraph_int_t nei_count = igraph_vector_int_size(&neis);
523
524
199k
        for (igraph_int_t i = 0; i < nei_count; i++) {
525
172k
            const igraph_int_t neighbor = VECTOR(neis)[i];
526
172k
            if (IGRAPH_BIT_TEST(already_added, neighbor)) {
527
126k
                continue;
528
126k
            }
529
530
46.0k
            IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
531
46.0k
            added_count++;
532
46.0k
            IGRAPH_BIT_SET(already_added, neighbor);
533
534
46.0k
            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
721
                goto done;
538
721
            }
539
46.0k
        }
540
28.1k
    }
541
542
823
done:
543
    /* Connected? */
544
823
    *res = (added_count == no_of_nodes);
545
546
823
    igraph_bitset_destroy(&already_added);
547
823
    igraph_dqueue_int_destroy(&q);
548
823
    igraph_vector_int_destroy(&neis);
549
823
    IGRAPH_FINALLY_CLEAN(3);
550
551
1.07k
exit:
552
1.07k
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, *res);
553
1.07k
    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.07k
    return IGRAPH_SUCCESS;
560
823
}
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
0
                     igraph_int_t maxcompno, igraph_int_t minelements) {
606
0
    if (!igraph_is_directed(graph)) {
607
0
        mode = IGRAPH_WEAK;
608
0
    }
609
610
0
    switch (mode) {
611
0
    case IGRAPH_WEAK:
612
0
        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
0
    }
618
0
}
619
620
static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph,
621
                                   igraph_graph_list_t *components,
622
0
                                   igraph_int_t maxcompno, igraph_int_t minelements) {
623
624
0
    igraph_int_t actstart;
625
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
626
0
    igraph_int_t resco = 0;   /* number of graphs created so far */
627
0
    igraph_bitset_t already_added;
628
0
    igraph_dqueue_int_t q;
629
0
    igraph_vector_int_t verts;
630
0
    igraph_vector_int_t neis;
631
0
    igraph_vector_int_t vids_old2new;
632
0
    igraph_int_t i;
633
0
    igraph_t newg;
634
635
636
0
    if (maxcompno < 0) {
637
0
        maxcompno = IGRAPH_INTEGER_MAX;
638
0
    }
639
640
0
    igraph_graph_list_clear(components);
641
642
    /* already_added keeps track of what nodes made it into a graph already */
643
0
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
644
0
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
645
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0);
646
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
647
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes);
648
0
    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
0
    for (actstart = 0; resco < maxcompno && actstart < no_of_nodes; actstart++) {
657
658
0
        if (IGRAPH_BIT_TEST(already_added, actstart)) {
659
0
            continue;
660
0
        }
661
0
        IGRAPH_ALLOW_INTERRUPTION();
662
663
0
        igraph_vector_int_clear(&verts);
664
665
        /* add the node itself */
666
0
        IGRAPH_BIT_SET(already_added, actstart);
667
0
        IGRAPH_CHECK(igraph_vector_int_push_back(&verts, actstart));
668
0
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, actstart));
669
670
        /* add the neighbors, recursively */
671
0
        while (!igraph_dqueue_int_empty(&q) ) {
672
            /* pop from the queue of this component */
673
0
            igraph_int_t actvert = igraph_dqueue_int_pop(&q);
674
0
            IGRAPH_CHECK(igraph_neighbors(
675
0
                graph, &neis, actvert, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE
676
0
            ));
677
0
            igraph_int_t nei_count = igraph_vector_int_size(&neis);
678
            /* iterate over the neighbors */
679
0
            for (i = 0; i < nei_count; i++) {
680
0
                igraph_int_t neighbor = VECTOR(neis)[i];
681
0
                if (IGRAPH_BIT_TEST(already_added, neighbor)) {
682
0
                    continue;
683
0
                }
684
                /* add neighbor */
685
0
                IGRAPH_BIT_SET(already_added, neighbor);
686
687
                /* recursion: append neighbor to the queues */
688
0
                IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
689
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor));
690
0
            }
691
0
        }
692
693
        /* ok, we have a component */
694
0
        if (igraph_vector_int_size(&verts) < minelements) {
695
0
            continue;
696
0
        }
697
698
0
        IGRAPH_CHECK(igraph_i_induced_subgraph_map(
699
0
            graph, &newg, igraph_vss_vector(&verts),
700
0
            IGRAPH_SUBGRAPH_AUTO, &vids_old2new,
701
0
            /* invmap = */ NULL, /* map_is_prepared = */ true
702
0
        ));
703
0
        IGRAPH_FINALLY(igraph_destroy, &newg);
704
0
        IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg));
705
0
        IGRAPH_FINALLY_CLEAN(1);  /* ownership of newg now taken by 'components' */
706
0
        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
0
    } /* for actstart++ */
714
715
0
    igraph_vector_int_destroy(&vids_old2new);
716
0
    igraph_vector_int_destroy(&neis);
717
0
    igraph_vector_int_destroy(&verts);
718
0
    igraph_dqueue_int_destroy(&q);
719
0
    igraph_bitset_destroy(&already_added);
720
0
    IGRAPH_FINALLY_CLEAN(5);
721
722
0
    return IGRAPH_SUCCESS;
723
0
}
724
725
static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph,
726
                                     igraph_graph_list_t *components,
727
0
                                     igraph_int_t maxcompno, igraph_int_t minelements) {
728
729
730
0
    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
0
    igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL;
735
736
0
    igraph_int_t i, n, num_seen;
737
0
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
738
739
0
    igraph_int_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_int_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_int_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_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
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_int_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_int_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_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
0
                                  igraph_vector_int_t *articulation_points) {
1038
1039
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1040
0
    igraph_vector_int_t nextptr;
1041
0
    igraph_vector_int_t num, low;
1042
0
    igraph_bitset_t found;
1043
0
    igraph_vector_int_t *adjedges;
1044
0
    igraph_stack_int_t path;
1045
0
    igraph_stack_int_t edgestack;
1046
0
    igraph_inclist_t inclist;
1047
0
    igraph_int_t counter, rootdfs = 0;
1048
0
    igraph_vector_int_t vertex_added;
1049
0
    igraph_int_t comps = 0;
1050
0
    igraph_vector_int_list_t *mycomponents = components, vcomponents;
1051
1052
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes);
1053
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes);
1054
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes);
1055
0
    IGRAPH_BITSET_INIT_FINALLY(&found, no_of_nodes);
1056
1057
0
    IGRAPH_STACK_INT_INIT_FINALLY(&path, 100);
1058
0
    IGRAPH_STACK_INT_INIT_FINALLY(&edgestack, 100);
1059
1060
0
    IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
1061
0
    IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
1062
1063
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_added, no_of_nodes);
1064
1065
0
    if (no) {
1066
0
        *no = 0;
1067
0
    }
1068
0
    if (tree_edges) {
1069
0
        igraph_vector_int_list_clear(tree_edges);
1070
0
    }
1071
0
    if (components) {
1072
0
        igraph_vector_int_list_clear(components);
1073
0
    }
1074
0
    if (component_edges) {
1075
0
        igraph_vector_int_list_clear(component_edges);
1076
0
    }
1077
0
    if (articulation_points) {
1078
0
        igraph_vector_int_clear(articulation_points);
1079
0
    }
1080
0
    if (component_edges && !components) {
1081
0
        mycomponents = &vcomponents;
1082
0
        IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(mycomponents, 0);
1083
0
    }
1084
1085
0
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
1086
1087
0
        if (VECTOR(low)[i] != 0) {
1088
0
            continue;    /* already visited */
1089
0
        }
1090
1091
0
        IGRAPH_ALLOW_INTERRUPTION();
1092
1093
0
        IGRAPH_CHECK(igraph_stack_int_push(&path, i));
1094
0
        counter = 1;
1095
0
        rootdfs = 0;
1096
0
        VECTOR(low)[i] = VECTOR(num)[i] = counter++;
1097
0
        while (!igraph_stack_int_empty(&path)) {
1098
0
            igraph_int_t n;
1099
0
            igraph_int_t act = igraph_stack_int_top(&path);
1100
0
            igraph_int_t actnext = VECTOR(nextptr)[act];
1101
1102
0
            adjedges = igraph_inclist_get(&inclist, act);
1103
0
            n = igraph_vector_int_size(adjedges);
1104
0
            if (actnext < n) {
1105
                /* Step down (maybe) */
1106
0
                igraph_int_t edge = VECTOR(*adjedges)[actnext];
1107
0
                igraph_int_t nei = IGRAPH_OTHER(graph, edge, act);
1108
0
                if (VECTOR(low)[nei] == 0) {
1109
0
                    if (act == i) {
1110
0
                        rootdfs++;
1111
0
                    }
1112
0
                    IGRAPH_CHECK(igraph_stack_int_push(&edgestack, edge));
1113
0
                    IGRAPH_CHECK(igraph_stack_int_push(&path, nei));
1114
0
                    VECTOR(low)[nei] = VECTOR(num)[nei] = counter++;
1115
0
                } else {
1116
                    /* Update low value if needed */
1117
0
                    if (VECTOR(num)[nei] < VECTOR(low)[act]) {
1118
0
                        VECTOR(low)[act] = VECTOR(num)[nei];
1119
0
                    }
1120
0
                }
1121
0
                VECTOR(nextptr)[act] += 1;
1122
0
            } else {
1123
                /* Step up */
1124
0
                igraph_stack_int_pop(&path);
1125
0
                if (!igraph_stack_int_empty(&path)) {
1126
0
                    igraph_int_t prev = igraph_stack_int_top(&path);
1127
                    /* Update LOW value if needed */
1128
0
                    if (VECTOR(low)[act] < VECTOR(low)[prev]) {
1129
0
                        VECTOR(low)[prev] = VECTOR(low)[act];
1130
0
                    }
1131
                    /* Check for articulation point */
1132
0
                    if (VECTOR(low)[act] >= VECTOR(num)[prev]) {
1133
0
                        if (articulation_points && !IGRAPH_BIT_TEST(found, prev)
1134
0
                            && prev != i /* the root */) {
1135
0
                            IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, prev));
1136
0
                            IGRAPH_BIT_SET(found, prev);
1137
0
                        }
1138
0
                        if (no) {
1139
0
                            *no += 1;
1140
0
                        }
1141
1142
                        /*------------------------------------*/
1143
                        /* Record the biconnected component just found */
1144
0
                        if (tree_edges || mycomponents) {
1145
0
                            igraph_vector_int_t *v, *v2;
1146
0
                            comps++;
1147
0
                            if (tree_edges) {
1148
0
                                IGRAPH_CHECK(igraph_vector_int_list_push_back_new(tree_edges, &v));
1149
0
                            }
1150
0
                            if (mycomponents) {
1151
0
                                IGRAPH_CHECK(igraph_vector_int_list_push_back_new(mycomponents, &v2));
1152
0
                            }
1153
1154
0
                            while (!igraph_stack_int_empty(&edgestack)) {
1155
0
                                igraph_int_t e = igraph_stack_int_pop(&edgestack);
1156
0
                                igraph_int_t from = IGRAPH_FROM(graph, e);
1157
0
                                igraph_int_t to = IGRAPH_TO(graph, e);
1158
0
                                if (tree_edges) {
1159
0
                                    IGRAPH_CHECK(igraph_vector_int_push_back(v, e));
1160
0
                                }
1161
0
                                if (mycomponents) {
1162
0
                                    if (VECTOR(vertex_added)[from] != comps) {
1163
0
                                        VECTOR(vertex_added)[from] = comps;
1164
0
                                        IGRAPH_CHECK(igraph_vector_int_push_back(v2, from));
1165
0
                                    }
1166
0
                                    if (VECTOR(vertex_added)[to] != comps) {
1167
0
                                        VECTOR(vertex_added)[to] = comps;
1168
0
                                        IGRAPH_CHECK(igraph_vector_int_push_back(v2, to));
1169
0
                                    }
1170
0
                                }
1171
0
                                if (from == prev || to == prev) {
1172
0
                                    break;
1173
0
                                }
1174
0
                            }
1175
1176
0
                            if (component_edges) {
1177
0
                                igraph_vector_int_t *nodes = igraph_vector_int_list_get_ptr(mycomponents, comps - 1);
1178
0
                                igraph_int_t ii, no_vert = igraph_vector_int_size(nodes);
1179
0
                                igraph_vector_int_t *vv;
1180
1181
0
                                IGRAPH_CHECK(igraph_vector_int_list_push_back_new(component_edges, &vv));
1182
0
                                for (ii = 0; ii < no_vert; ii++) {
1183
0
                                    igraph_int_t vert = VECTOR(*nodes)[ii];
1184
0
                                    igraph_vector_int_t *edges = igraph_inclist_get(&inclist,
1185
0
                                                                 vert);
1186
0
                                    igraph_int_t j, nn = igraph_vector_int_size(edges);
1187
0
                                    for (j = 0; j < nn; j++) {
1188
0
                                        igraph_int_t e = VECTOR(*edges)[j];
1189
0
                                        igraph_int_t nei = IGRAPH_OTHER(graph, e, vert);
1190
0
                                        if (VECTOR(vertex_added)[nei] == comps && nei < vert) {
1191
0
                                            IGRAPH_CHECK(igraph_vector_int_push_back(vv, e));
1192
0
                                        }
1193
0
                                    }
1194
0
                                }
1195
0
                            }
1196
0
                        } /* record component if requested */
1197
                        /*------------------------------------*/
1198
1199
0
                    }
1200
0
                } /* !igraph_stack_int_empty(&path) */
1201
0
            }
1202
1203
0
        } /* !igraph_stack_int_empty(&path) */
1204
1205
0
        if (articulation_points && rootdfs >= 2) {
1206
0
            IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, i));
1207
0
        }
1208
1209
0
    } /* i < no_of_nodes */
1210
1211
0
    if (mycomponents != components) {
1212
0
        igraph_vector_int_list_destroy(mycomponents);
1213
0
        IGRAPH_FINALLY_CLEAN(1);
1214
0
    }
1215
1216
0
    igraph_vector_int_destroy(&vertex_added);
1217
0
    igraph_inclist_destroy(&inclist);
1218
0
    igraph_stack_int_destroy(&edgestack);
1219
0
    igraph_stack_int_destroy(&path);
1220
0
    igraph_bitset_destroy(&found);
1221
0
    igraph_vector_int_destroy(&low);
1222
0
    igraph_vector_int_destroy(&num);
1223
0
    igraph_vector_int_destroy(&nextptr);
1224
0
    IGRAPH_FINALLY_CLEAN(8);
1225
1226
0
    return IGRAPH_SUCCESS;
1227
0
}
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
0
igraph_error_t igraph_is_biconnected(const igraph_t *graph, igraph_bool_t *res) {
1255
1256
0
    igraph_int_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_int_t root = 0; /* start DFS from vertex 0 */
1296
0
    igraph_int_t counter = 1;
1297
0
    igraph_int_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_int_t act = igraph_stack_int_top(&path);
1302
0
        igraph_int_t actnext = VECTOR(nextptr)[act];
1303
1304
0
        const igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&inclist, act);
1305
0
        const igraph_int_t n = igraph_vector_int_size(neis);
1306
0
        if (actnext < n) {
1307
            /* Step down (maybe) */
1308
0
            igraph_int_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_int_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 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
0
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
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1409
0
    igraph_inclist_t il;
1410
0
    igraph_bitset_t visited;
1411
0
    igraph_vector_int_t vis; /* vis[u] time when vertex u was first visited */
1412
0
    igraph_vector_int_t low; /* low[u] is the lowest visit time of vertices reachable from u */
1413
0
    igraph_vector_int_t incoming_edge;
1414
0
    igraph_stack_int_t su, si;
1415
0
    igraph_int_t time;
1416
1417
0
    IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
1418
0
    IGRAPH_FINALLY(igraph_inclist_destroy, &il);
1419
1420
1421
0
    IGRAPH_BITSET_INIT_FINALLY(&visited, no_of_nodes);
1422
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vis, no_of_nodes);
1423
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes);
1424
1425
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&incoming_edge, no_of_nodes);
1426
0
    igraph_vector_int_fill(&incoming_edge, -1);
1427
1428
0
    IGRAPH_STACK_INT_INIT_FINALLY(&su, 0);
1429
0
    IGRAPH_STACK_INT_INIT_FINALLY(&si, 0);
1430
1431
0
    igraph_vector_int_clear(bridges);
1432
1433
0
    time = 0;
1434
0
    for (igraph_int_t start = 0; start < no_of_nodes; ++start) {
1435
0
        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
0
            IGRAPH_CHECK(igraph_stack_int_push(&su, start));
1442
0
            IGRAPH_CHECK(igraph_stack_int_push(&si, 0));
1443
1444
0
            while (! igraph_stack_int_empty(&su)) {
1445
0
                igraph_int_t u = igraph_stack_int_pop(&su);
1446
0
                igraph_int_t i = igraph_stack_int_pop(&si);
1447
1448
0
                if (i == 0) {
1449
                    /* We are at the first step of visiting vertex u. */
1450
1451
0
                    IGRAPH_BIT_SET(visited, u);
1452
1453
0
                    time += 1;
1454
1455
0
                    VECTOR(vis)[u] = time;
1456
0
                    VECTOR(low)[u] = time;
1457
0
                }
1458
1459
0
                igraph_vector_int_t *incedges = igraph_inclist_get(&il, u);
1460
1461
0
                if (i < igraph_vector_int_size(incedges)) {
1462
0
                    IGRAPH_CHECK(igraph_stack_int_push(&su, u));
1463
0
                    IGRAPH_CHECK(igraph_stack_int_push(&si, i+1));
1464
1465
0
                    igraph_int_t edge = VECTOR(*incedges)[i];
1466
0
                    igraph_int_t v = IGRAPH_OTHER(graph, edge, u);
1467
1468
0
                    if (! IGRAPH_BIT_TEST(visited, v)) {
1469
0
                        VECTOR(incoming_edge)[v] = edge;
1470
1471
0
                        IGRAPH_CHECK(igraph_stack_int_push(&su, v));
1472
0
                        IGRAPH_CHECK(igraph_stack_int_push(&si, 0));
1473
0
                    } else if (edge != VECTOR(incoming_edge)[u]) {
1474
0
                        VECTOR(low)[u] = VECTOR(low)[u] < VECTOR(vis)[v] ? VECTOR(low)[u] : VECTOR(vis)[v];
1475
0
                    }
1476
0
                } 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
0
                    igraph_int_t edge = VECTOR(incoming_edge)[u];
1482
0
                    if (edge >= 0) {
1483
0
                        igraph_int_t w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */
1484
0
                        VECTOR(low)[w] = VECTOR(low)[w] < VECTOR(low)[u] ? VECTOR(low)[w] : VECTOR(low)[u];
1485
0
                        if (VECTOR(low)[u] > VECTOR(vis)[w]) {
1486
0
                            IGRAPH_CHECK(igraph_vector_int_push_back(bridges, edge));
1487
0
                        }
1488
0
                    }
1489
0
                }
1490
0
            }
1491
0
        }
1492
0
    }
1493
1494
0
    igraph_stack_int_destroy(&si);
1495
0
    igraph_stack_int_destroy(&su);
1496
0
    igraph_vector_int_destroy(&incoming_edge);
1497
0
    igraph_vector_int_destroy(&low);
1498
0
    igraph_vector_int_destroy(&vis);
1499
0
    igraph_bitset_destroy(&visited);
1500
0
    igraph_inclist_destroy(&il);
1501
0
    IGRAPH_FINALLY_CLEAN(7);
1502
1503
0
    return IGRAPH_SUCCESS;
1504
0
}
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
0
) {
1558
1559
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
1560
0
    igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL;
1561
0
    igraph_bitset_t already_added;
1562
0
    igraph_int_t i, vsize;
1563
0
    igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL;
1564
1565
0
    if (vertex < 0 || vertex >= no_of_nodes) {
1566
0
        IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVVID);
1567
0
    }
1568
0
    if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
1569
0
        mode != IGRAPH_ALL) {
1570
0
        IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE);
1571
0
    }
1572
1573
0
    igraph_vector_int_clear(res);
1574
1575
0
    IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes);
1576
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0);
1577
0
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
1578
1579
0
    IGRAPH_CHECK(igraph_dqueue_int_push(&q, vertex));
1580
0
    IGRAPH_CHECK(igraph_vector_int_push_back(res, vertex));
1581
0
    IGRAPH_BIT_SET(already_added, vertex);
1582
1583
0
    while (!igraph_dqueue_int_empty(&q)) {
1584
0
        igraph_int_t actnode = igraph_dqueue_int_pop(&q);
1585
1586
0
        IGRAPH_ALLOW_INTERRUPTION();
1587
1588
0
        IGRAPH_CHECK(igraph_neighbors(graph, &tmp, actnode, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE));
1589
0
        vsize = igraph_vector_int_size(&tmp);
1590
0
        for (i = 0; i < vsize; i++) {
1591
0
            igraph_int_t neighbor = VECTOR(tmp)[i];
1592
1593
0
            if (IGRAPH_BIT_TEST(already_added, neighbor)) {
1594
0
                continue;
1595
0
            }
1596
0
            IGRAPH_BIT_SET(already_added, neighbor);
1597
0
            IGRAPH_CHECK(igraph_vector_int_push_back(res, neighbor));
1598
0
            IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor));
1599
0
        }
1600
0
    }
1601
1602
0
    igraph_dqueue_int_destroy(&q);
1603
0
    igraph_vector_int_destroy(&tmp);
1604
0
    igraph_bitset_destroy(&already_added);
1605
0
    IGRAPH_FINALLY_CLEAN(3);
1606
1607
0
    return IGRAPH_SUCCESS;
1608
0
}