1 /* -*- mode: C -*- */
2 /*
3 IGraph library.
4 Copyright (C) 2005-2020 The igraph development team
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA
20
21 */
22
23 #include "igraph_foreign.h"
24
25 #include "igraph_attributes.h"
26 #include "igraph_interface.h"
27
28 #include "graph/attributes.h"
29
30 #include "lgl-header.h"
31
32 int igraph_lgl_yylex_init_extra (igraph_i_lgl_parsedata_t* user_defined,
33 void* scanner);
34 void igraph_lgl_yylex_destroy (void *scanner );
35 int igraph_lgl_yyparse (igraph_i_lgl_parsedata_t* context);
36 void igraph_lgl_yyset_in (FILE * in_str, void* yyscanner );
37
38 /**
39 * \ingroup loadsave
40 * \function igraph_read_graph_lgl
41 * \brief Reads a graph from an <code>.lgl</code> file
42 *
43 * </para><para>
44 * The <code>.lgl</code> format is used by the Large Graph
45 * Layout visualization software
46 * (http://lgl.sourceforge.net), it can
47 * describe undirected optionally weighted graphs. From the LGL
48 * manual:
49 *
50 * \blockquote <para>The second format is the LGL file format
51 * (<code>.lgl</code> file
52 * suffix). This is yet another graph file format that tries to be as
53 * stingy as possible with space, yet keeping the edge file in a human
54 * readable (not binary) format. The format itself is like the
55 * following:
56 * \verbatim # vertex1name
57 vertex2name [optionalWeight]
58 vertex3name [optionalWeight] \endverbatim
59 * Here, the first vertex of an edge is preceded with a pound sign
60 * '#'. Then each vertex that shares an edge with that vertex is
61 * listed one per line on subsequent lines.</para> \endblockquote
62 *
63 * </para><para>
64 * LGL cannot handle loop and multiple edges or directed graphs, but
65 * in \a igraph it is not an error to have multiple and loop edges.
66 * \param graph Pointer to an uninitialized graph object.
67 * \param instream A stream, it should be readable.
68 * \param names Logical value, if TRUE the symbolic names of the
69 * vertices will be added to the graph as a vertex attribute
70 * called \quote name\endquote.
71 * \param weights Whether to add the weights of the edges to the
72 * graph as an edge attribute called \quote weight\endquote.
73 * \c IGRAPH_ADD_WEIGHTS_YES adds the weights (even if they
74 * are not present in the file, in this case they are assumed
75 * to be zero). \c IGRAPH_ADD_WEIGHTS_NO does not add any
76 * edge attribute. \c IGRAPH_ADD_WEIGHTS_IF_PRESENT adds the
77 * attribute if and only if there is at least one explicit
78 * edge weight in the input file.
79 * \param directed Whether to create a directed graph. As this format
80 * was originally used only for undirected graphs there is no
81 * information in the file about the directedness of the graph.
82 * Set this parameter to \c IGRAPH_DIRECTED or \c
83 * IGRAPH_UNDIRECTED to create a directed or undirected graph.
84 * \return Error code:
85 * \c IGRAPH_PARSEERROR: if there is a
86 * problem reading the file, or the file is syntactically
87 * incorrect.
88 *
89 * Time complexity:
90 * O(|V|+|E|log(|V|)) if we neglect
91 * the time required by the parsing. As usual
92 * |V| is the number of vertices,
93 * while |E| is the number of edges.
94 *
95 * \sa \ref igraph_read_graph_ncol(), \ref igraph_write_graph_lgl()
96 *
97 * \example examples/simple/igraph_read_graph_lgl.c
98 */
igraph_read_graph_lgl(igraph_t * graph,FILE * instream,igraph_bool_t names,igraph_add_weights_t weights,igraph_bool_t directed)99 int igraph_read_graph_lgl(igraph_t *graph, FILE *instream,
100 igraph_bool_t names,
101 igraph_add_weights_t weights,
102 igraph_bool_t directed) {
103
104 igraph_vector_t edges = IGRAPH_VECTOR_NULL, ws = IGRAPH_VECTOR_NULL;
105 igraph_trie_t trie = IGRAPH_TRIE_NULL;
106 igraph_vector_ptr_t name, weight;
107 igraph_vector_ptr_t *pname = 0, *pweight = 0;
108 igraph_attribute_record_t namerec, weightrec;
109 const char *namestr = "name", *weightstr = "weight";
110 igraph_i_lgl_parsedata_t context;
111
112 IGRAPH_VECTOR_INIT_FINALLY(&ws, 0);
113 IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
114 IGRAPH_TRIE_INIT_FINALLY(&trie, names);
115
116 context.has_weights = 0;
117 context.vector = &edges;
118 context.weights = &ws;
119 context.trie = ≜
120 context.eof = 0;
121
122 igraph_lgl_yylex_init_extra(&context, &context.scanner);
123 IGRAPH_FINALLY(igraph_lgl_yylex_destroy, context.scanner);
124
125 igraph_lgl_yyset_in(instream, context.scanner);
126
127 if (igraph_lgl_yyparse(&context)) {
128 if (context.errmsg[0] != 0) {
129 IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR);
130 } else {
131 IGRAPH_ERROR("Cannot read LGL file", IGRAPH_PARSEERROR);
132 }
133 }
134
135 IGRAPH_CHECK(igraph_empty(graph, 0, directed));
136 IGRAPH_FINALLY(igraph_destroy, graph);
137
138 if (names) {
139 const igraph_strvector_t *namevec;
140 IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1));
141 IGRAPH_FINALLY(igraph_vector_ptr_destroy, &name);
142 pname = &name;
143 igraph_trie_getkeys(&trie, &namevec); /* dirty */
144 namerec.name = namestr;
145 namerec.type = IGRAPH_ATTRIBUTE_STRING;
146 namerec.value = namevec;
147 VECTOR(name)[0] = &namerec;
148 }
149
150 if (weights == IGRAPH_ADD_WEIGHTS_YES ||
151 (weights == IGRAPH_ADD_WEIGHTS_IF_PRESENT && context.has_weights)) {
152 IGRAPH_CHECK(igraph_vector_ptr_init(&weight, 1));
153 IGRAPH_FINALLY(igraph_vector_ptr_destroy, &weight);
154 pweight = &weight;
155 weightrec.name = weightstr;
156 weightrec.type = IGRAPH_ATTRIBUTE_NUMERIC;
157 weightrec.value = &ws;
158 VECTOR(weight)[0] = &weightrec;
159 }
160
161 IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t)
162 igraph_trie_size(&trie), pname));
163 IGRAPH_CHECK(igraph_add_edges(graph, &edges, pweight));
164
165 if (pweight) {
166 igraph_vector_ptr_destroy(pweight);
167 IGRAPH_FINALLY_CLEAN(1);
168 }
169 if (pname) {
170 igraph_vector_ptr_destroy(pname);
171 IGRAPH_FINALLY_CLEAN(1);
172 }
173 igraph_trie_destroy(&trie);
174 igraph_vector_destroy(&edges);
175 igraph_vector_destroy(&ws);
176 igraph_lgl_yylex_destroy(context.scanner);
177 IGRAPH_FINALLY_CLEAN(5);
178
179 return 0;
180 }
181
182 /**
183 * \ingroup loadsave
184 * \function igraph_write_graph_lgl
185 * \brief Writes the graph to a file in <code>.lgl</code> format
186 *
187 * </para><para>
188 * <code>.lgl</code> is a format used by LGL, see \ref
189 * igraph_read_graph_lgl() for details.
190 *
191 * </para><para>
192 * Note that having multiple or loop edges in an
193 * <code>.lgl</code> file breaks the LGL software but \a igraph
194 * does not check for this condition.
195 * \param graph The graph to write.
196 * \param outstream The stream object to write to, it should be
197 * writable.
198 * \param names The name of the vertex attribute, if symbolic names
199 * are written to the file. If not supply 0 here.
200 * \param weights The name of the edge attribute, if they are also
201 * written to the file. If you don't want weights supply 0
202 * here.
203 * \param isolates Logical, if TRUE isolated vertices are also written
204 * to the file. If FALSE they will be omitted.
205 * \return Error code:
206 * \c IGRAPH_EFILE if there is an error
207 * writing the file.
208 *
209 * Time complexity: O(|E|), the
210 * number of edges if \p isolates is
211 * FALSE, O(|V|+|E|) otherwise. All
212 * file operations are expected to have time complexity
213 * O(1).
214 *
215 * \sa \ref igraph_read_graph_lgl(), \ref igraph_write_graph_ncol()
216 *
217 * \example examples/simple/igraph_write_graph_lgl.c
218 */
igraph_write_graph_lgl(const igraph_t * graph,FILE * outstream,const char * names,const char * weights,igraph_bool_t isolates)219 int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream,
220 const char *names, const char *weights,
221 igraph_bool_t isolates) {
222 igraph_eit_t it;
223 long int actvertex = -1;
224 igraph_attribute_type_t nametype, weighttype;
225
226 IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM),
227 &it));
228 IGRAPH_FINALLY(igraph_eit_destroy, &it);
229
230 /* Check if we have the names attribute */
231 if (names && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX,
232 names)) {
233 names = 0;
234 IGRAPH_WARNING("names attribute does not exists");
235 }
236 if (names) {
237 IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &nametype,
238 IGRAPH_ATTRIBUTE_VERTEX, names));
239 if (nametype != IGRAPH_ATTRIBUTE_NUMERIC && nametype != IGRAPH_ATTRIBUTE_STRING) {
240 IGRAPH_WARNING("ignoring names attribute, unknown attribute type");
241 names = 0;
242 }
243 }
244
245 /* Check the weights as well */
246 if (weights && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE,
247 weights)) {
248 weights = 0;
249 IGRAPH_WARNING("weights attribute does not exists");
250 }
251 if (weights) {
252 IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &weighttype,
253 IGRAPH_ATTRIBUTE_EDGE, weights));
254 if (weighttype != IGRAPH_ATTRIBUTE_NUMERIC && weighttype != IGRAPH_ATTRIBUTE_STRING) {
255 IGRAPH_WARNING("ignoring weights attribute, unknown attribute type");
256 weights = 0;
257 }
258 }
259
260 if (names == 0 && weights == 0) {
261 /* No names, no weights */
262 while (!IGRAPH_EIT_END(it)) {
263 igraph_integer_t from, to;
264 int ret;
265 igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to);
266 if (from == actvertex) {
267 ret = fprintf(outstream, "%li\n", (long int)to);
268 } else {
269 actvertex = from;
270 ret = fprintf(outstream, "# %li\n%li\n", (long int)from, (long int)to);
271 }
272 if (ret < 0) {
273 IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
274 }
275 IGRAPH_EIT_NEXT(it);
276 }
277 } else if (weights == 0) {
278 /* No weights but use names */
279 igraph_strvector_t nvec;
280 IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph)));
281 IGRAPH_FINALLY(igraph_strvector_destroy, &nvec);
282 IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names,
283 igraph_vss_all(),
284 &nvec));
285 while (!IGRAPH_EIT_END(it)) {
286 igraph_integer_t edge = IGRAPH_EIT_GET(it);
287 igraph_integer_t from, to;
288 int ret = 0;
289 char *str1, *str2;
290 igraph_edge(graph, edge, &from, &to);
291 igraph_strvector_get(&nvec, to, &str2);
292
293 if (from == actvertex) {
294 ret = fprintf(outstream, "%s\n", str2);
295 } else {
296 actvertex = from;
297 igraph_strvector_get(&nvec, from, &str1);
298 ret = fprintf(outstream, "# %s\n%s\n", str1, str2);
299 }
300 if (ret < 0) {
301 IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
302 }
303 IGRAPH_EIT_NEXT(it);
304 }
305 IGRAPH_FINALLY_CLEAN(1);
306 } else if (names == 0) {
307 igraph_strvector_t wvec;
308 IGRAPH_CHECK(igraph_strvector_init(&wvec, igraph_ecount(graph)));
309 IGRAPH_FINALLY(igraph_strvector_destroy, &wvec);
310 IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, weights,
311 igraph_ess_all(IGRAPH_EDGEORDER_ID),
312 &wvec));
313 /* No names but weights */
314 while (!IGRAPH_EIT_END(it)) {
315 igraph_integer_t edge = IGRAPH_EIT_GET(it);
316 igraph_integer_t from, to;
317 int ret = 0;
318 char *str1;
319 igraph_edge(graph, edge, &from, &to);
320 igraph_strvector_get(&wvec, edge, &str1);
321 if (from == actvertex) {
322 ret = fprintf(outstream, "%li %s\n", (long)to, str1);
323 } else {
324 actvertex = from;
325 ret = fprintf(outstream, "# %li\n%li %s\n", (long)from, (long)to, str1);
326 }
327 if (ret < 0) {
328 IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
329 }
330 IGRAPH_EIT_NEXT(it);
331 }
332 igraph_strvector_destroy(&wvec);
333 IGRAPH_FINALLY_CLEAN(1);
334 } else {
335 /* Both names and weights */
336 igraph_strvector_t nvec, wvec;
337 IGRAPH_CHECK(igraph_strvector_init(&wvec, igraph_ecount(graph)));
338 IGRAPH_FINALLY(igraph_strvector_destroy, &wvec);
339 IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph)));
340 IGRAPH_FINALLY(igraph_strvector_destroy, &nvec);
341 IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, weights,
342 igraph_ess_all(IGRAPH_EDGEORDER_ID),
343 &wvec));
344 IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names,
345 igraph_vss_all(),
346 &nvec));
347 while (!IGRAPH_EIT_END(it)) {
348 igraph_integer_t edge = IGRAPH_EIT_GET(it);
349 igraph_integer_t from, to;
350 int ret = 0;
351 char *str1, *str2, *str3;
352 igraph_edge(graph, edge, &from, &to);
353 igraph_strvector_get(&nvec, to, &str2);
354 igraph_strvector_get(&wvec, edge, &str3);
355 if (from == actvertex) {
356 ret = fprintf(outstream, "%s ", str2);
357 } else {
358 actvertex = from;
359 igraph_strvector_get(&nvec, from, &str1);
360 ret = fprintf(outstream, "# %s\n%s ", str1, str2);
361 }
362 if (ret < 0) {
363 IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
364 }
365 ret = fprintf(outstream, "%s\n", str3);
366 if (ret < 0) {
367 IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
368 }
369 IGRAPH_EIT_NEXT(it);
370 }
371 igraph_strvector_destroy(&nvec);
372 igraph_strvector_destroy(&wvec);
373 IGRAPH_FINALLY_CLEAN(2);
374 }
375
376 if (isolates) {
377 long int nov = igraph_vcount(graph);
378 long int i;
379 int ret = 0;
380 igraph_vector_t deg;
381 igraph_strvector_t nvec;
382 char *str;
383
384 IGRAPH_VECTOR_INIT_FINALLY(°, 1);
385 IGRAPH_CHECK(igraph_strvector_init(&nvec, 1));
386 IGRAPH_FINALLY(igraph_strvector_destroy, &nvec);
387 for (i = 0; i < nov; i++) {
388 igraph_degree(graph, °, igraph_vss_1((igraph_integer_t) i),
389 IGRAPH_ALL, IGRAPH_LOOPS);
390 if (VECTOR(deg)[0] == 0) {
391 if (names == 0) {
392 ret = fprintf(outstream, "# %li\n", i);
393 } else {
394 IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, names,
395 igraph_vss_1((igraph_integer_t) i), &nvec));
396 igraph_strvector_get(&nvec, 0, &str);
397 ret = fprintf(outstream, "# %s\n", str);
398 }
399 }
400 if (ret < 0) {
401 IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
402 }
403 }
404 igraph_strvector_destroy(&nvec);
405 igraph_vector_destroy(°);
406 IGRAPH_FINALLY_CLEAN(2);
407 }
408
409 igraph_eit_destroy(&it);
410 IGRAPH_FINALLY_CLEAN(1);
411 return 0;
412 }
413