Coverage Report

Created: 2025-06-20 06:27

/src/igraph/src/io/leda.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   IGraph library.
3
   Copyright (C) 2005-2020  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_foreign.h"
23
24
#include "igraph_attributes.h"
25
#include "igraph_interface.h"
26
#include "igraph_iterators.h"
27
28
#include "graph/attributes.h"
29
30
#include <string.h>
31
32
#define CHECK(cmd) \
33
346k
    do { \
34
346k
        int ret=(cmd); \
35
346k
        if (ret<0) IGRAPH_ERROR("Writing LEDA format failed.", IGRAPH_EFILE); \
36
346k
    } while (0)
37
38
/**
39
 * \function igraph_write_graph_leda
40
 * \brief Write a graph in LEDA native graph format.
41
 *
42
 * This function writes a graph to an output stream in LEDA format.
43
 * See http://www.algorithmic-solutions.info/leda_guide/graphs/leda_native_graph_fileformat.html
44
 *
45
 * </para><para>
46
 * The support for the LEDA format is very basic at the moment; igraph
47
 * writes only the LEDA graph section which supports one selected vertex
48
 * and edge attribute and no layout information or visual attributes.
49
 *
50
 * \param graph The graph to write to the stream.
51
 * \param outstream The stream.
52
 * \param vertex_attr_name The name of the vertex attribute whose values
53
 *                         are to be stored in the output, or \c NULL if no
54
 *                         vertex attribute should be stored.
55
 * \param edge_attr_name   The name of the edge attribute whose values
56
 *                         are to be stored in the output, or \c NULL if no
57
 *                         edge attribute should be stored.
58
 * \return Error code.
59
 *
60
 * Time complexity: O(|V|+|E|), the number of vertices and edges in the
61
 * graph.
62
 */
63
64
igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream,
65
                                       const char *vertex_attr_name,
66
3.26k
                                       const char *edge_attr_name) {
67
3.26k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
68
3.26k
    igraph_integer_t no_of_edges = igraph_ecount(graph);
69
3.26k
    igraph_eit_t it;
70
3.26k
    igraph_integer_t i = 0;
71
3.26k
    igraph_attribute_type_t vertex_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED;
72
3.26k
    igraph_attribute_type_t edge_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED;
73
3.26k
    igraph_integer_t from, to, rev;
74
75
3.26k
    IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), &it));
76
3.26k
    IGRAPH_FINALLY(igraph_eit_destroy, &it);
77
78
    /* Check if we have the vertex attribute */
79
3.26k
    if (vertex_attr_name &&
80
3.26k
        !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vertex_attr_name)) {
81
2.80k
        IGRAPH_WARNINGF("The vertex attribute '%s' does not exist. No vertex values will be written.",
82
2.80k
                        vertex_attr_name);
83
2.80k
        vertex_attr_name = NULL;
84
2.80k
    }
85
3.26k
    if (vertex_attr_name) {
86
456
        IGRAPH_CHECK(igraph_i_attribute_get_type(graph, &vertex_attr_type,
87
456
                                                IGRAPH_ATTRIBUTE_VERTEX, vertex_attr_name));
88
456
        if (vertex_attr_type != IGRAPH_ATTRIBUTE_NUMERIC &&
89
456
            vertex_attr_type != IGRAPH_ATTRIBUTE_STRING &&
90
456
            vertex_attr_type != IGRAPH_ATTRIBUTE_BOOLEAN) {
91
0
            IGRAPH_WARNINGF("The vertex attribute '%s' is not numeric, string or boolean. "
92
0
                            "No vertex values will be written.",
93
0
                            vertex_attr_name);
94
0
            vertex_attr_name = NULL; vertex_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED;
95
0
        }
96
456
    }
97
98
    /* Check if we have the edge attribute */
99
3.26k
    if (edge_attr_name &&
100
3.26k
        !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, edge_attr_name)) {
101
3.01k
        IGRAPH_WARNINGF("The edge attribute '%s' does not exist. No edge values will be written.",
102
3.01k
                        edge_attr_name);
103
3.01k
        edge_attr_name = NULL;
104
3.01k
    }
105
3.26k
    if (edge_attr_name) {
106
251
        IGRAPH_CHECK(igraph_i_attribute_get_type(graph, &edge_attr_type,
107
251
                                                IGRAPH_ATTRIBUTE_EDGE, edge_attr_name));
108
251
        if (edge_attr_type != IGRAPH_ATTRIBUTE_NUMERIC &&
109
251
            edge_attr_type != IGRAPH_ATTRIBUTE_STRING &&
110
251
            edge_attr_type != IGRAPH_ATTRIBUTE_BOOLEAN) {
111
0
            IGRAPH_WARNINGF("The edge attribute '%s' is not numeric, string or boolean. "
112
0
                            "No edge values will be written.",
113
0
                            edge_attr_name);
114
0
            edge_attr_name = NULL; edge_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED;
115
0
        }
116
251
    }
