Coverage Report

Created: 2023-11-27 06:05

/src/igraph/src/misc/conversion.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: C -*-  */
2
/*
3
   IGraph library.
4
   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
5
   334 Harvard street, Cambridge, MA 02139 USA
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 2 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program; if not, write to the Free Software
19
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20
   02110-1301 USA
21
22
*/
23
24
#include "igraph_conversion.h"
25
26
#include "igraph_iterators.h"
27
#include "igraph_interface.h"
28
#include "igraph_attributes.h"
29
#include "igraph_constructors.h"
30
#include "igraph_structural.h"
31
#include "igraph_sparsemat.h"
32
#include "igraph_random.h"
33
34
#include "core/fixed_vectorlist.h"
35
#include "graph/attributes.h"
36
#include "math/safe_intop.h"
37
38
0
#define WEIGHT_OF(eid) (weights ? VECTOR(*weights)[eid] : 1)
39
40
/**
41
 * \ingroup conversion
42
 * \function igraph_get_adjacency
43
 * \brief The adjacency matrix of a graph.
44
 *
45
 * </para><para>
46
 * The result is an adjacency matrix. Entry i, j of the matrix
47
 * contains the number of edges connecting vertex i to vertex j in the unweighted
48
 * case, or the total weight of edges connecting vertex i to vertex j in the
49
 * weighted case.
50
 *
51
 * \param graph Pointer to the graph to convert
52
 * \param res Pointer to an initialized matrix object, it will be
53
 *        resized if needed.
54
 * \param type Constant specifying the type of the adjacency matrix to
55
 *        create for undirected graphs. It is ignored for directed
56
 *        graphs. Possible values:
57
 *        \clist
58
 *        \cli IGRAPH_GET_ADJACENCY_UPPER
59
 *          the upper right triangle of the matrix is used.
60
 *        \cli IGRAPH_GET_ADJACENCY_LOWER
61
 *          the lower left triangle of the matrix is used.
62
 *        \cli IGRAPH_GET_ADJACENCY_BOTH
63
 *          the whole matrix is used, a symmetric matrix is returned
64
 *          if the graph is undirected.
65
 *        \endclist
66
 * \param weights An optional vector containing the weight of each edge
67
 *        in the graph. Supply a null pointer here to make all edges have
68
 *        the same weight of 1.
69
 * \param loops Constant specifying how loop edges should be handled.
70
 *        Possible values:
71
 *        \clist
72
 *        \cli IGRAPH_NO_LOOPS
73
 *          loop edges are ignored and the diagonal of the matrix will contain
74
 *          zeros only
75
 *        \cli IGRAPH_LOOPS_ONCE
76
 *          loop edges are counted once, i.e. a vertex with a single unweighted
77
 *          loop edge will have 1 in the corresponding diagonal entry
78
 *        \cli IGRAPH_LOOPS_TWICE
79
 *          loop edges are counted twice in \em undirected graphs, i.e. a vertex
80
 *          with a single unweighted loop edge in an undirected graph will have
81
 *          2 in the corresponding diagonal entry. Loop edges in directed graphs
82
 *          are still counted as 1. Essentially, this means that the function is
83
 *          counting the incident edge \em stems , which makes more sense when
84
 *          using the adjacency matrix in linear algebra.
85
 *        \endclist
86
 * \return Error code:
87
 *        \c IGRAPH_EINVAL invalid type argument.
88
 *
89
 * \sa \ref igraph_get_adjacency_sparse() if you want a sparse matrix representation
90
 *
91
 * Time complexity: O(|V||V|), |V| is the number of vertices in the graph.
92
 */
93
94
igraph_error_t igraph_get_adjacency(
95
    const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type,
96
    const igraph_vector_t *weights, igraph_loops_t loops
97
0
) {
98
0
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
99
0
    igraph_integer_t no_of_edges = igraph_ecount(graph);
100
0
    igraph_bool_t directed = igraph_is_directed(graph);
101
0
    igraph_integer_t i, from, to;
102
103
0
    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes));
104
0
    igraph_matrix_null(res);
105
106
0
    if (directed) {
107
0
        for (i = 0; i < no_of_edges; i++) {
108
0
            from = IGRAPH_FROM(graph, i);
109
0
            to = IGRAPH_TO(graph, i);
110
0
            if (from != to || loops != IGRAPH_NO_LOOPS) {
111
0
                MATRIX(*res, from, to) += WEIGHT_OF(i);
112
0
            }
113
0
        }
114
0
    } else if (type == IGRAPH_GET_ADJACENCY_UPPER) {
115
0
        for (i = 0; i < no_of_edges; i++) {
116
0
            from = IGRAPH_FROM(graph, i);
117
0
            to = IGRAPH_TO(graph, i);
118
0
            if (to < from) {
119
0
                MATRIX(*res, to, from) += WEIGHT_OF(i);
120
0
            } else {
121
0
                MATRIX(*res, from, to) += WEIGHT_OF(i);
122
0
            }
123
0
            if (to == from && loops == IGRAPH_LOOPS_TWICE) {
124
0
                MATRIX(*res, to, to) += WEIGHT_OF(i);
125
0
            }
126
0
        }
127
0
    } else if (type == IGRAPH_GET_ADJACENCY_LOWER) {
128
0
        for (i = 0; i < no_of_edges; i++) {
129
0
            from = IGRAPH_FROM(graph, i);
130
0
            to = IGRAPH_TO(graph, i);
131
0
            if (to < from) {
132
0
                MATRIX(*res, from, to) += WEIGHT_OF(i);
133
0
            } else {
134
0
                MATRIX(*res, to, from) += WEIGHT_OF(i);
135
0
            }
136
0
            if (to == from && loops == IGRAPH_LOOPS_TWICE) {
137
0
                MATRIX(*res, to, to) += WEIGHT_OF(i);
138
0
            }
139
0
        }
140
0
    } else if (type == IGRAPH_GET_ADJACENCY_BOTH) {
141
0
        for (i = 0; i < no_of_edges; i++) {
142
0
            from = IGRAPH_FROM(graph, i);
143
0
            to = IGRAPH_TO(graph, i);
144
0
            MATRIX(*res, from, to) += WEIGHT_OF(i);
145
0
            if (from != to || loops == IGRAPH_LOOPS_TWICE) {
146
0
                MATRIX(*res, to, from) += WEIGHT_OF(i);
147
0
            }
148
0
        }
149
0
    } else {
150
0
        IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL);
