Coverage Report

Created: 2024-02-11 06:20

/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
78.9k
igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *res, igraph_bool_t bycol) {
335
336
78.9k
    igraph_eit_t edgeit;
337
78.9k
    igraph_integer_t no_of_edges = igraph_ecount(graph);
338
78.9k
    igraph_integer_t vptr = 0;
339
78.9k
    igraph_integer_t from, to;
340
341
78.9k
    IGRAPH_CHECK(igraph_vector_int_resize(res, no_of_edges * 2));
342
78.9k
    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID),
343
78.9k
                                   &edgeit));
344
78.9k
    IGRAPH_FINALLY(igraph_eit_destroy, &edgeit);
345
346
78.9k
    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
78.9k
    } else {
355
280M
        while (!IGRAPH_EIT_END(edgeit)) {
356
280M
            igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &from, &to);
357
280M
            VECTOR(*res)[vptr++] = from;
358
280M
            VECTOR(*res)[vptr++] = to;
359
280M
            IGRAPH_EIT_NEXT(edgeit);
360
280M
        }
361
78.9k
    }
362
363
78.9k
    igraph_eit_destroy(&edgeit);
364
78.9k
    IGRAPH_FINALLY_CLEAN(1);
365
78.9k
    return IGRAPH_SUCCESS;
366
78.9k
}
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
789
                       igraph_to_directed_t mode) {
401
789
    igraph_integer_t no_of_edges = igraph_ecount(graph);
402
789
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
403
404
789
    if (igraph_is_directed(graph)) {
405
0
        return IGRAPH_SUCCESS;
406
0
    }
407
408
789
    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
789
    case IGRAPH_TO_DIRECTED_MUTUAL:
464
789
      {
465
789
        igraph_t newgraph;
466
789
        igraph_vector_int_t edges;
467
789
        igraph_vector_int_t index;
468
789
        igraph_integer_t size;
469
470
789
        IGRAPH_SAFE_MULT(no_of_edges, 4, &size);
471
789
        IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0);
472
789
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size));
473
789
        IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
474
789
        IGRAPH_CHECK(igraph_vector_int_resize(&edges, size));
475
789
        IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges * 2);
476
17.7k
        for (igraph_integer_t i = 0; i < no_of_edges; i++) {
477
16.9k
            VECTOR(edges)[no_of_edges * 2 + i * 2]  = VECTOR(edges)[i * 2 + 1];
478
16.9k
            VECTOR(edges)[no_of_edges * 2 + i * 2 + 1] = VECTOR(edges)[i * 2];
479
16.9k
            VECTOR(index)[i] = VECTOR(index)[no_of_edges + i] = i;
480
16.9k
        }
481
482
789
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
483
789
                                   no_of_nodes,
484
789
                                   IGRAPH_DIRECTED));
485
789
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
486
789
        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
487
789
        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges=*/false);
488
789
        IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &index));
489
490
789
        igraph_vector_int_destroy(&index);
491
789
        igraph_vector_int_destroy(&edges);
492
789
        IGRAPH_FINALLY_CLEAN(3);
493
494
789
        igraph_destroy(graph);
495
789
        *graph = newgraph;
496
497
789
        break;
498
789
      }
499
0
    default:
500
0
        IGRAPH_ERROR("Cannot direct graph, invalid mode.", IGRAPH_EINVAL);
501
789
    }
502
503
789
    return IGRAPH_SUCCESS;
504
789
}
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.05k
                         const igraph_attribute_combination_t *edge_comb) {
542
543
1.05k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
544
1.05k
    igraph_integer_t no_of_edges = igraph_ecount(graph);
545
1.05k
    igraph_vector_int_t edges;
546
1.05k
    igraph_t newgraph;
547
1.05k
    igraph_bool_t attr = edge_comb && igraph_has_attribute_table();
548
549
1.05k
    if (mode != IGRAPH_TO_UNDIRECTED_EACH &&
550
1.05k
        mode != IGRAPH_TO_UNDIRECTED_COLLAPSE &&
551
1.05k
        mode != IGRAPH_TO_UNDIRECTED_MUTUAL) {
552
0
        IGRAPH_ERROR("Cannot undirect graph, invalid mode", IGRAPH_EINVAL);
553
0
    }
554
555
1.05k
    if (!igraph_is_directed(graph)) {
556
0
        return IGRAPH_SUCCESS;
557
0
    }
558
559
1.05k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0);
560
561
1.05k
    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.05k
    } else if (mode == IGRAPH_TO_UNDIRECTED_COLLAPSE) {
594
1.05k
        igraph_vector_int_t inadj, outadj;
595
1.05k
        igraph_vector_int_t mergeinto;
596
1.05k
        igraph_integer_t actedge = 0;
597
598
1.05k
        if (attr) {
599
0
            IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges);
600
0
        }