117
118
    /* Start writing header */
119
3.26k
    CHECK(fprintf(outstream, "LEDA.GRAPH\n"));
120
121
3.26k
    switch (vertex_attr_type) {
122
17
    case IGRAPH_ATTRIBUTE_NUMERIC:
123
17
        CHECK(fprintf(outstream, "double\n"));
124
17
        break;
125
430
    case IGRAPH_ATTRIBUTE_STRING:
126
430
        CHECK(fprintf(outstream, "string\n"));
127
430
        break;
128
430
    case IGRAPH_ATTRIBUTE_BOOLEAN:
129
9
        CHECK(fprintf(outstream, "bool\n"));
130
9
        break;
131
2.80k
    default:
132
2.80k
        CHECK(fprintf(outstream, "void\n"));
133
3.26k
    }
134
135
3.26k
    switch (edge_attr_type) {
136
191
    case IGRAPH_ATTRIBUTE_NUMERIC:
137
191
        CHECK(fprintf(outstream, "double\n"));
138
191
        break;
139
191
    case IGRAPH_ATTRIBUTE_STRING:
140
59
        CHECK(fprintf(outstream, "string\n"));
141
59
        break;
142
59
    case IGRAPH_ATTRIBUTE_BOOLEAN:
143
1
        CHECK(fprintf(outstream, "bool\n"));
144
1
        break;
145
3.01k
    default:
146
3.01k
        CHECK(fprintf(outstream, "void\n"));
147
3.26k
    }
148
149
3.26k
    CHECK(fprintf(outstream, "%d\n", (igraph_is_directed(graph) ? -1 : -2)));
150
151
    /* Start writing vertices */
152
3.26k
    CHECK(fprintf(outstream, "# Vertices\n"));
153
3.26k
    CHECK(fprintf(outstream, "%" IGRAPH_PRId "\n", no_of_nodes));
154
155
3.26k
    if (vertex_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) {
156
        /* Vertices with numeric attributes */
157
17
        igraph_vector_t values;
158
159
17
        IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes);
160
17
        IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(
161
17
                         graph, vertex_attr_name, igraph_vss_all(), &values));
162
163
275
        for (i = 0; i < no_of_nodes; i++) {
164
258
            CHECK(fprintf(outstream, "|{"));
165
258
            CHECK(igraph_real_fprintf_precise(outstream, VECTOR(values)[i]));
166
258
            CHECK(fprintf(outstream, "}|\n"));
167
258
        }
168
169
17
        igraph_vector_destroy(&values);
170
17
        IGRAPH_FINALLY_CLEAN(1);
171
3.24k
    } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_STRING) {
172
        /* Vertices with string attributes */
173
430
        igraph_strvector_t values;
174
175
430
        IGRAPH_CHECK(igraph_strvector_init(&values, no_of_nodes));
176
430
        IGRAPH_FINALLY(igraph_strvector_destroy, &values);
177
178
430
        IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(
179
430
                         graph, vertex_attr_name, igraph_vss_all(), &values));
180
181
7.54k
        for (i = 0; i < no_of_nodes; i++) {
182
7.14k
            const char *str = igraph_strvector_get(&values, i);
183
7.14k
            if (strchr(str, '\n') != 0) {
184
38
                IGRAPH_ERROR("Vertex attribute values cannot contain newline characters.",
185
38
                             IGRAPH_EINVAL);
186
38
            }
187
7.11k
            CHECK(fprintf(outstream, "|{%s}|\n", str));
188
7.11k
        }
189
190
392
        igraph_strvector_destroy(&values);
191
392
        IGRAPH_FINALLY_CLEAN(1);
192
2.81k
    } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) {
193
        /* Vertices with boolean attributes */
194
9
        igraph_vector_bool_t values;
195
196
9
        IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, no_of_nodes);
197
9
        IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(
198
9
                         graph, vertex_attr_name, igraph_vss_all(), &values));
199
200
51
        for (i = 0; i < no_of_nodes; i++) {
201
42
            CHECK(fprintf(outstream, "|{%s|}\n", VECTOR(values)[i] ? "true" : "false"));
202
42
        }
203
204
9
        igraph_vector_bool_destroy(&values);
205
9
        IGRAPH_FINALLY_CLEAN(1);
206
2.80k
    } else {
207
        /* Vertices with no attributes */
208
25.8k
        for (i = 0; i < no_of_nodes; i++) {
209
23.0k
            CHECK(fprintf(outstream, "|{}|\n"));
210
23.0k
        }
211
2.80k
    }