151
0
    }
152
153
    /* Erase the diagonal if we don't need loop edges */
154
0
    if (loops == IGRAPH_NO_LOOPS) {
155
0
        for (i = 0; i < no_of_nodes; i++) {
156
0
            MATRIX(*res, i, i) = 0;
157
0
        }
158
0
    }
159
160
0
    return IGRAPH_SUCCESS;
161
0
}
162
163
/**
164
 * \function igraph_get_adjacency_sparse
165
 * \brief Returns the adjacency matrix of a graph in a sparse matrix format.
166
 *
167
 * \param graph The input graph.
168
 * \param res Pointer to an \em initialized sparse matrix. The result
169
 *    will be stored here. The matrix will be resized as needed.
170
 * \param type Constant specifying the type of the adjacency matrix to
171
 *        create for undirected graphs. It is ignored for directed
172
 *        graphs. Possible values:
173
 *        \clist
174
 *        \cli IGRAPH_GET_ADJACENCY_UPPER
175
 *          the upper right triangle of the matrix is used.
176
 *        \cli IGRAPH_GET_ADJACENCY_LOWER
177
 *          the lower left triangle of the matrix is used.
178
 *        \cli IGRAPH_GET_ADJACENCY_BOTH
179
 *          the whole matrix is used, a symmetric matrix is returned
180
 *          if the graph is undirected.
181
 *        \endclist
182
 * \return Error code:
183
 *        \c IGRAPH_EINVAL invalid type argument.
184
 *
185
 * \sa \ref igraph_get_adjacency(), the dense version of this function.
186
 *
187
 * Time complexity: TODO.
188
 */
189
190
igraph_error_t igraph_get_adjacency_sparse(
191
    const igraph_t *graph, igraph_sparsemat_t *res, igraph_get_adjacency_t type,
192
    const igraph_vector_t *weights, igraph_loops_t loops
193
0
) {
194
195
0
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
196
0
    igraph_integer_t no_of_edges = igraph_ecount(graph);
197
0
    igraph_bool_t directed = igraph_is_directed(graph);
198
0
    igraph_integer_t nzmax = directed ? no_of_edges : no_of_edges * 2;
199
0
    igraph_integer_t i, from, to;
200
201
0
    IGRAPH_CHECK(igraph_sparsemat_resize(res, no_of_nodes, no_of_nodes, nzmax));
202
203
0
    if (directed) {
204
0
        for (i = 0; i < no_of_edges; i++) {
205
0
            from = IGRAPH_FROM(graph, i);
206
0
            to = IGRAPH_TO(graph, i);
207
0
            if (from != to || loops != IGRAPH_NO_LOOPS) {
208
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i)));
209
0
            }
210
0
        }
211
0
    } else if (type == IGRAPH_GET_ADJACENCY_UPPER) {
212
0
        for (i = 0; i < no_of_edges; i++) {
213
0
            from = IGRAPH_FROM(graph, i);
214
0
            to = IGRAPH_TO(graph, i);
215
0
            if (to < from) {
216
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i)));
217
0
            } else if (to == from) {
218
0
                switch (loops) {
219
0
                    case IGRAPH_LOOPS_ONCE:
220
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i)));
221
0
                        break;
222
0
                    case IGRAPH_LOOPS_TWICE:
223
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i)));
224
0
                        break;
225
0
                    case IGRAPH_NO_LOOPS:
226
0
                    default:
227
0
                        break;
228
0
                }
229
0
            } else {
230
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i)));
231
0
            }
232
0
        }
233
0
    } else if (type == IGRAPH_GET_ADJACENCY_LOWER) {
234
0
        for (i = 0; i < no_of_edges; i++) {
235
0
            from = IGRAPH_FROM(graph, i);
236
0
            to = IGRAPH_TO(graph, i);
237
0
            if (to < from) {
238
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i)));
239
0
            } else if (to == from) {
240
0
                switch (loops) {
241
0
                    case IGRAPH_LOOPS_ONCE:
242
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i)));
243
0
                        break;
244
0
                    case IGRAPH_LOOPS_TWICE:
245
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i)));
246
0
                        break;
247
0
                    case IGRAPH_NO_LOOPS:
248
0
                    default:
249
0
                        break;
250
0
                }
251
0
            } else {
252
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i)));
253
0
            }
254
0
        }
255
0
    } else if (type == IGRAPH_GET_ADJACENCY_BOTH) {
256
0
        for (i = 0; i < no_of_edges; i++) {
257
0
            from = IGRAPH_FROM(graph, i);
258
0
            to = IGRAPH_TO(graph, i);
259
0
            if (to == from) {
260
0
                switch (loops) {
261
0
                    case IGRAPH_LOOPS_ONCE:
262
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i)));
263
0
                        break;
264
0
                    case IGRAPH_LOOPS_TWICE:
265
0
                        IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i)));
