Coverage Report

Created: 2025-11-14 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/internal/utils.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2023  The igraph development team <igraph@igraph.org>
4
5
   This program is free software; you can redistribute it and/or modify
6
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; either version 2 of the License, or
8
   (at your option) any later version.
9
10
   This program is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
   GNU General Public License for more details.
14
15
   You should have received a copy of the GNU General Public License
16
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
*/
18
19
#include "igraph_interface.h"
20
#include "igraph_qsort.h"
21
#include "igraph_random.h"
22
23
#include "internal/utils.h"
24
25
/**
26
 * \function igraph_i_matrix_subset_vertices
27
 * \brief Subsets a matrix whose rows/columns correspond to graph vertices.
28
 *
29
 * This is a convenience function to subset a matrix computed from a graph.
30
 * It takes a matrix whose rows and columns correspond to the vertices
31
 * of a graph, and subsets it in-place to retain only some of the vertices.
32
 *
33
 * \param m A square matrix with the same number of rows/columns as the vertex
34
 *    count of \p graph. It will be modified in-place, deleting rows \em not present
35
 *    in \p from and columns \em not present in \p to.
36
 * \param graph The corresponding graph. <code>m[u,v]</code> is assumed to contain
37
 *    a value associated with vertices \c u and \c v of \p graph, e.g. the graph
38
 *    distance between them, their similarity, etc.
39
 * \param from Vertex set, these rows of the matrix will be retained.
40
 * \param to Vertex set, these columns of the matrix will be retained.
41
 * \return Error code.
42
 *
43
 * Time complexity:
44
 * O(1) when taking all vertices,
45
 * O(|from|*|to|) otherwise where |from| and |to| denote the size
46
 * of the source and target vertex sets.
47
 */
48
igraph_error_t igraph_i_matrix_subset_vertices(
49
        igraph_matrix_t *m,
50
        const igraph_t *graph,
51
        igraph_vs_t from,
52
0
        igraph_vs_t to) {
53
54
    /* Assertion: the size of 'm' agrees with 'graph': */
55
56
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
57
0
    igraph_int_t ncol = igraph_matrix_ncol(m);
58
0
    igraph_int_t nrow = igraph_matrix_nrow(m);
59
60
0
    IGRAPH_ASSERT(nrow == no_of_nodes && nrow == ncol);
61
62
    /* When taking all vertices, nothing needs to be done: */
63
64
0
    if (igraph_vs_is_all(&from) && igraph_vs_is_all(&to)) {
65
0
        return IGRAPH_SUCCESS;
66
0
    }
67
68
    /* Otherwise, allocate a temporary matrix to copy the data into: */
69
70
0
    igraph_vit_t fromvit, tovit;
71
0
    igraph_matrix_t tmp;
72
73
0
    IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit));
74
0
    IGRAPH_FINALLY(igraph_vit_destroy, &fromvit);
75
76
0
    IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit));
77
0
    IGRAPH_FINALLY(igraph_vit_destroy, &tovit);
78
79
0
    IGRAPH_MATRIX_INIT_FINALLY(&tmp, IGRAPH_VIT_SIZE(fromvit), IGRAPH_VIT_SIZE(tovit));
80
81
0
    for (igraph_int_t j=0; ! IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit), j++) {
82
0
        igraph_int_t i;
83
0
        for (IGRAPH_VIT_RESET(fromvit), i=0; ! IGRAPH_VIT_END(fromvit); IGRAPH_VIT_NEXT(fromvit), i++) {
84
0
            MATRIX(tmp, i, j) = MATRIX(*m, IGRAPH_VIT_GET(fromvit), IGRAPH_VIT_GET(tovit));
85
0
        }
86
0
    }
87
88
    /* This is O(1) time */
89
0
    igraph_matrix_swap(m, &tmp);
90
91
0
    igraph_matrix_destroy(&tmp);
92
0
    igraph_vit_destroy(&tovit);
93
0
    igraph_vit_destroy(&fromvit);
94
0
    IGRAPH_FINALLY_CLEAN(3);
95
96
0
    return IGRAPH_SUCCESS;
