1 /*
2    IGraph library.
3    Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
4    334 Harvard st, Cambridge, MA, 02138 USA
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 %{
24 
25 /*
26    IGraph library.
27    Copyright (C) 2006-2012  Gabor Csardi <csardi.gabor@gmail.com>
28    334 Harvard st, Cambridge, MA, 02138 USA
29 
30    This program is free software; you can redistribute it and/or modify
31    it under the terms of the GNU General Public License as published by
32    the Free Software Foundation; either version 2 of the License, or
33    (at your option) any later version.
34 
35    This program is distributed in the hope that it will be useful,
36    but WITHOUT ANY WARRANTY; without even the implied warranty of
37    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38    GNU General Public License for more details.
39 
40    You should have received a copy of the GNU General Public License
41    along with this program; if not, write to the Free Software
42    Foundation, Inc.,  51 Franklin Street, Fifth Floor, Boston, MA
43    02110-1301 USA
44 
45 */
46 
47 #include <stdio.h>
48 #include <string.h>
49 #include <math.h>
50 
51 #include "igraph_types.h"
52 #include "igraph_memory.h"
53 #include "igraph_error.h"
54 #include "igraph_attributes.h"
55 #include "config.h"
56 
57 #include "core/math.h"
58 #include "io/pajek-header.h"
59 #include "io/parsers/pajek-parser.h" /* it must come first because of YYSTYPE */
60 #include "io/parsers/pajek-lexer.h"
61 #include "internal/hacks.h"
62 
63 int igraph_pajek_yyerror(YYLTYPE* locp,
64                          igraph_i_pajek_parsedata_t *context,
65                          const char *s);
66 
67 int igraph_i_pajek_add_string_vertex_attribute(const char *name,
68                                                const char *value,
69                                                int len,
70                                                igraph_i_pajek_parsedata_t *context);
71 int igraph_i_pajek_add_string_edge_attribute(const char *name,
72                                              const char *value,
73                                              int len,
74                                              igraph_i_pajek_parsedata_t *context);
75 int igraph_i_pajek_add_numeric_vertex_attribute(const char *name,
76                                                 igraph_real_t value,
77                                                 igraph_i_pajek_parsedata_t *context);
78 int igraph_i_pajek_add_numeric_edge_attribute(const char *name,
79                                               igraph_real_t value,
80                                               igraph_i_pajek_parsedata_t *context);
81 int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names,
82                                          igraph_vector_ptr_t *attrs,
83                                          long int count,
84                                          const char *attrname,
85                                          igraph_integer_t vid,
86                                          igraph_real_t number);
87 int igraph_i_pajek_add_string_attribute(igraph_trie_t *names,
88                                         igraph_vector_ptr_t *attrs,
89                                         long int count,
90                                         const char *attrname,
91                                         igraph_integer_t vid,
92                                         const char *str);
93 
94 int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context);
95 int igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context);
96 
97 extern igraph_real_t igraph_pajek_get_number(const char *str, long int len);
98 extern long int igraph_i_pajek_actvertex;
99 extern long int igraph_i_pajek_actedge;
100 
101 #define scanner context->scanner
102 
103 %}
104 
105 %pure-parser
106 /* bison: do not remove the equals sign; macOS XCode ships with bison 2.3, which
107  * needs the equals sign */
108 %name-prefix="igraph_pajek_yy"
109 %defines
110 %locations
111 %error-verbose
112 %parse-param { igraph_i_pajek_parsedata_t* context }
113 %lex-param { void *scanner }
114 
115 %union {
116   long int intnum;
117   double   realnum;
118   struct {
119     char *str;
120     int len;
121   } string;
122 }
123 
124 %type <intnum>   longint;
125 %type <intnum>   arcfrom;
126 %type <intnum>   arcto;
127 %type <intnum>   edgefrom;
128 %type <intnum>   edgeto;
129 %type <realnum>  number;
130 %type <string>   word;
131 %type <string>   vpwordpar;
132 %type <string>   epwordpar;
133 %type <intnum>   vertex;
134 
135 %token NEWLINE
136 %token NUM
137 %token ALNUM
138 %token QSTR
139 %token PSTR
140 %token NETWORKLINE
141 %token VERTICESLINE
142 %token ARCSLINE
143 %token EDGESLINE
144 %token ARCSLISTLINE
145 %token EDGESLISTLINE
146 %token MATRIXLINE
147 %token ERROR
148 
149 %token VP_X_FACT
150 %token VP_Y_FACT
151 %token VP_IC
152 %token VP_BC
153 %token VP_LC
154 %token VP_LR
155 %token VP_LPHI
156 %token VP_BW
157 %token VP_FOS
158 %token VP_PHI
159 %token VP_R
160 %token VP_Q
161 %token VP_LA
162 %token VP_FONT
163 %token VP_URL
164 %token VP_SIZE
165 
166 %token EP_C
167 %token EP_S
168 %token EP_A
169 %token EP_W
170 %token EP_H1
171 %token EP_H2
172 %token EP_A1
173 %token EP_A2
174 %token EP_K1
175 %token EP_K2
176 %token EP_AP
177 %token EP_P
178 %token EP_L
179 %token EP_LP
180 %token EP_LR
181 %token EP_LPHI
182 %token EP_LC
183 %token EP_LA
184 %token EP_SIZE
185 %token EP_FOS
186 
187 %%
188 
189 input: nethead vertices edgeblock {
190   if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); }
191  };
192 
193 nethead: /* empty */ | NETWORKLINE words NEWLINE;
194 
195 vertices: verticeshead NEWLINE vertdefs;
196 
197 verticeshead: VERTICESLINE longint {
198   context->vcount=$2;
199   context->vcount2=0;
200             }
201             | VERTICESLINE longint longint {
202   context->vcount=$2;
203   context->vcount2=$3;
204   igraph_i_pajek_add_bipartite_type(context);
205 };
206 
207 vertdefs: /* empty */  | vertdefs vertexline;
208 
209 vertexline: NEWLINE |
210             vertex NEWLINE |
211             vertex { context->actvertex=$1; } vertexid vertexcoords shape params NEWLINE { }
212 ;
213 
214 vertex: longint { $$=$1; context->mode=1; };
215 
216 vertexid: word {
217   igraph_i_pajek_add_string_vertex_attribute("id", $1.str, $1.len, context);
218   igraph_i_pajek_add_string_vertex_attribute("name", $1.str, $1.len, context);
219 };
220 
221 vertexcoords: /* empty */
222             | number number {
223   igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context);
224   igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context);
225             }
226             | number number number {
227   igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context);
228   igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context);
229   igraph_i_pajek_add_numeric_vertex_attribute("z", $3, context);
230             };
231 
232 shape: /* empty */ | word {
233   igraph_i_pajek_add_string_vertex_attribute("shape", $1.str, $1.len, context);
234 };
235 
236 params: /* empty */ | params param;
237 
238 param:
239        vpword
240      | VP_X_FACT number {
241          igraph_i_pajek_add_numeric_vertex_attribute("xfact", $2, context);
242        }
243      | VP_Y_FACT number {
244          igraph_i_pajek_add_numeric_vertex_attribute("yfact", $2, context);
245        }
246      | VP_IC number number number { /* RGB color */
247          igraph_i_pajek_add_numeric_vertex_attribute("color-red", $2, context);
248          igraph_i_pajek_add_numeric_vertex_attribute("color-green", $3, context);
249          igraph_i_pajek_add_numeric_vertex_attribute("color-blue", $4, context);
250        }
251      | VP_BC number number number {
252          igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", $2, context);
253          igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", $3, context);
254          igraph_i_pajek_add_numeric_vertex_attribute("framecolor-blue", $4, context);
255        }
256      | VP_LC number number number {
257          igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", $2, context);
258          igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", $3, context);
259          igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", $4, context);
260        }
261      | VP_LR number {
262          igraph_i_pajek_add_numeric_vertex_attribute("labeldist", $2, context);
263      }
264      | VP_LPHI number {
265          igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", $2, context);
266      }
267      | VP_BW number {
268          igraph_i_pajek_add_numeric_vertex_attribute("framewidth", $2, context);
269      }
270      | VP_FOS number {
271          igraph_i_pajek_add_numeric_vertex_attribute("fontsize", $2, context);
272      }
273      | VP_PHI number {
274          igraph_i_pajek_add_numeric_vertex_attribute("rotation", $2, context);
275      }
276      | VP_R number {
277          igraph_i_pajek_add_numeric_vertex_attribute("radius", $2, context);
278      }
279      | VP_Q number {
280          igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", $2, context);
281      }
282      | VP_LA number {
283          igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", $2, context);
284      }
285      | VP_SIZE number {
286          igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", $2, context);
287      }
288 ;
289 
290 vpword: VP_FONT { context->mode=3; } vpwordpar {
291          context->mode=1;
292          igraph_i_pajek_add_string_vertex_attribute("font", $3.str, $3.len, context);
293      }
294      | VP_URL { context->mode=3; } vpwordpar {
295          context->mode=1;
296          igraph_i_pajek_add_string_vertex_attribute("url", $3.str, $3.len, context);
297      }
298      | VP_IC { context->mode=3; } vpwordpar {
299          context->mode=1;
300          igraph_i_pajek_add_string_vertex_attribute("color", $3.str, $3.len, context);
301      }
302      | VP_BC { context->mode=3; } vpwordpar {
303          context->mode=1;
304          igraph_i_pajek_add_string_vertex_attribute("framecolor",
305                                                     $3.str, $3.len, context);
306      }
307      | VP_LC { context->mode=3; } vpwordpar {
308          context->mode=1;
309          igraph_i_pajek_add_string_vertex_attribute("labelcolor",
310                                                     $3.str, $3.len, context);
311      }
312 ;
313 
314 vpwordpar: word { $$=$1; };
315 
316 edgeblock: /* empty */ | edgeblock arcs | edgeblock edges | edgeblock arcslist | edgeblock edgeslist | edgeblock adjmatrix;
317 
318 arcs:   ARCSLINE NEWLINE arcsdefs        { context->directed=1; }
319       | ARCSLINE number NEWLINE arcsdefs { context->directed=1; };
320 
321 arcsdefs: /* empty */ | arcsdefs arcsline;
322 
323 arcsline: NEWLINE |
324           arcfrom arcto { context->actedge++;
325                           context->mode=2; } weight edgeparams NEWLINE  {
326   igraph_vector_push_back(context->vector, $1-1);
327   igraph_vector_push_back(context->vector, $2-1); }
328 ;
329 
330 arcfrom: longint;
331 
332 arcto: longint;
333 
334 edges:   EDGESLINE NEWLINE edgesdefs { context->directed=0; }
335        | EDGESLINE number NEWLINE edgesdefs { context->directed=0; }
336 
337 edgesdefs: /* empty */ | edgesdefs edgesline;
338 
339 edgesline: NEWLINE |
340           edgefrom edgeto { context->actedge++;
341                             context->mode=2; } weight edgeparams NEWLINE {
342   igraph_vector_push_back(context->vector, $1-1);
343   igraph_vector_push_back(context->vector, $2-1); }
344 ;
345 
346 edgefrom: longint;
347 
348 edgeto: longint;
349 
350 weight: /* empty */ | number {
351   igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context);
352 };
353 
354 edgeparams: /* empty */ | edgeparams edgeparam;
355 
356 edgeparam:
357      epword
358    | EP_C number number number {
359        igraph_i_pajek_add_numeric_edge_attribute("color-red", $2, context);
360        igraph_i_pajek_add_numeric_edge_attribute("color-green", $3, context);
361        igraph_i_pajek_add_numeric_edge_attribute("color-blue", $4, context);
362    }
363    | EP_S number {
364        igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context);
365    }
366    | EP_W number {
367        igraph_i_pajek_add_numeric_edge_attribute("edgewidth", $2, context);
368    }
369    | EP_H1 number {
370        igraph_i_pajek_add_numeric_edge_attribute("hook1", $2, context);
371    }
372    | EP_H2 number {
373        igraph_i_pajek_add_numeric_edge_attribute("hook2", $2, context);
374    }
375    | EP_A1 number {
376        igraph_i_pajek_add_numeric_edge_attribute("angle1", $2, context);
377    }
378    | EP_A2 number {
379        igraph_i_pajek_add_numeric_edge_attribute("angle2", $2, context);
380    }
381    | EP_K1 number {
382        igraph_i_pajek_add_numeric_edge_attribute("velocity1", $2, context);
383    }
384    | EP_K2 number {
385        igraph_i_pajek_add_numeric_edge_attribute("velocity2", $2, context);
386    }
387    | EP_AP number {
388        igraph_i_pajek_add_numeric_edge_attribute("arrowpos", $2, context);
389    }
390    | EP_LP number {
391        igraph_i_pajek_add_numeric_edge_attribute("labelpos", $2, context);
392    }
393    | EP_LR number {
394        igraph_i_pajek_add_numeric_edge_attribute("labelangle", $2, context);
395    }
396    | EP_LPHI number {
397        igraph_i_pajek_add_numeric_edge_attribute("labelangle2", $2, context);
398    }
399    | EP_LA number {
400        igraph_i_pajek_add_numeric_edge_attribute("labeldegree", $2, context);
401    }
402    | EP_SIZE number { /* what is this??? */
403        igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context);
404    }
405    | EP_FOS number {
406        igraph_i_pajek_add_numeric_edge_attribute("fontsize", $2, context);
407    }
408 ;
409 
410 epword: EP_A { context->mode=4; } epwordpar {
411       context->mode=2;
412       igraph_i_pajek_add_string_edge_attribute("arrowtype", $3.str, $3.len, context);
413     }
414     | EP_P { context->mode=4; } epwordpar {
415       context->mode=2;
416       igraph_i_pajek_add_string_edge_attribute("linepattern", $3.str, $3.len, context);
417     }
418     | EP_L { context->mode=4; } epwordpar {
419       context->mode=2;
420       igraph_i_pajek_add_string_edge_attribute("label", $3.str, $3.len, context);
421     }
422     | EP_LC { context->mode=4; } epwordpar {
423       context->mode=2;
424       igraph_i_pajek_add_string_edge_attribute("labelcolor", $3.str, $3.len, context);
425     }
426     | EP_C { context->mode=4; } epwordpar {
427       context->mode=2;
428       igraph_i_pajek_add_string_edge_attribute("color", $3.str, $3.len, context);
429     }
430 ;
431 
432 epwordpar: word { context->mode=2; $$=$1; };
433 
434 arcslist: ARCSLISTLINE NEWLINE arcslistlines { context->directed=1; };
435 
436 arcslistlines: /* empty */ | arcslistlines arclistline;
437 
438 arclistline: NEWLINE | arclistfrom arctolist NEWLINE;
439 
440 arctolist: /* empty */ | arctolist arclistto;
441 
442 arclistfrom: longint { context->mode=0; context->actfrom=labs($1)-1; };
443 
444 arclistto: longint {
445   igraph_vector_push_back(context->vector, context->actfrom);
446   igraph_vector_push_back(context->vector, labs($1)-1);
447 };
448 
449 edgeslist: EDGESLISTLINE NEWLINE edgelistlines { context->directed=0; };
450 
451 edgelistlines: /* empty */ | edgelistlines edgelistline;
452 
453 edgelistline: NEWLINE | edgelistfrom edgetolist NEWLINE;
454 
455 edgetolist: /* empty */ | edgetolist edgelistto;
456 
457 edgelistfrom: longint { context->mode=0; context->actfrom=labs($1)-1; };
458 
459 edgelistto: longint {
460   igraph_vector_push_back(context->vector, context->actfrom);
461   igraph_vector_push_back(context->vector, labs($1)-1);
462 };
463 
464 /* -----------------------------------------------------*/
465 
466 adjmatrix: matrixline NEWLINE adjmatrixlines;
467 
468 matrixline: MATRIXLINE { context->actfrom=0;
469                          context->actto=0;
470                          context->directed=(context->vcount2==0);
471                        };
472 
473 adjmatrixlines: /* empty */ | adjmatrixlines adjmatrixline;
474 
475 adjmatrixline: adjmatrixnumbers NEWLINE { context->actfrom++; context->actto=0; };
476 
477 adjmatrixnumbers: /* empty */ | adjmatrixentry adjmatrixnumbers;
478 
479 adjmatrixentry: number {
480   if ($1 != 0) {
481     if (context->vcount2==0) {
482       context->actedge++;
483       igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context);
484       igraph_vector_push_back(context->vector, context->actfrom);
485       igraph_vector_push_back(context->vector, context->actto);
486     } else if (context->vcount2 + context->actto < context->vcount) {
487       context->actedge++;
488       igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context);
489       igraph_vector_push_back(context->vector, context->actfrom);
490       igraph_vector_push_back(context->vector,
491                               context->vcount2+context->actto);
492     }
493   }
494   context->actto++;
495 };
496 
497 /* -----------------------------------------------------*/
498 
499 longint: NUM { $$=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner),
500                                           igraph_pajek_yyget_leng(scanner)); };
501 
502 number: NUM  { $$=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner),
503                                           igraph_pajek_yyget_leng(scanner)); };
504 
505 words: /* empty */ | words word;
506 
507 word: ALNUM { $$.str=igraph_pajek_yyget_text(scanner);
508               $$.len=igraph_pajek_yyget_leng(scanner); }
509       | NUM { $$.str=igraph_pajek_yyget_text(scanner);
510               $$.len=igraph_pajek_yyget_leng(scanner); }
511       | QSTR { $$.str=igraph_pajek_yyget_text(scanner)+1;
512                $$.len=igraph_pajek_yyget_leng(scanner)-2; };
513 
514 %%
515 
516 int igraph_pajek_yyerror(YYLTYPE* locp,
517                          igraph_i_pajek_parsedata_t *context,
518                          const char *s) {
519   snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1,
520            "Parse error in Pajek file, line %i (%s)",
521            locp->first_line, s);
522   return 0;
523 }
524 
igraph_pajek_get_number(const char * str,long int length)525 igraph_real_t igraph_pajek_get_number(const char *str, long int length) {
526   igraph_real_t num;
527   char *tmp=IGRAPH_CALLOC(length+1, char);
528 
529   strncpy(tmp, str, length);
530   tmp[length]='\0';
531   sscanf(tmp, "%lf", &num);
532   IGRAPH_FREE(tmp);
533   return num;
534 }
535 
536 /* TODO: NA's */
537 
igraph_i_pajek_add_numeric_attribute(igraph_trie_t * names,igraph_vector_ptr_t * attrs,long int count,const char * attrname,igraph_integer_t vid,igraph_real_t number)538 int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names,
539                                          igraph_vector_ptr_t *attrs,
540                                          long int count,
541                                          const char *attrname,
542                                          igraph_integer_t vid,
543                                          igraph_real_t number) {
544   long int attrsize=igraph_trie_size(names);
545   long int id;
546   igraph_vector_t *na;
547   igraph_attribute_record_t *rec;
548 
549   igraph_trie_get(names, attrname, &id);
550   if (id == attrsize) {
551     /* add a new attribute */
552     rec=IGRAPH_CALLOC(1, igraph_attribute_record_t);
553     na=IGRAPH_CALLOC(1, igraph_vector_t);
554     igraph_vector_init(na, count);
555     rec->name=strdup(attrname);
556     rec->type=IGRAPH_ATTRIBUTE_NUMERIC;
557     rec->value=na;
558     igraph_vector_ptr_push_back(attrs, rec);
559   }
560   rec=VECTOR(*attrs)[id];
561   na=(igraph_vector_t*)rec->value;
562   if (igraph_vector_size(na) == vid) {
563     IGRAPH_CHECK(igraph_vector_push_back(na, number));
564   } else if (igraph_vector_size(na) < vid) {
565     long int origsize=igraph_vector_size(na);
566     IGRAPH_CHECK(igraph_vector_resize(na, (long int)vid+1));
567     for (;origsize<count; origsize++) {
568       VECTOR(*na)[origsize] = IGRAPH_NAN;
569     }
570     VECTOR(*na)[(long int) vid] = number;
571   } else {
572     VECTOR(*na)[(long int) vid] = number;
573   }
574 
575   return 0;
576 }
577 
578 /* TODO: NA's */
579 
igraph_i_pajek_add_string_attribute(igraph_trie_t * names,igraph_vector_ptr_t * attrs,long int count,const char * attrname,igraph_integer_t vid,const char * str)580 int igraph_i_pajek_add_string_attribute(igraph_trie_t *names,
581                                         igraph_vector_ptr_t *attrs,
582                                         long int count,
583                                         const char *attrname,
584                                         igraph_integer_t vid,
585                                         const char *str) {
586   long int attrsize=igraph_trie_size(names);
587   long int id;
588   igraph_strvector_t *na;
589   igraph_attribute_record_t *rec;
590   long int i;
591 
592   igraph_trie_get(names, attrname, &id);
593   if (id == attrsize) {
594     /* add a new attribute */
595     rec=IGRAPH_CALLOC(1, igraph_attribute_record_t);
596     na=IGRAPH_CALLOC(1, igraph_strvector_t);
597     igraph_strvector_init(na, count);
598     for (i=0; i<count; i++) {
599       igraph_strvector_set(na, i, "");
600     }
601     rec->name=strdup(attrname);
602     rec->type=IGRAPH_ATTRIBUTE_STRING;
603     rec->value=na;
604     igraph_vector_ptr_push_back(attrs, rec);
605   }
606   rec=VECTOR(*attrs)[id];
607   na=(igraph_strvector_t*)rec->value;
608   if (igraph_strvector_size(na) <= vid) {
609     long int origsize=igraph_strvector_size(na);
610     IGRAPH_CHECK(igraph_strvector_resize(na, vid+1));
611     for (;origsize<count; origsize++) {
612       igraph_strvector_set(na, origsize, "");
613     }
614   }
615   igraph_strvector_set(na, vid, str);
616 
617   return 0;
618 }
619 
igraph_i_pajek_add_string_vertex_attribute(const char * name,const char * value,int len,igraph_i_pajek_parsedata_t * context)620 int igraph_i_pajek_add_string_vertex_attribute(const char *name,
621                                                const char *value,
622                                                int len,
623                                                igraph_i_pajek_parsedata_t *context) {
624   char *tmp;
625   int ret;
626 
627   tmp=IGRAPH_CALLOC(len+1, char);
628   if (tmp==0) {
629     IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM);
630   }
631   IGRAPH_FINALLY(igraph_free, tmp);
632   strncpy(tmp, value, len);
633   tmp[len]='\0';
634 
635   ret=igraph_i_pajek_add_string_attribute(context->vertex_attribute_names,
636                                           context->vertex_attributes,
637                                           context->vcount,
638                                           name, context->actvertex-1,
639                                           tmp);
640 
641   IGRAPH_FREE(tmp);
642   IGRAPH_FINALLY_CLEAN(1);
643 
644   return ret;
645 }
646 
igraph_i_pajek_add_string_edge_attribute(const char * name,const char * value,int len,igraph_i_pajek_parsedata_t * context)647 int igraph_i_pajek_add_string_edge_attribute(const char *name,
648                                              const char *value,
649                                              int len,
650                                              igraph_i_pajek_parsedata_t *context) {
651   char *tmp;
652   int ret;
653 
654   tmp=IGRAPH_CALLOC(len+1, char);
655   if (tmp==0) {
656     IGRAPH_ERROR("cannot add element to hash table", IGRAPH_ENOMEM);
657   }
658   IGRAPH_FINALLY(igraph_free, tmp);
659   strncpy(tmp, value, len);
660   tmp[len]='\0';
661 
662   ret=igraph_i_pajek_add_string_attribute(context->edge_attribute_names,
663                                           context->edge_attributes,
664                                           context->actedge,
665                                           name, context->actedge-1,
666                                           tmp);
667 
668   IGRAPH_FREE(tmp);
669   IGRAPH_FINALLY_CLEAN(1);
670 
671   return ret;
672 }
673 
igraph_i_pajek_add_numeric_vertex_attribute(const char * name,igraph_real_t value,igraph_i_pajek_parsedata_t * context)674 int igraph_i_pajek_add_numeric_vertex_attribute(const char *name,
675                                                 igraph_real_t value,
676                                                 igraph_i_pajek_parsedata_t *context) {
677 
678   return
679     igraph_i_pajek_add_numeric_attribute(context->vertex_attribute_names,
680                                          context->vertex_attributes,
681                                          context->vcount,
682                                          name, context->actvertex-1,
683                                          value);
684 }
685 
igraph_i_pajek_add_numeric_edge_attribute(const char * name,igraph_real_t value,igraph_i_pajek_parsedata_t * context)686 int igraph_i_pajek_add_numeric_edge_attribute(const char *name,
687                                               igraph_real_t value,
688                                               igraph_i_pajek_parsedata_t *context) {
689 
690   return
691     igraph_i_pajek_add_numeric_attribute(context->edge_attribute_names,
692                                          context->edge_attributes,
693                                          context->actedge,
694                                          name, context->actedge-1,
695                                          value);
696 }
697 
igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t * context)698 int igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context) {
699 
700   const char *attrname="type";
701   igraph_trie_t *names=context->vertex_attribute_names;
702   igraph_vector_ptr_t *attrs=context->vertex_attributes;
703   int i, n=context->vcount, n1=context->vcount2;
704   long int attrid, attrsize=igraph_trie_size(names);
705   igraph_attribute_record_t *rec;
706   igraph_vector_t *na;
707 
708   if (n1 > n) {
709     IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file",
710                  IGRAPH_PARSEERROR);
711   }
712 
713   igraph_trie_get(names, attrname, &attrid);
714   if (attrid != attrsize) {
715     IGRAPH_ERROR("Duplicate 'type' attribute in Pajek file, "
716                  "this should not happen", IGRAPH_EINTERNAL);
717   }
718 
719   /* add a new attribute */
720   rec=IGRAPH_CALLOC(1, igraph_attribute_record_t);
721   na=IGRAPH_CALLOC(1, igraph_vector_t);
722   igraph_vector_init(na, n);
723   rec->name=strdup(attrname);
724   rec->type=IGRAPH_ATTRIBUTE_NUMERIC;
725   rec->value=na;
726   igraph_vector_ptr_push_back(attrs, rec);
727 
728   for (i=0; i<n1; i++) {
729     VECTOR(*na)[i] = 0;
730   }
731   for (i=n1; i<n; i++) {
732     VECTOR(*na)[i] = 1;
733   }
734 
735   return 0;
736 }
737 
igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t * context)738 int igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context) {
739   const igraph_vector_t *edges=context->vector;
740   int i, n1=context->vcount2;
741   int ne=igraph_vector_size(edges);
742 
743   for (i=0; i<ne; i+=2) {
744     int v1=VECTOR(*edges)[i];
745     int v2=VECTOR(*edges)[i+1];
746     if ( (v1 < n1 && v2 < n1) || (v1 > n1 && v2 > n1) ) {
747       IGRAPH_WARNING("Invalid edge in bipartite graph");
748     }
749   }
750 
751   return 0;
752 }
753