266
0
                        break;
267
0
                    case IGRAPH_NO_LOOPS:
268
0
                    default:
269
0
                        break;
270
0
                }
271
0
            } else {
272
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i)));
273
0
                IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i)));
274
0
            }
275
0
        }
276
0
    } else {
277
0
        IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL);
278
0
    }
279
280
0
    return IGRAPH_SUCCESS;
281
0
}
282
283
#undef WEIGHT_OF
284
285
/**
286
 * \function igraph_get_sparsemat
287
 * \brief Converts an igraph graph to a sparse matrix (deprecated).
288
 *
289
 * If the graph is undirected, then a symmetric matrix is created.
290
 *
291
 * </para><para>
292
 * This function is deprecated in favour of \ref igraph_get_adjacency_sparse(),
293
 * but does not work in an identical way. This function takes an \em uninitialized
294
 * \c igraph_sparsemat_t while \ref igraph_get_adjacency_sparse() takes
295
 * an already initialized one.
296
 *
297
 * \param graph The input graph.
298
 * \param res Pointer to an \em uninitialized sparse matrix. The result
299
 *    will be stored here.
300
 * \return Error code.
301
 *
302
 * \deprecated-by igraph_get_adjacency_sparse 0.10.0
303
 */
304
305
0
igraph_error_t igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res) {
306
0
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
307
0
    igraph_integer_t no_of_edges = igraph_ecount(graph);
308
0
    igraph_integer_t nzmax = igraph_is_directed(graph) ? no_of_edges : 2*no_of_edges;
309
0
    IGRAPH_CHECK(igraph_sparsemat_init(res, no_of_nodes, no_of_nodes, nzmax));
310
0
    return igraph_get_adjacency_sparse(graph, res, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE);
311
0
}
312
313
/**
314
 * \ingroup conversion
315
 * \function igraph_get_edgelist
316
 * \brief The list of edges in a graph.
317
 *
318
 * The order of the edges is given by the edge IDs.
319
 *
320
 * \param graph Pointer to the graph object
321
 * \param res Pointer to an initialized vector object, it will be
322
 *        resized.
323
 * \param bycol Logical, if true, the edges will be returned
324
 *        columnwise, e.g. the first edge is
325
 *        <code>res[0]->res[|E|]</code>, the second is
326
 *        <code>res[1]->res[|E|+1]</code>, etc.
327
 * \return Error code.
328
 *
329
 * \sa \ref igraph_edges() to return the result only for some edge IDs.
330
 *
331
 * Time complexity: O(|E|), the number of edges in the graph.
332
 */
333
334
84.0k
igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *res, igraph_bool_t bycol) {
335
336
84.0k
    igraph_eit_t edgeit;
337
84.0k
    igraph_integer_t no_of_edges = igraph_ecount(graph);
338
84.0k
    igraph_integer_t vptr = 0;
339
84.0k
    igraph_integer_t from, to;
340
341
84.0k
    IGRAPH_CHECK(igraph_vector_int_resize(res, no_of_edges * 2));
342
84.0k
    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID),
343
84.0k
                                   &edgeit));
344
84.0k
    IGRAPH_FINALLY(igraph_eit_destroy, &edgeit);
345
346
84.0k
    if (bycol) {
347
0
        while (!IGRAPH_EIT_END(edgeit)) {
348
0
            igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &from, &to);
349
0
            VECTOR(*res)[vptr] = from;
350
0
            VECTOR(*res)[vptr + no_of_edges] = to;
351
0
            vptr++;
352
0
            IGRAPH_EIT_NEXT(edgeit);
353
0
        }
354
84.0k
    } else {
355
326M
        while (!IGRAPH_EIT_END(edgeit)) {
356
326M
            igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &from, &to);
357
326M
            VECTOR(*res)[vptr++] = from;
358
326M
            VECTOR(*res)[vptr++] = to;
359
326M
            IGRAPH_EIT_NEXT(edgeit);
360
326M
        }
361
84.0k
    }
362
363
84.0k
    igraph_eit_destroy(&edgeit);
364
84.0k
    IGRAPH_FINALLY_CLEAN(1);
365
84.0k
    return IGRAPH_SUCCESS;
366
84.0k
}
367
368
/**
369
 * \function igraph_to_directed
370
 * \brief Convert an undirected graph to a directed one.
371
 *
372
 * If the supplied graph is directed, this function does nothing.
373
 *
374
 * \param graph The graph object to convert.
375
 * \param mode Constant, specifies the details of how exactly the
376
 *        conversion is done. Possible values:
377
 *        \clist
378
 *        \cli IGRAPH_TO_DIRECTED_ARBITRARY
379
 *        The number of edges in the
380
 *        graph stays the same, an arbitrarily directed edge is
381
 *        created for each undirected edge.
382
 *        \cli IGRAPH_TO_DIRECTED_MUTUAL
383
 *        Two directed edges are
384
 *        created for each undirected edge, one in each direction.
385
 *        \cli IGRAPH_TO_DIRECTED_RANDOM
386
 *        Each undirected edge is converted to a randomly oriented
387
 *        directed one.
388
 *        \cli IGRAPH_TO_DIRECTED_ACYCLIC
389
 *        Each undirected edge is converted to a directed edge oriented
390
 *        from a lower index vertex to a higher index one. If no self-loops
391
 *        were present, then the result is a directed acyclic graph.
392
 *        \endclist
393
 * \return Error code.
394
 *
395
 * Time complexity: O(|V|+|E|), the number of vertices plus the number
396
 * of edges.
397
 */
