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 = &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(&deg, 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, &deg, 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(&deg);
406         IGRAPH_FINALLY_CLEAN(2);
407     }
408 
409     igraph_eit_destroy(&it);
410     IGRAPH_FINALLY_CLEAN(1);
411     return 0;
412 }
413