Coverage Report

Created: 2025-11-10 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/properties/loops.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2005-2021 The igraph development team
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, write to the Free Software
17
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
   02110-1301 USA
19
20
*/
21
22
#include "igraph_structural.h"
23
24
#include "igraph_interface.h"
25
26
/**
27
 * \function igraph_has_loop
28
 * \brief Returns whether the graph has at least one loop edge.
29
 *
30
 * </para><para>
31
 * A loop edge is an edge from a vertex to itself.
32
 *
33
 * </para><para>
34
 * The return value of this function is cached in the graph itself; calling
35
 * the function multiple times with no modifications to the graph in between
36
 * will return a cached value in O(1) time.
37
 *
38
 * \param graph The input graph.
39
 * \param res Pointer to an initialized boolean vector for storing the result.
40
 * \return Error code.
41
 *
42
 * \sa \ref igraph_simplify() to get rid of loop edges.
43
 *
44
 * Time complexity: O(e), the number of edges to check.
45
 *
46
 * \example examples/simple/igraph_is_loop.c
47
 */
48
536
igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) {
49
536
    igraph_int_t i, m = igraph_ecount(graph);
50
51
536
    IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_HAS_LOOP, res);
52
53
536
    *res = false;
54
55
13.2k
    for (i = 0; i < m; i++) {
56
12.9k
        if (IGRAPH_FROM(graph, i) == IGRAPH_TO(graph, i)) {
57
224
            *res = true;
58
224
            break;
59
224
        }
60
12.9k
    }
61
62
536
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, *res);
63
64
536
    return IGRAPH_SUCCESS;
65
536
}
66
67
/**
68
 * \function igraph_is_loop
69
 * \brief Find the loop edges in a graph.
70
 *
71
 * A loop edge, also called a self-loop, is an edge from a vertex to itself.
72
 *
73
 * \param graph The input graph.
74
 * \param res Pointer to an initialized boolean vector for storing the result,
75
 *         it will be resized as needed.
76
 * \param es The edges to check, for all edges supply \ref igraph_ess_all() here.
77
 * \return Error code.
78
 *
79
 * \sa \ref igraph_simplify() to get rid of loop edges.
80
 *
81
 * Time complexity: O(e), the number of edges to check.
82
 *
83
 * \example examples/simple/igraph_is_loop.c
84
 */
85
igraph_error_t igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res,
86
0
                              igraph_es_t es) {
87
0
    igraph_eit_t eit;
88
0
    igraph_bool_t found_loop = false;
89
90
0
    IGRAPH_CHECK(igraph_eit_create(graph, es, &eit));
91
0
    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
92
93
0
    IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit)));
94
95
0
    if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) &&
96
0
        ! igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) {
97
0
        igraph_vector_bool_null(res);
98
0
        goto done;
99
0
    }
100
101
0
    for (igraph_int_t i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) {
102
0
        igraph_int_t e = IGRAPH_EIT_GET(eit);
103
0
        igraph_bool_t is_loop = (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e));
104
0
        VECTOR(*res)[i] = is_loop;
105
0
        if (is_loop) {
106
0
            found_loop = true;
107
0
        }
108
0
    }
109
110
0
    if (found_loop) {
111
0
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, true);
112
0
    } else if (igraph_es_is_all(&es)) {
113
0
        igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, false);
114
0
    }
115
116
0
done:
117
0
    igraph_eit_destroy(&eit);
118
0
    IGRAPH_FINALLY_CLEAN(1);
119
120
0
    return IGRAPH_SUCCESS;
121
0
}
122
123
/**
124
 * \function igraph_count_loops
125
 * \brief Counts the self-loops in the graph.
126
 *
127
 * Counts loop edges, i.e. edges whose two endpoints coincide.
128
 *
129
 * \param graph The input graph.
130
 * \param loop_count Pointer to an integer, the number of self-loops will be stored here.
131
 * \return Error code.
132
 *
133
 * Time complexity: O(|E|), linear in the number of edges.
134
 *
135
 * \example examples/simple/igraph_is_loop.c
136
 */
137
0
igraph_error_t igraph_count_loops(const igraph_t *graph, igraph_int_t *loop_count) {
138
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
139
0
    igraph_int_t count;
140
141
    /* Nothing to do if we know that there are no loops. */
142
0
    if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) &&
143
0
        !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) {
144
0
        *loop_count = 0;
145
0
        return IGRAPH_SUCCESS;
146
0
    }
147
148
0
    count = 0;
149
0
    for (igraph_int_t e=0; e < no_of_edges; e++) {
150
0
        if (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e)) {
151
0
            count++;
152
0
        }
153
0
    }
154
155
    /* We already checked for loops, so take the opportunity to set the cache. */
156
0
    igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, count > 0);
157
158
0
    *loop_count = count;
159
160
0
    return IGRAPH_SUCCESS;
161
0
}