398
399
igraph_error_t igraph_to_directed(igraph_t *graph,
400
833
                       igraph_to_directed_t mode) {
401
833
    igraph_integer_t no_of_edges = igraph_ecount(graph);
402
833
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
403
404
833
    if (igraph_is_directed(graph)) {
405
0
        return IGRAPH_SUCCESS;
406
0
    }
407
408
833
    switch (mode) {
409
0
    case IGRAPH_TO_DIRECTED_ARBITRARY:
410
0
    case IGRAPH_TO_DIRECTED_RANDOM:
411
0
    case IGRAPH_TO_DIRECTED_ACYCLIC:
412
0
      {
413
0
        igraph_t newgraph;
414
0
        igraph_vector_int_t edges;
415
0
        igraph_integer_t size = no_of_edges * 2;
416
417
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, size);
418
0
        IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
419
420
0
        if (mode == IGRAPH_TO_DIRECTED_RANDOM) {
421
0
            RNG_BEGIN();
422
423
0
            for (igraph_integer_t i=0; i < no_of_edges; ++i) {
424
0
                if (RNG_INTEGER(0,1)) {
425
0
                    igraph_integer_t temp = VECTOR(edges)[2*i];
426
0
                    VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1];
427
0
                    VECTOR(edges)[2*i+1] = temp;
428
0
                }
429
0
            }
430
431
0
            RNG_END();
432
0
        } else if (mode == IGRAPH_TO_DIRECTED_ACYCLIC) {
433
            /* Currently, the endpoints of undirected edges are ordered in the
434
               internal graph datastructure, i.e. it is always true that from < to.
435
               However, it is not guaranteed that this will not be changed in
436
               the future, and this ordering should not be relied on outside of
437
               the implementation of the minimal API in type_indexededgelist.c.
438
439
               Therefore, we order the edge endpoints anyway in the following loop: */
440
0
            for (igraph_integer_t i=0; i < no_of_edges; ++i) {
441
0
                if (VECTOR(edges)[2*i] > VECTOR(edges)[2*i+1]) {
442
0
                    igraph_integer_t temp = VECTOR(edges)[2*i];
443
0
                    VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1];
444
0
                    VECTOR(edges)[2*i+1] = temp;
445
0
                }
446
0
            }
447
0
        }
448
449
0
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
450
0
                                   no_of_nodes,
451
0
                                   IGRAPH_DIRECTED));
452
0
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
453
0
        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
454
0
        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, true);
455
0
        igraph_vector_int_destroy(&edges);
456
0
        IGRAPH_FINALLY_CLEAN(2);
457
458
0
        igraph_destroy(graph);
459
0
        *graph = newgraph;
460
461
0
        break;
462
0
      }
463
833
    case IGRAPH_TO_DIRECTED_MUTUAL:
464
833
      {
465
833
        igraph_t newgraph;
466
833
        igraph_vector_int_t edges;
467
833
        igraph_vector_int_t index;
468
833
        igraph_integer_t size;
469
470
833
        IGRAPH_SAFE_MULT(no_of_edges, 4, &size);
471
833
        IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0);
472
833
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size));
473
833
        IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
474
833
        IGRAPH_CHECK(igraph_vector_int_resize(&edges, size));
475
833
        IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges * 2);
476
18.6k
        for (igraph_integer_t i = 0; i < no_of_edges; i++) {
477
17.8k
            VECTOR(edges)[no_of_edges * 2 + i * 2]  = VECTOR(edges)[i * 2 + 1];
478
17.8k
            VECTOR(edges)[no_of_edges * 2 + i * 2 + 1] = VECTOR(edges)[i * 2];
479
17.8k
            VECTOR(index)[i] = VECTOR(index)[no_of_edges + i] = i;
480
17.8k
        }
481
482
833
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
483
833
                                   no_of_nodes,
484
833
                                   IGRAPH_DIRECTED));
485
833
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
486
833
        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
487
833
        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges=*/false);
488
833
        IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &index));
489
490
833
        igraph_vector_int_destroy(&index);
491
833
        igraph_vector_int_destroy(&edges);
492
833
        IGRAPH_FINALLY_CLEAN(3);
493
494
833
        igraph_destroy(graph);
495
833
        *graph = newgraph;
496
497
833
        break;
498
833
      }
499
0
    default:
500
0
        IGRAPH_ERROR("Cannot direct graph, invalid mode.", IGRAPH_EINVAL);
501
833
    }
502
503
833
    return IGRAPH_SUCCESS;
504
833
}
505
506
/**
507
 * \function igraph_to_undirected
508
 * \brief Convert a directed graph to an undirected one.
509
 *
510
 * </para><para>
511
 * If the supplied graph is undirected, this function does nothing.
512
 *
513
 * \param graph The graph object to convert.
514
 * \param mode Constant, specifies the details of how exactly the
515
 *        conversion is done. Possible values: \c
516
 *        IGRAPH_TO_UNDIRECTED_EACH: the number of edges remains
517
 *        constant, an undirected edge is created for each directed
518
 *        one, this version might create graphs with multiple edges;
519
 *        \c IGRAPH_TO_UNDIRECTED_COLLAPSE: one undirected edge will
520
 *        be created for each pair of vertices that are connected
521
 *        with at least one directed edge, no multiple edges will be
522
 *        created. \c IGRAPH_TO_UNDIRECTED_MUTUAL creates an undirected
523
 *        edge for each pair of mutual edges in the directed graph.
524
 *        Non-mutual edges are lost; loop edges are kept unconditionally.
525
 *        This mode might create multiple edges.
526
 * \param edge_comb What to do with the edge attributes. See the igraph
527
 *        manual section about attributes for details. \c NULL means that
528
 *        the edge attributes are lost during the conversion, \em except
529
 *        when \c mode is \c IGRAPH_TO_UNDIRECTED_EACH, in which case the
530
 *        edge attributes are kept intact.
531
 * \return Error code.
532
 *
533
 * Time complexity: O(|V|+|E|), the number of vertices plus the number
534
 * of edges.
535
 *
536
 * \example examples/simple/igraph_to_undirected.c
537
 */