97
0
}
98
99
100
/* Lexicographic edge comparator, used with igraph_qsort() in igraph_i_simplify_edge_list() */
101
0
static int edge_comparator(const void *a, const void *b) {
102
0
    igraph_int_t *A = (igraph_int_t *) a;
103
0
    igraph_int_t *B = (igraph_int_t *) b;
104
105
0
    if (A[0] < B[0]) {
106
0
        return -1;
107
0
    }
108
0
    if (A[0] > B[0]) {
109
0
        return  1;
110
0
    }
111
112
    /* first are equal */
113
0
    if (A[1] < B[1]) {
114
0
        return -1;
115
0
    }
116
0
    if (A[1] > B[1]) {
117
0
        return  1;
118
0
    }
119
120
    /* second are equal, so the edges must be equal */
121
0
    return 0;
122
0
}
123
124
/**
125
 * Simplify an edge list in-place. Edges may be reordered by this function.
126
 *
127
 * TODO: Refactor this to take the number of vertices as input and use linear-time radix sort.
128
 *
129
 * \param edges The edge list vector, as a consecutive list of pairs. It will be modified in-place.
130
 * \param self_loops Set to \c false to remove self-loops.
131
 * \param multi_edges Set to \c false to eliminate multi-edges.
132
 * \param directed Whether to treat edges as directed.
133
 */
134
void igraph_i_simplify_edge_list(
135
        igraph_vector_int_t *edges,
136
        igraph_bool_t remove_loops, igraph_bool_t remove_multiple,
137
0
        igraph_bool_t directed) {
138
139
0
    igraph_int_t size = igraph_vector_int_size(edges);
140
141
0
    if (size == 0 || (!remove_loops && !remove_multiple)) {
142
0
        return;
143
0
    }
144
145
    /* Canonicalize undirected edges. */
146
0
    if (!directed) {
147
0
        for (igraph_int_t i = 0; i < size; i += 2) {
148
0
            if (VECTOR(*edges)[i] > VECTOR(*edges)[i + 1]) {
149
0
                igraph_int_t temp = VECTOR(*edges)[i];
150
0
                VECTOR(*edges)[i] = VECTOR(*edges)[i + 1];
151
0
                VECTOR(*edges)[i + 1] = temp;
152
0
            }
153
0
        }
154
0
    }
155
156
    /* Sort edge list. Not needed if multi edges are allowed. */
157
0
    if (remove_multiple) {
158
0
        igraph_qsort(VECTOR(*edges), size / 2, 2 * sizeof(igraph_int_t),
159
0
                     &edge_comparator);
160
0
    }
161
162
    /* Remove self-loops and duplicate edges from the sorted edge list, in place.
163
     * i points to the current edge being examined, j points to the last edge copied. */
164
165
0
    igraph_int_t j = -2;
166
0
    for (igraph_int_t i = 0 ; i < size; i += 2) {
167
0
        if (remove_multiple &&
168
            /* If we've already copied some edges, */
169
0
            j != -2 &&
170
            /* and the current edge is equal to the previously copied one: */
171
0
            VECTOR(*edges)[i] == VECTOR(*edges)[j] &&
172
0
            VECTOR(*edges)[i + 1] == VECTOR(*edges)[j + 1])
173
0
        {
174
            /* This edge is a duplicate, and should be skipped */
175
0
            continue;
176
0
        }
177
178
0
        if (remove_loops &&
179
0
            VECTOR(*edges)[i] == VECTOR(*edges)[i + 1])
180
0
        {
181
            /* This edge is a self loop, and should be skipped */
182
0
            continue;
183
0
        }
184
185
0
        j += 2;
186
0
        VECTOR(*edges)[j]     = VECTOR(*edges)[i];
187
0
        VECTOR(*edges)[j + 1] = VECTOR(*edges)[i + 1];
188
0
    }
189
190
0
    igraph_vector_int_resize(edges, j + 2); /* shrinks */
191
0
}
192
193
/**
194
 * Shuffle a list of pairs, such as an edge list.
195
 *
196
 * \param pairs Vector of pairs, will be modified in-place.
197
 * \return Error code, when the input is not of even length.
198
 */
199
0
igraph_error_t igraph_i_vector_int_shuffle_pairs(igraph_vector_int_t *pairs) {
200
0
    igraph_int_t pair_count = igraph_vector_int_size(pairs);
201
202
0
    if (pair_count % 2 == 1) {
203
0
        IGRAPH_ERROR("A vector of pairs must have an even length.", IGRAPH_EINVAL);
204
0
    }
205
206
0
    pair_count /= 2;
207
0
    while (pair_count > 1) {
208
0
        igraph_int_t dummy, k;
209
210
0
        pair_count--;
211
0
        k = RNG_INTEGER(0, pair_count);
212
213
0
        dummy = VECTOR(*pairs)[pair_count * 2];
214
0
        VECTOR(*pairs)[pair_count * 2] = VECTOR(*pairs)[k * 2];
215
216
0
        VECTOR(*pairs)[k * 2] = dummy;
217
0
        dummy = VECTOR(*pairs)[pair_count * 2 + 1];
218
219
0
        VECTOR(*pairs)[pair_count * 2] = VECTOR(*pairs)[k * 2 + 1];
220
0
        VECTOR(*pairs)[k * 2 + 1] = dummy;
221
0
    }
222
223
0
    return IGRAPH_SUCCESS;
224
0
}