212
213
3.22k
    CHECK(fprintf(outstream, "# Edges\n"));
214
3.22k
    CHECK(fprintf(outstream, "%" IGRAPH_PRId "\n", no_of_edges));
215
216
3.22k
    if (edge_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) {
217
        /* Edges with numeric attributes */
218
189
        igraph_vector_t values;
219
189
        IGRAPH_VECTOR_INIT_FINALLY(&values, no_of_nodes);
220
189
        IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(
221
189
                         graph, edge_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values));
222
51.2k
        while (!IGRAPH_EIT_END(it)) {
223
51.0k
            igraph_integer_t eid = IGRAPH_EIT_GET(it);
224
51.0k
            igraph_edge(graph, eid, &from, &to);
225
51.0k
            igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false);
226
51.0k
            if (rev == IGRAPH_EIT_GET(it)) {
227
2.55k
                rev = -1;
228
2.55k
            }
229
51.0k
            CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{",
230
51.0k
                          from + 1, to + 1,
231
51.0k
                          rev + 1));
232
51.0k
            CHECK(igraph_real_fprintf_precise(outstream, VECTOR(values)[eid]));
233
51.0k
            CHECK(fprintf(outstream, "}|\n"));
234
51.0k
            IGRAPH_EIT_NEXT(it);
235
51.0k
        }
236
189
        igraph_vector_destroy(&values);
237
189
        IGRAPH_FINALLY_CLEAN(1);
238
3.03k
    } else if (edge_attr_type == IGRAPH_ATTRIBUTE_STRING) {
239
        /* Edges with string attributes */
240
59
        igraph_strvector_t values;
241
59
        IGRAPH_CHECK(igraph_strvector_init(&values, no_of_nodes));
242
59
        IGRAPH_FINALLY(igraph_strvector_destroy, &values);
243
59
        IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(
244
59
                         graph, edge_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values));
245
52.2k
        while (!IGRAPH_EIT_END(it)) {
246
52.1k
            igraph_integer_t eid = IGRAPH_EIT_GET(it);
247
52.1k
            const char *str = igraph_strvector_get(&values, eid);
248
52.1k
            igraph_edge(graph, eid, &from, &to);
249
52.1k
            igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false);
250
52.1k
            if (rev == IGRAPH_EIT_GET(it)) {
251
800
                rev = -1;
252
800
            }
253
52.1k
            if (strchr(str, '\n') != 0) {
254
1
                IGRAPH_ERROR("Edge attribute values cannot contain newline characters.",
255
1
                             IGRAPH_EINVAL);
256
1
            }
257
52.1k
            CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{%s}|\n",
258
52.1k
                          from + 1, to + 1,
259
52.1k
                          rev + 1, str));
260
52.1k
            IGRAPH_EIT_NEXT(it);
261
52.1k
        }
262
58
        igraph_strvector_destroy(&values);
263
58
        IGRAPH_FINALLY_CLEAN(1);
264
2.97k
    } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) {
265
        /* Edges with boolean attributes */
266
8
        igraph_vector_bool_t values;
267
268
8
        IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, no_of_edges);
269
8
        IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(
270
8
                         graph, vertex_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values));
271
272
0
        while (!IGRAPH_EIT_END(it)) {
273
0
            igraph_integer_t eid = IGRAPH_EIT_GET(it);
274
0
            igraph_edge(graph, eid, &from, &to);
275
0
            igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false);
276
0
            if (rev == IGRAPH_EIT_GET(it)) {
277
0
                rev = -1;
278
0
            }
279
0
            CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{%s}|\n",
280
0
                          from + 1, to + 1,
281
0
                          rev + 1,
282
0
                          VECTOR(values)[eid] ? "true" : "false"));
283
0
            IGRAPH_EIT_NEXT(it);
284
0
        }
285
286
0
        igraph_vector_bool_destroy(&values);
287
0
        IGRAPH_FINALLY_CLEAN(1);
288
2.96k
    } else {
289
        /* Edges with no attributes */
290
86.6k
        while (!IGRAPH_EIT_END(it)) {
291
83.6k
            igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to);
292
83.6k
            igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false);
293
83.6k
            if (rev == IGRAPH_EIT_GET(it)) {
294
16.7k
                rev = -1;
295
16.7k
            }
296
83.6k
            CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{}|\n",
297
83.6k
                          from + 1, to + 1,
298
83.6k
                          rev + 1));
299
83.6k
            IGRAPH_EIT_NEXT(it);
300
83.6k
        }
301
2.96k
    }
302
303
3.21k
    igraph_eit_destroy(&it);
304
3.21k
    IGRAPH_FINALLY_CLEAN(1);
305
306
3.21k
    return IGRAPH_SUCCESS;
307
3.22k
}
308
309
#undef CHECK