538
539
igraph_error_t igraph_to_undirected(igraph_t *graph,
540
                         igraph_to_undirected_t mode,
541
1.08k
                         const igraph_attribute_combination_t *edge_comb) {
542
543
1.08k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
544
1.08k
    igraph_integer_t no_of_edges = igraph_ecount(graph);
545
1.08k
    igraph_vector_int_t edges;
546
1.08k
    igraph_t newgraph;
547
1.08k
    igraph_bool_t attr = edge_comb && igraph_has_attribute_table();
548
549
1.08k
    if (mode != IGRAPH_TO_UNDIRECTED_EACH &&
550
1.08k
        mode != IGRAPH_TO_UNDIRECTED_COLLAPSE &&
551
1.08k
        mode != IGRAPH_TO_UNDIRECTED_MUTUAL) {
552
0
        IGRAPH_ERROR("Cannot undirect graph, invalid mode", IGRAPH_EINVAL);
553
0
    }
554
555
1.08k
    if (!igraph_is_directed(graph)) {
556
0
        return IGRAPH_SUCCESS;
557
0
    }
558
559
1.08k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0);
560
561
1.08k
    if (mode == IGRAPH_TO_UNDIRECTED_EACH) {
562
0
        igraph_es_t es;
563
0
        igraph_eit_t eit;
564
565
0
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2));
566
0
        IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID));
567
0
        IGRAPH_FINALLY(igraph_es_destroy, &es);
568
0
        IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
569
0
        IGRAPH_FINALLY(igraph_eit_destroy, &eit);
570
571
0
        while (!IGRAPH_EIT_END(eit)) {
572
0
            igraph_integer_t edge = IGRAPH_EIT_GET(eit);
573
0
            IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_FROM(graph, edge)));
574
0
            IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_TO(graph, edge)));
575
0
            IGRAPH_EIT_NEXT(eit);
576
0
        }
577
578
0
        igraph_eit_destroy(&eit);
579
0
        igraph_es_destroy(&es);
580
0
        IGRAPH_FINALLY_CLEAN(2);
581
582
0
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
583
0
                                   no_of_nodes,
584
0
                                   IGRAPH_UNDIRECTED));
585
0
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
586
0
        igraph_vector_int_destroy(&edges);
587
0
        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
588
0
        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, true);
589
0
        IGRAPH_FINALLY_CLEAN(2);
590
0
        igraph_destroy(graph);
591
0
        *graph = newgraph;
592
593
1.08k
    } else if (mode == IGRAPH_TO_UNDIRECTED_COLLAPSE) {
594
1.08k
        igraph_vector_int_t inadj, outadj;
595
1.08k
        igraph_vector_int_t mergeinto;
596
1.08k
        igraph_integer_t actedge = 0;
597
598
1.08k
        if (attr) {
599
0
            IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges);
600
0
        }
601
602
1.08k
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2));
603
1.08k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0);
604
1.08k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&outadj, 0);
605
606
87.5k
        for (igraph_integer_t i = 0; i < no_of_nodes; i++) {
607
86.4k
            igraph_integer_t n_out, n_in;
608
86.4k
            igraph_integer_t p1 = -1, p2 = -1;
609
86.4k
            igraph_integer_t e1 = 0, e2 = 0, n1 = 0, n2 = 0, last;
610
86.4k
            IGRAPH_CHECK(igraph_incident(graph, &outadj, i, IGRAPH_OUT));
611
86.4k
            IGRAPH_CHECK(igraph_incident(graph, &inadj, i, IGRAPH_IN));
612
86.4k
            n_out = igraph_vector_int_size(&outadj);
613
86.4k
            n_in = igraph_vector_int_size(&inadj);
614
615
1.05M
#define STEPOUT() if ( (++p1) < n_out) {    \
616
995k
        e1 = VECTOR(outadj)[p1]; \
617
995k
        n1 = IGRAPH_TO(graph, e1);      \
618
995k
    }
619
1.10M
#define STEPIN()  if ( (++p2) < n_in) {         \
620
1.04M
        e2 = VECTOR(inadj )[p2]; \
621
1.04M
        n2 = IGRAPH_FROM(graph, e2);        \
622
1.04M
    }
623
294k
#define ADD_NEW_EDGE() { \
624
294k
    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); \
625
294k
    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, last)); \
626
294k
}
627
1.98M
#define MERGE_INTO_CURRENT_EDGE(which) { \
628
1.98M
    if (attr) { \
629
0
        VECTOR(mergeinto)[which] = actedge; \
630
0
    } \
631
1.98M
}
632
633
86.4k
            STEPOUT();
634
86.4k
            STEPIN();
635
636
337k
            while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) {
637
250k
                last = (n1 <= n2) ? n1 : n2;
638
250k
                ADD_NEW_EDGE();
639
1.14M
                while (p1 < n_out && last == n1) {
640
892k
                    MERGE_INTO_CURRENT_EDGE(e1);
641
892k
                    STEPOUT();
642
892k
                }
643
1.16M
                while (p2 < n_in && last == n2) {
644
912k
                    MERGE_INTO_CURRENT_EDGE(e2);
645
912k
                    STEPIN();
646
912k
                }
647
250k
                actedge++;
648
250k
            }