601
602
1.05k
        IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2));
603
1.05k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0);
604
1.05k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&outadj, 0);
605
606
81.3k
        for (igraph_integer_t i = 0; i < no_of_nodes; i++) {
607
80.3k
            igraph_integer_t n_out, n_in;
608
80.3k
            igraph_integer_t p1 = -1, p2 = -1;
609
80.3k
            igraph_integer_t e1 = 0, e2 = 0, n1 = 0, n2 = 0, last;
610
80.3k
            IGRAPH_CHECK(igraph_incident(graph, &outadj, i, IGRAPH_OUT));
611
80.3k
            IGRAPH_CHECK(igraph_incident(graph, &inadj, i, IGRAPH_IN));
612
80.3k
            n_out = igraph_vector_int_size(&outadj);
613
80.3k
            n_in = igraph_vector_int_size(&inadj);
614
615
1.05M
#define STEPOUT() if ( (++p1) < n_out) {    \
616
999k
        e1 = VECTOR(outadj)[p1]; \
617
999k
        n1 = IGRAPH_TO(graph, e1);      \
618
999k
    }
619
1.06M
#define STEPIN()  if ( (++p2) < n_in) {         \
620
1.01M
        e2 = VECTOR(inadj )[p2]; \
621
1.01M
        n2 = IGRAPH_FROM(graph, e2);        \
622
1.01M
    }
623
261k
#define ADD_NEW_EDGE() { \
624
261k
    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); \
625
261k
    IGRAPH_CHECK(igraph_vector_int_push_back(&edges, last)); \
626
261k
}
627
1.96M
#define MERGE_INTO_CURRENT_EDGE(which) { \
628
1.96M
    if (attr) { \
629
0
        VECTOR(mergeinto)[which] = actedge; \
630
0
    } \
631
1.96M
}
632
633
80.3k
            STEPOUT();
634
80.3k
            STEPIN();
635
636
304k
            while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) {
637
224k
                last = (n1 <= n2) ? n1 : n2;
638
224k
                ADD_NEW_EDGE();
639
1.11M
                while (p1 < n_out && last == n1) {
640
887k
                    MERGE_INTO_CURRENT_EDGE(e1);
641
887k
                    STEPOUT();
642
887k
                }
643
1.13M
                while (p2 < n_in && last == n2) {
644
911k
                    MERGE_INTO_CURRENT_EDGE(e2);
645
911k
                    STEPIN();
646
911k
                }
647
224k
                actedge++;
648
224k
            }
649
650
98.5k
            while (p1 < n_out && n1 <= i) {
651
18.2k
                last = n1;
652
18.2k
                ADD_NEW_EDGE();
653
106k
                while (p1 < n_out && last == n1) {
654
87.8k
                    MERGE_INTO_CURRENT_EDGE(e1);
655
87.8k
                    STEPOUT();
656
87.8k
                }
657
18.2k
                actedge++;
658
18.2k
            }
659
660
99.3k
            while (p2 < n_in && n2 <= i) {
661
18.9k
                last = n2;
662
18.9k
                ADD_NEW_EDGE();
663
96.7k
                while (p2 < n_in && last == n2) {
664
77.7k
                    MERGE_INTO_CURRENT_EDGE(e2);
665
77.7k
                    STEPIN();
666
77.7k
                }
667
18.9k
                actedge++;
668
18.9k
            }
669
80.3k
        }
670
671
1.05k
#undef MERGE_INTO_CURRENT_EDGE
672
1.05k
#undef ADD_NEW_EDGE
673
1.05k
#undef STEPOUT
674
1.05k
#undef STEPIN
675
676
1.05k
        igraph_vector_int_destroy(&outadj);
