/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 |