649
650
108k
            while (p1 < n_out && n1 <= i) {
651
21.7k
                last = n1;
652
21.7k
                ADD_NEW_EDGE();
653
97.5k
                while (p1 < n_out && last == n1) {
654
75.7k
                    MERGE_INTO_CURRENT_EDGE(e1);
655
75.7k
                    STEPOUT();
656
75.7k
                }
657
21.7k
                actedge++;
658
21.7k
            }
659
660
108k
            while (p2 < n_in && n2 <= i) {
661
21.5k
                last = n2;
662
21.5k
                ADD_NEW_EDGE();
663
124k
                while (p2 < n_in && last == n2) {
664
103k
                    MERGE_INTO_CURRENT_EDGE(e2);
665
103k
                    STEPIN();
666
103k
                }
667
21.5k
                actedge++;
668
21.5k
            }
669
86.4k
        }
670
671
1.08k
#undef MERGE_INTO_CURRENT_EDGE
672
1.08k
#undef ADD_NEW_EDGE
673
1.08k
#undef STEPOUT
674
1.08k
#undef STEPIN
675
676
1.08k
        igraph_vector_int_destroy(&outadj);
677
1.08k
        igraph_vector_int_destroy(&inadj);
678
1.08k
        IGRAPH_FINALLY_CLEAN(2);
679
680
1.08k
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
681
1.08k
                                   no_of_nodes,
682
1.08k
                                   IGRAPH_UNDIRECTED));
683
1.08k
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
684
1.08k
        igraph_vector_int_destroy(&edges);
685
1.08k
        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
686
1.08k
        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges*/ false); /* no edge attributes */
687
688
1.08k
        if (attr) {
689
0
            igraph_fixed_vectorlist_t vl;
690
0
            IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, actedge));
691
0
            IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl);
692
693
0
            IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.vecs, edge_comb));
694
695
0
            igraph_fixed_vectorlist_destroy(&vl);
696
0
            IGRAPH_FINALLY_CLEAN(1);
697
0
        }
698
699
1.08k
        IGRAPH_FINALLY_CLEAN(2);
700
1.08k
        igraph_destroy(graph);
701
1.08k
        *graph = newgraph;
702
703
1.08k
        if (attr) {
704
0
            igraph_vector_int_destroy(&mergeinto);
705
0
            IGRAPH_FINALLY_CLEAN(1);
706
0
        }
707
1.08k
    } else if (mode == IGRAPH_TO_UNDIRECTED_MUTUAL) {
708
0
        igraph_vector_int_t inadj, outadj;
709
0
        igraph_vector_int_t mergeinto;
710
0
        igraph_integer_t actedge = 0;
711
712
0
        if (attr) {
713
0
            IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges);
714
0
            igraph_vector_int_fill(&mergeinto, -1);
715
0
        }
716
717
0
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2));
718
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0);
719
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&outadj, 0);
720
721
0
        for (igraph_integer_t i = 0; i < no_of_nodes; i++) {
722
0
            igraph_integer_t n_out, n_in;
723
0
            igraph_integer_t p1 = -1, p2 = -1;
724
0
            igraph_integer_t e1 = 0, e2 = 0, n1 = 0, n2 = 0;
725
0
            IGRAPH_CHECK(igraph_incident(graph, &outadj, i,
726
0
                                         IGRAPH_OUT));
727
0
            IGRAPH_CHECK(igraph_incident(graph, &inadj,  i,
728
0
                                         IGRAPH_IN));
729
0
            n_out = igraph_vector_int_size(&outadj);
730
0
            n_in = igraph_vector_int_size(&inadj);
731
732
0
#define STEPOUT() if ( (++p1) < n_out) {    \
733
0
        e1 = VECTOR(outadj)[p1]; \
734
0
        n1 = IGRAPH_TO(graph, e1);      \
735
0
    }
736
0
#define STEPIN()  if ( (++p2) < n_in) {         \
737
0
        e2 = VECTOR(inadj )[p2]; \
738
0
        n2 = IGRAPH_FROM(graph, e2);        \
739
0
    }
740
741
0
            STEPOUT();
742
0
            STEPIN();
743
744
0
            while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) {
745
0
                if (n1 == n2) {
746
0
                    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i));
747
0
                    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, n1));
748
0
                    if (attr) {
749
0
                        VECTOR(mergeinto)[e1] = actedge;
750
0
                        VECTOR(mergeinto)[e2] = actedge;
751
0
                        actedge++;
752
0
                    }
753
0
                    STEPOUT();
754
0
                    STEPIN();
755
0
                } else if (n1 < n2) {
756
0
                    STEPOUT();
757
0
                } else { /* n2<n1 */
758
0
                    STEPIN();
759
0
                }
760
0
            }
761
0
        }
762
763
0
#undef STEPOUT
764
0
#undef STEPIN
765
766
0
        igraph_vector_int_destroy(&outadj);
767
0
        igraph_vector_int_destroy(&inadj);
768
0
        IGRAPH_FINALLY_CLEAN(2);
769
770
0
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
771
0
                                   no_of_nodes,
772
0
                                   IGRAPH_UNDIRECTED));
773
0
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
774
0
        igraph_vector_int_destroy(&edges);
775
0
        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
776
0
        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges*/ false); /* no edge attributes */
777
778
0
        if (attr) {
779
0
            igraph_fixed_vectorlist_t vl;
780
0
            IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, actedge));
781
0
            IGRAPH_FINALLY(igraph_fixed_vectorlist_destroy, &vl);
782
783
0
            IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.vecs, edge_comb));
784
785
0
            igraph_fixed_vectorlist_destroy(&vl);
786
0
            IGRAPH_FINALLY_CLEAN(1);