677
1.05k
        igraph_vector_int_destroy(&inadj);
678
1.05k
        IGRAPH_FINALLY_CLEAN(2);
679
680
1.05k
        IGRAPH_CHECK(igraph_create(&newgraph, &edges,
681
1.05k
                                   no_of_nodes,
682
1.05k
                                   IGRAPH_UNDIRECTED));
683
1.05k
        IGRAPH_FINALLY(igraph_destroy, &newgraph);
684
1.05k
        igraph_vector_int_destroy(&edges);
685
1.05k
        IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
686
1.05k
        IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges*/ false); /* no edge attributes */
687
688
1.05k
        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.05k
        IGRAPH_FINALLY_CLEAN(2);
700
1.05k
        igraph_destroy(graph);
701
1.05k
        *graph = newgraph;
702
703
1.05k
        if (attr) {
704
0
            igraph_vector_int_destroy(&mergeinto);
705
0
            IGRAPH_FINALLY_CLEAN(1);
706
0
        }
707
1.05k
    } 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.05k
    return IGRAPH_SUCCESS;
800
1.05k
}
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 = */ false));
903
0
    } else {
904
0
        IGRAPH_CHECK(igraph_sparsemat_normalize_rows(res, /* allow_zeros = */ false));
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
106
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
106
    igraph_integer_t u;
975
106
    igraph_vector_int_t degrees;
976
106
    igraph_vector_int_t neighbors;
977
106
    igraph_integer_t prufer_index = 0;
978
106
    igraph_integer_t n = igraph_vcount(graph);
979
106
    igraph_bool_t is_tree = false;
980
981
106
    IGRAPH_CHECK(igraph_is_tree(graph, &is_tree, NULL, IGRAPH_ALL));
982
983
106
    if (!is_tree) {
984
0
        IGRAPH_ERROR("The graph must be a tree", IGRAPH_EINVAL);
985
0
    }
986
987
106
    if (n < 2) {
988
0
        IGRAPH_ERROR("The tree must have at least 2 vertices", IGRAPH_EINVAL);
989
0
    }
990
991
106
    IGRAPH_CHECK(igraph_vector_int_resize(prufer, n - 2));
992
106
    IGRAPH_VECTOR_INT_INIT_FINALLY(&degrees, n);
993
106
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 1);
994
995
106
    IGRAPH_CHECK(igraph_degree(graph, &degrees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS));
996
997
905
    for (u = 0; u < n; ++u) {
998
799
        igraph_integer_t degree = VECTOR(degrees)[u];
999
799
        igraph_integer_t leaf = u;
1000
1001
1.49k
        while (degree == 1 && leaf <= u) {
1002
693
            igraph_integer_t neighbor = 0;
1003
693
            igraph_integer_t neighbor_count = 0;
1004
1005
693
            VECTOR(degrees)[leaf] = 0; /* mark leaf v as deleted */
1006
1007
693
            IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, leaf, IGRAPH_ALL));
1008
1009
            /* Find the unique remaining neighbor of the leaf */
1010
693
            neighbor_count = igraph_vector_int_size(&neighbors);
1011
1.10k
            for (igraph_integer_t i = 0; i < neighbor_count; i++) {
1012
1.10k
                neighbor = VECTOR(neighbors)[i];
1013
1.10k
                if (VECTOR(degrees)[neighbor] > 0) {
1014
693
                    break;
1015
693
                }
1016
1.10k
            }
1017
1018
            /* remember that we have removed the leaf */
1019
693
            VECTOR(degrees)[neighbor]--;
1020
693
            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
693
            if (degree > 0) {
1025
587
                VECTOR(*prufer)[prufer_index] = neighbor;
1026
587
                prufer_index++;
1027
587
            }
1028
693
            leaf = neighbor;
1029
693
        }
1030
799
    }
1031
1032
106
    igraph_vector_int_destroy(&degrees);
1033
106
    igraph_vector_int_destroy(&neighbors);
1034
106
    IGRAPH_FINALLY_CLEAN(2);
1035
1036
106
    return IGRAPH_SUCCESS;
1037
106
}