787
0
        }
788
789
0
        IGRAPH_FINALLY_CLEAN(2);
790
0
        igraph_destroy(graph);
791
0
        *graph = newgraph;
792
793
0
        if (attr) {
794
0
            igraph_vector_int_destroy(&mergeinto);
795
0
            IGRAPH_FINALLY_CLEAN(1);
796
0
        }
797
0
    }
798
799
1.08k
    return IGRAPH_SUCCESS;
800
1.08k
}
801
802
0
#define WEIGHT_OF(eid) (weights ? VECTOR(*weights)[eid] : 1)
803
804
/**
805
 * \function igraph_get_stochastic
806
 * \brief Stochastic adjacency matrix of a graph.
807
 *
808
 * Stochastic matrix of a graph. The stochastic matrix of a graph is
809
 * its adjacency matrix, normalized row-wise or column-wise, such that
810
 * the sum of each row (or column) is one.
811
 *
812
 * \param graph The input graph.
813
 * \param res Pointer to an initialized matrix, the result is stored here.
814
 *   It will be resized as needed.
815
 * \param column_wise Whether to normalize column-wise.
816
 * \return Error code.
817
 *
818
 * Time complexity: O(|V||V|), |V| is the number of vertices in the graph.
819
 *
820
 * \sa \ref igraph_get_stochastic_sparse(), the sparse version of this
821
 * function.
822
 */
823
824
igraph_error_t igraph_get_stochastic(
825
    const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t column_wise,
826
    const igraph_vector_t *weights
827
0
) {
828
0
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
829
0
    igraph_integer_t no_of_edges = igraph_ecount(graph);
830
0
    igraph_bool_t directed = igraph_is_directed(graph);
831
0
    igraph_integer_t i, from, to;
832
0
    igraph_vector_t sums;
833
0
    igraph_real_t sum;
834
835
0
    IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes));
836
0
    igraph_matrix_null(res);
837
838
0
    IGRAPH_VECTOR_INIT_FINALLY(&sums, no_of_nodes);
839
840
0
    if (directed) {
841
0
        IGRAPH_CHECK(igraph_strength(
842
0
            graph, &sums, igraph_vss_all(),
843
0
            column_wise ? IGRAPH_IN : IGRAPH_OUT,
844
0
            /* loops = */ true, weights
845
0
        ));
846
847
0
        for (i = 0; i < no_of_edges; i++) {
848
0
            from = IGRAPH_FROM(graph, i);
849
0
            to = IGRAPH_TO(graph, i);
850
0
            sum = VECTOR(sums)[column_wise ? to : from];
851
0
            MATRIX(*res, from, to) += WEIGHT_OF(i) / sum;
852
0
        }
853
0
    } else {
854
0
        IGRAPH_CHECK(igraph_strength(
855
0
            graph, &sums, igraph_vss_all(), IGRAPH_ALL,
856
0
            /* loops = */ true, weights
857
0
        ));
858
859
0
        for (i = 0; i < no_of_edges; i++) {
860
0
            from = IGRAPH_FROM(graph, i);
861
0
            to = IGRAPH_TO(graph, i);
862
0
            MATRIX(*res, from, to) += WEIGHT_OF(i) / VECTOR(sums)[column_wise ? to : from];
863
0
            MATRIX(*res, to, from) += WEIGHT_OF(i) / VECTOR(sums)[column_wise ? from: to];
864
0
        }
865
0
    }
866
867
0
    igraph_vector_destroy(&sums);
868
0
    IGRAPH_FINALLY_CLEAN(1);
869
870
0
    return IGRAPH_SUCCESS;
871
0
}
872
873
#undef WEIGHT_OF
874
875
/**
876
 * \function igraph_get_stochastic_sparse
877
 * \brief The stochastic adjacency matrix of a graph.
878
 *
879
 * Stochastic matrix of a graph. The stochastic matrix of a graph is
880
 * its adjacency matrix, normalized row-wise or column-wise, such that
881
 * the sum of each row (or column) is one.
882
 *
883
 * \param graph The input graph.
884
 * \param res Pointer to an \em initialized sparse matrix, the
885
 *    result is stored here. The matrix will be resized as needed.
886
 * \param column_wise Whether to normalize column-wise.
887
 * \return Error code.
888
 *
889
 * Time complexity: O(|V|+|E|), linear in the number of vertices and
890
 * edges.
891
 *
892
 * \sa \ref igraph_get_stochastic(), the dense version of this function.
893
 */
894
895
igraph_error_t igraph_get_stochastic_sparse(
896
    const igraph_t *graph, igraph_sparsemat_t *res, igraph_bool_t column_wise,
897
    const igraph_vector_t *weights
898
0
) {
899
0
    IGRAPH_CHECK(igraph_get_adjacency_sparse(graph, res, IGRAPH_GET_ADJACENCY_BOTH, weights, IGRAPH_LOOPS_TWICE));
900
901
0
    if (column_wise) {
902
0
        IGRAPH_CHECK(igraph_sparsemat_normalize_cols(res, /* allow_zeros = */ 0));
903
0
    } else {
904
0
        IGRAPH_CHECK(igraph_sparsemat_normalize_rows(res, /* allow_zeros = */ 0));
905
0
    }
906
907
0
    return IGRAPH_SUCCESS;
908
0
}
909
910
911
/**
912
 * \function igraph_get_stochastic_sparsemat
913
 * \brief Stochastic adjacency matrix of a graph (deprecated).
914
 *
915
 * This function is deprecated in favour of \ref igraph_get_stochastic_sparse(),
916
 * but does not work in an identical way. This function takes an \em uninitialized
917
 * \c igraph_sparsemat_t while \ref igraph_get_stochastic_sparse() takes
918
 * an already initialized one.
919
 *
920
 * \param graph The input graph.
921
 * \param res Pointer to an \em uninitialized sparse matrix, the
922
 *    result is stored here. The matrix will be resized as needed.
923
 * \param column_wise Whether to normalize column-wise. For undirected
924
 *    graphs this argument does not have any effect.
925
 * \return Error code.
926
 *
927
 * \deprecated-by igraph_get_stochastic_sparse 0.10.0
928
 */
929
930
igraph_error_t igraph_get_stochastic_sparsemat(const igraph_t *graph,
931
                                               igraph_sparsemat_t *res,
932
0
                                               igraph_bool_t column_wise) {
933
934
0
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
935
0
    igraph_integer_t no_of_edges = igraph_ecount(graph);
936
0
    igraph_integer_t nzmax = igraph_is_directed(graph) ? no_of_edges : 2*no_of_edges;
937
0
    IGRAPH_CHECK(igraph_sparsemat_init(res, no_of_nodes, no_of_nodes, nzmax));
938
0
    return igraph_get_stochastic_sparse(graph, res, column_wise, NULL);
939
0
}
940
941
942
/**
943
 * \ingroup conversion
944
 * \function igraph_to_prufer
945
 * \brief Converts a tree to its Pr&uuml;fer sequence.
946
 *
947
 * A Pr&uuml;fer sequence is a unique sequence of integers associated
948
 * with a labelled tree. A tree on n >= 2 vertices can be represented by a
949
 * sequence of n-2 integers, each between 0 and n-1 (inclusive).
950
 *
951
 * \param graph Pointer to an initialized graph object which
952
          must be a tree on n >= 2 vertices.
953
 * \param prufer A pointer to the integer vector that should hold the Pr&uuml;fer sequence;
954
                 the vector must be initialized and will be resized to n - 2.
955
 * \return Error code:
956
 *          \clist
957
 *          \cli IGRAPH_ENOMEM
958
 *             there is not enough memory to perform the operation.
959
 *          \cli IGRAPH_EINVAL
960
 *             the graph is not a tree or it is has less than vertices
961
 *          \endclist
962
 *
963
 * \sa \ref igraph_from_prufer()
964
 *
965
 */
966
0
igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* prufer) {
967
    /* For generating the Prüfer sequence, we enumerate the vertices u of the tree.
968
       We keep track of the degrees of all vertices, treating vertices
969
       of degree 0 as removed. We maintain the invariant that all leafs
970
       that are still contained in the tree are >= u.
971
       If u is a leaf, we remove it and add its unique neighbor to the Prüfer
972
       sequence. If the removal of u turns the neighbor into a leaf which is < u,
973
       we repeat the procedure for the new leaf and so on. */
974
0
    igraph_integer_t u;
975
0
    igraph_vector_int_t degrees;
976
0
    igraph_vector_int_t neighbors;
977
0
    igraph_integer_t prufer_index = 0;
978
0
    igraph_integer_t n = igraph_vcount(graph);
979
0
    igraph_bool_t is_tree = false;
980
981
0
    IGRAPH_CHECK(igraph_is_tree(graph, &is_tree, NULL, IGRAPH_ALL));
982
983
0
    if (!is_tree) {
984
0
        IGRAPH_ERROR("The graph must be a tree", IGRAPH_EINVAL);
985
0
    }
986
987
0
    if (n < 2) {
988
0
        IGRAPH_ERROR("The tree must have at least 2 vertices", IGRAPH_EINVAL);
989
0
    }
990
991
0
    IGRAPH_CHECK(igraph_vector_int_resize(prufer, n - 2));
992
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&degrees, n);
993
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 1);
994
995
0
    IGRAPH_CHECK(igraph_degree(graph, &degrees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS));
996
997
0
    for (u = 0; u < n; ++u) {
998
0
        igraph_integer_t degree = VECTOR(degrees)[u];
999
0
        igraph_integer_t leaf = u;
1000
1001
0
        while (degree == 1 && leaf <= u) {
1002
0
            igraph_integer_t neighbor = 0;
1003
0
            igraph_integer_t neighbor_count = 0;
1004
1005
0
            VECTOR(degrees)[leaf] = 0; /* mark leaf v as deleted */
1006
1007
0
            IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, leaf, IGRAPH_ALL));
1008
1009
            /* Find the unique remaining neighbor of the leaf */
1010
0
            neighbor_count = igraph_vector_int_size(&neighbors);
1011
0
            for (igraph_integer_t i = 0; i < neighbor_count; i++) {
1012
0
                neighbor = VECTOR(neighbors)[i];
1013
0
                if (VECTOR(degrees)[neighbor] > 0) {
1014
0
                    break;
1015
0
                }
1016
0
            }
1017
1018
            /* remember that we have removed the leaf */
1019
0
            VECTOR(degrees)[neighbor]--;
1020
0
            degree = VECTOR(degrees)[neighbor];
1021
1022
            /* Add the neighbor to the prufer sequence unless it is the last vertex
1023
            (i.e. degree == 0) */
1024
0
            if (degree > 0) {
1025
0
                VECTOR(*prufer)[prufer_index] = neighbor;
1026
0
                prufer_index++;
1027
0
            }
1028
0
            leaf = neighbor;
1029
0
        }
1030
0
    }
1031
1032
0
    igraph_vector_int_destroy(&degrees);
1033
0
    igraph_vector_int_destroy(&neighbors);
1034
0
    IGRAPH_FINALLY_CLEAN(2);
1035
1036
0
    return IGRAPH_SUCCESS;
1037
0
}