1 /* -*- mode: C -*-  */
2 /*
3    IGraph library.
4    Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
5    334 Harvard street, Cambridge, MA 02139 USA
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301 USA
21 
22 */
23 
24 #include "igraph_attributes.h"
25 #include "igraph_memory.h"
26 
27 #include "graph/attributes.h"
28 
29 #include "config.h"
30 
31 #include <string.h>
32 #include <stdarg.h>
33 
34 /* Should you ever want to have a thread-local attribute handler table, prepend
35  * IGRAPH_THREAD_LOCAL to the following declaration */
36 igraph_attribute_table_t *igraph_i_attribute_table = 0;
37 
igraph_i_attribute_init(igraph_t * graph,void * attr)38 int igraph_i_attribute_init(igraph_t *graph, void *attr) {
39     graph->attr = 0;
40     if (igraph_i_attribute_table) {
41         return igraph_i_attribute_table->init(graph, attr);
42     } else {
43         return 0;
44     }
45 }
46 
igraph_i_attribute_destroy(igraph_t * graph)47 void igraph_i_attribute_destroy(igraph_t *graph) {
48     if (igraph_i_attribute_table) {
49         igraph_i_attribute_table->destroy(graph);
50     }
51 }
52 
igraph_i_attribute_copy(igraph_t * to,const igraph_t * from,igraph_bool_t ga,igraph_bool_t va,igraph_bool_t ea)53 int igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga,
54                             igraph_bool_t va, igraph_bool_t ea) {
55     if (igraph_i_attribute_table) {
56         return igraph_i_attribute_table->copy(to, from, ga, va, ea);
57     } else {
58         return 0;
59     }
60 }
61 
igraph_i_attribute_add_vertices(igraph_t * graph,long int nv,void * attr)62 int igraph_i_attribute_add_vertices(igraph_t *graph, long int nv, void *attr) {
63     if (igraph_i_attribute_table) {
64         return igraph_i_attribute_table->add_vertices(graph, nv, attr);
65     } else {
66         return 0;
67     }
68 }
69 
igraph_i_attribute_permute_vertices(const igraph_t * graph,igraph_t * newgraph,const igraph_vector_t * idx)70 int igraph_i_attribute_permute_vertices(const igraph_t *graph,
71                                         igraph_t *newgraph,
72                                         const igraph_vector_t *idx) {
73 
74     if (igraph_i_attribute_table) {
75         return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx);
76     } else {
77         return 0;
78     }
79 }
80 
igraph_i_attribute_combine_vertices(const igraph_t * graph,igraph_t * newgraph,const igraph_vector_ptr_t * merges,const igraph_attribute_combination_t * comb)81 int igraph_i_attribute_combine_vertices(const igraph_t *graph,
82                                         igraph_t *newgraph,
83                                         const igraph_vector_ptr_t *merges,
84                                         const igraph_attribute_combination_t *comb) {
85     if (igraph_i_attribute_table) {
86         return igraph_i_attribute_table->combine_vertices(graph, newgraph,
87                 merges,
88                 comb);
89     } else {
90         return 0;
91     }
92 }
93 
igraph_i_attribute_add_edges(igraph_t * graph,const igraph_vector_t * edges,void * attr)94 int igraph_i_attribute_add_edges(igraph_t *graph,
95                                  const igraph_vector_t *edges, void *attr) {
96     if (igraph_i_attribute_table) {
97         return igraph_i_attribute_table->add_edges(graph, edges, attr);
98     } else {
99         return 0;
100     }
101 }
102 
igraph_i_attribute_permute_edges(const igraph_t * graph,igraph_t * newgraph,const igraph_vector_t * idx)103 int igraph_i_attribute_permute_edges(const igraph_t *graph,
104                                      igraph_t *newgraph,
105                                      const igraph_vector_t *idx) {
106     if (igraph_i_attribute_table) {
107         return igraph_i_attribute_table->permute_edges(graph, newgraph, idx);
108     } else {
109         return 0;
110     }
111 }
112 
igraph_i_attribute_combine_edges(const igraph_t * graph,igraph_t * newgraph,const igraph_vector_ptr_t * merges,const igraph_attribute_combination_t * comb)113 int igraph_i_attribute_combine_edges(const igraph_t *graph,
114                                      igraph_t *newgraph,
115                                      const igraph_vector_ptr_t *merges,
116                                      const igraph_attribute_combination_t *comb) {
117     if (igraph_i_attribute_table) {
118         return igraph_i_attribute_table->combine_edges(graph, newgraph,
119                 merges,
120                 comb);
121     } else {
122         return 0;
123     }
124 }
125 
igraph_i_attribute_get_info(const igraph_t * graph,igraph_strvector_t * gnames,igraph_vector_t * gtypes,igraph_strvector_t * vnames,igraph_vector_t * vtypes,igraph_strvector_t * enames,igraph_vector_t * etypes)126 int igraph_i_attribute_get_info(const igraph_t *graph,
127                                 igraph_strvector_t *gnames,
128                                 igraph_vector_t *gtypes,
129                                 igraph_strvector_t *vnames,
130                                 igraph_vector_t *vtypes,
131                                 igraph_strvector_t *enames,
132                                 igraph_vector_t *etypes) {
133     if (igraph_i_attribute_table) {
134         return igraph_i_attribute_table->get_info(graph, gnames, gtypes,
135                 vnames, vtypes,
136                 enames, etypes);
137     } else {
138         return 0;
139     }
140 }
141 
igraph_i_attribute_has_attr(const igraph_t * graph,igraph_attribute_elemtype_t type,const char * name)142 igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph,
143         igraph_attribute_elemtype_t type,
144         const char *name) {
145     if (igraph_i_attribute_table) {
146         return igraph_i_attribute_table->has_attr(graph, type, name);
147     } else {
148         return 0;
149     }
150 }
151 
igraph_i_attribute_gettype(const igraph_t * graph,igraph_attribute_type_t * type,igraph_attribute_elemtype_t elemtype,const char * name)152 int igraph_i_attribute_gettype(const igraph_t *graph,
153                                igraph_attribute_type_t *type,
154                                igraph_attribute_elemtype_t elemtype,
155                                const char *name) {
156     if (igraph_i_attribute_table) {
157         return igraph_i_attribute_table->gettype(graph, type, elemtype, name);
158     } else {
159         return 0;
160     }
161 
162 }
163 
igraph_i_attribute_get_numeric_graph_attr(const igraph_t * graph,const char * name,igraph_vector_t * value)164 int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph,
165         const char *name,
166         igraph_vector_t *value) {
167     if (igraph_i_attribute_table) {
168         return igraph_i_attribute_table->get_numeric_graph_attr(graph, name, value);
169     } else {
170         return 0;
171     }
172 }
173 
igraph_i_attribute_get_numeric_vertex_attr(const igraph_t * graph,const char * name,igraph_vs_t vs,igraph_vector_t * value)174 int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph,
175         const char *name,
176         igraph_vs_t vs,
177         igraph_vector_t *value) {
178     if (igraph_i_attribute_table) {
179         return igraph_i_attribute_table->get_numeric_vertex_attr(graph, name, vs, value);
180     } else {
181         return 0;
182     }
183 }
184 
igraph_i_attribute_get_numeric_edge_attr(const igraph_t * graph,const char * name,igraph_es_t es,igraph_vector_t * value)185 int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph,
186         const char *name,
187         igraph_es_t es,
188         igraph_vector_t *value) {
189     if (igraph_i_attribute_table) {
190         return igraph_i_attribute_table->get_numeric_edge_attr(graph, name, es, value);
191     } else {
192         return 0;
193     }
194 }
195 
igraph_i_attribute_get_string_graph_attr(const igraph_t * graph,const char * name,igraph_strvector_t * value)196 int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph,
197         const char *name,
198         igraph_strvector_t *value) {
199     if (igraph_i_attribute_table) {
200         return igraph_i_attribute_table->get_string_graph_attr(graph, name, value);
201     } else {
202         return 0;
203     }
204 }
205 
igraph_i_attribute_get_string_vertex_attr(const igraph_t * graph,const char * name,igraph_vs_t vs,igraph_strvector_t * value)206 int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph,
207         const char *name,
208         igraph_vs_t vs,
209         igraph_strvector_t *value) {
210     if (igraph_i_attribute_table) {
211         return igraph_i_attribute_table->get_string_vertex_attr(graph, name, vs, value);
212     } else {
213         return 0;
214     }
215 }
216 
igraph_i_attribute_get_string_edge_attr(const igraph_t * graph,const char * name,igraph_es_t es,igraph_strvector_t * value)217 int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph,
218         const char *name,
219         igraph_es_t es,
220         igraph_strvector_t *value) {
221     if (igraph_i_attribute_table) {
222         return igraph_i_attribute_table->get_string_edge_attr(graph, name, es, value);
223     } else {
224         return 0;
225     }
226 }
227 
igraph_i_attribute_get_bool_graph_attr(const igraph_t * graph,const char * name,igraph_vector_bool_t * value)228 int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph,
229         const char *name,
230         igraph_vector_bool_t *value) {
231     if (igraph_i_attribute_table) {
232         return igraph_i_attribute_table->get_bool_graph_attr(graph, name, value);
233     } else {
234         return 0;
235     }
236 }
237 
igraph_i_attribute_get_bool_vertex_attr(const igraph_t * graph,const char * name,igraph_vs_t vs,igraph_vector_bool_t * value)238 int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph,
239         const char *name,
240         igraph_vs_t vs,
241         igraph_vector_bool_t *value) {
242     if (igraph_i_attribute_table) {
243         return igraph_i_attribute_table->get_bool_vertex_attr(graph, name, vs, value);
244     } else {
245         return 0;
246     }
247 }
248 
igraph_i_attribute_get_bool_edge_attr(const igraph_t * graph,const char * name,igraph_es_t es,igraph_vector_bool_t * value)249 int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph,
250         const char *name,
251         igraph_es_t es,
252         igraph_vector_bool_t *value) {
253     if (igraph_i_attribute_table) {
254         return igraph_i_attribute_table->get_bool_edge_attr(graph, name, es, value);
255     } else {
256         return 0;
257     }
258 }
259 
260 /**
261  * \function igraph_set_attribute_table
262  * \brief Attach an attribute table.
263  *
264  * This function attaches attribute handling code to the igraph library.
265  * Note that the attribute handler table is \em not thread-local even if
266  * igraph is compiled in thread-local mode. In the vast majority of cases,
267  * this is not a significant restriction.
268  *
269  * \param table Pointer to an \ref igraph_attribute_table_t object
270  *    containing the functions for attribute manipulation. Supply \c
271  *    NULL here if you don't want attributes.
272  * \return Pointer to the old attribute handling table.
273  *
274  * Time complexity: O(1).
275  */
276 
277 igraph_attribute_table_t *
igraph_set_attribute_table(const igraph_attribute_table_t * table)278 igraph_set_attribute_table(const igraph_attribute_table_t * table) {
279     igraph_attribute_table_t *old = igraph_i_attribute_table;
280     igraph_i_attribute_table = (igraph_attribute_table_t*) table;
281     return old;
282 }
283 
284 igraph_attribute_table_t *
igraph_i_set_attribute_table(const igraph_attribute_table_t * table)285 igraph_i_set_attribute_table(const igraph_attribute_table_t * table) {
286     IGRAPH_WARNING("igraph_i_set_attribute_table is deprecated, use igraph_set_attribute_table.");
287     return igraph_set_attribute_table(table);
288 }
289 
igraph_has_attribute_table()290 igraph_bool_t igraph_has_attribute_table() {
291     return igraph_i_attribute_table != 0;
292 }
293 
294 
295 /**
296  * \function igraph_attribute_combination_init
297  * \brief Initialize attribute combination list.
298  *
299  * \param comb The uninitialized attribute combination list.
300  * \return Error code.
301  *
302  * Time complexity: O(1)
303  */
igraph_attribute_combination_init(igraph_attribute_combination_t * comb)304 int igraph_attribute_combination_init(igraph_attribute_combination_t *comb) {
305     IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0));
306     return IGRAPH_SUCCESS;
307 }
308 
309 /**
310  * \function igraph_attribute_combination_destroy
311  * \brief Destroy attribute combination list.
312  *
313  * \param comb The attribute combination list.
314  *
315  * Time complexity: O(n), where n is the number of records in the
316                     attribute combination list.
317  */
igraph_attribute_combination_destroy(igraph_attribute_combination_t * comb)318 void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) {
319     long int i, n = igraph_vector_ptr_size(&comb->list);
320     for (i = 0; i < n; i++) {
321         igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
322         if (rec->name) {
323             IGRAPH_FREE(rec->name);
324         }
325         IGRAPH_FREE(rec);
326     }
327     igraph_vector_ptr_destroy(&comb->list);
328 }
329 
330 /**
331  * \function igraph_attribute_combination_add
332  * \brief Add combination record to attribute combination list.
333  *
334  * \param comb The attribute combination list.
335  * \param name The name of the attribute. If the name already exists
336  *             the attribute combination record will be replaced.
337  *             Use NULL to add a default combination record for all
338  *             atributes not in the list.
339  * \param type The type of the attribute combination. See \ref
340  *             igraph_attribute_combination_type_t for the options.
341  * \param func Function to be used if \p type is
342  *             \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION.
343  * \return Error code.
344  *
345  * Time complexity: O(n), where n is the number of current attribute
346  *                  combinations.
347  */
igraph_attribute_combination_add(igraph_attribute_combination_t * comb,const char * name,igraph_attribute_combination_type_t type,igraph_function_pointer_t func)348 int igraph_attribute_combination_add(igraph_attribute_combination_t *comb,
349                                      const char *name,
350                                      igraph_attribute_combination_type_t type,
351                                      igraph_function_pointer_t func) {
352     long int i, n = igraph_vector_ptr_size(&comb->list);
353 
354     /* Search, in case it is already there */
355     for (i = 0; i < n; i++) {
356         igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
357         const char *n = r->name;
358         if ( (!name && !n) ||
359              (name && n && !strcmp(n, name)) ) {
360             r->type = type;
361             r->func = func;
362             break;
363         }
364     }
365 
366     if (i == n) {
367         /* This is a new attribute name */
368         igraph_attribute_combination_record_t *rec =
369             IGRAPH_CALLOC(1, igraph_attribute_combination_record_t);
370 
371         if (!rec) {
372             IGRAPH_ERROR("Cannot create attribute combination data",
373                          IGRAPH_ENOMEM);
374         }
375         if (!name) {
376             rec->name = NULL;
377         } else {
378             rec->name = strdup(name);
379         }
380         rec->type = type;
381         rec->func = func;
382 
383         IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec));
384 
385     }
386 
387     return IGRAPH_SUCCESS;
388 }
389 
390 /**
391  * \function igraph_attribute_combination_remove
392  * \brief Remove a record from an attribute combination list.
393  *
394  * \param comb The attribute combination list.
395  * \param name The attribute name of the attribute combination record
396  *             to remove. It will be ignored if the named attribute
397  *             does not exist. It can be NULL to remove the default
398  *             combination record.
399  * \return Error code. This currently always returns IGRAPH_SUCCESS.
400  *
401  * Time complexity: O(n), where n is the number of records in the attribute
402                     combination list.
403  */
igraph_attribute_combination_remove(igraph_attribute_combination_t * comb,const char * name)404 int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb,
405                                         const char *name) {
406     long int i, n = igraph_vector_ptr_size(&comb->list);
407 
408     /* Search, in case it is already there */
409     for (i = 0; i < n; i++) {
410         igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
411         const char *n = r->name;
412         if ( (!name && !n) ||
413              (name && n && !strcmp(n, name)) ) {
414             break;
415         }
416     }
417 
418     if (i != n) {
419         igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
420         if (r->name) {
421             IGRAPH_FREE(r->name);
422         }
423         IGRAPH_FREE(r);
424         igraph_vector_ptr_remove(&comb->list, i);
425     } else {
426         /* It is not there, we don't do anything */
427     }
428 
429     return IGRAPH_SUCCESS;
430 }
431 
igraph_attribute_combination_query(const igraph_attribute_combination_t * comb,const char * name,igraph_attribute_combination_type_t * type,igraph_function_pointer_t * func)432 int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb,
433                                        const char *name,
434                                        igraph_attribute_combination_type_t *type,
435                                        igraph_function_pointer_t *func) {
436     long int i, def = -1, len = igraph_vector_ptr_size(&comb->list);
437 
438     for (i = 0; i < len; i++) {
439         igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
440         const char *n = rec->name;
441         if ( (!name && !n) ||
442              (name && n && !strcmp(n, name)) ) {
443             *type = rec->type;
444             *func = rec->func;
445             return 0;
446         }
447         if (!n) {
448             def = i;
449         }
450     }
451 
452     if (def == -1) {
453         /* Did not find anything */
454         *type = IGRAPH_ATTRIBUTE_COMBINE_DEFAULT;
455         *func = 0;
456     } else {
457         igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[def];
458         *type = rec->type;
459         *func = rec->func;
460     }
461 
462     return 0;
463 }
464 
igraph_attribute_combination(igraph_attribute_combination_t * comb,...)465 int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...) {
466 
467     va_list ap;
468 
469     IGRAPH_CHECK(igraph_attribute_combination_init(comb));
470 
471     va_start(ap, comb);
472     while (1) {
473         igraph_function_pointer_t func = 0;
474         igraph_attribute_combination_type_t type;
475         const char *name;
476 
477         name = va_arg(ap, const char *);
478 
479         if (name == IGRAPH_NO_MORE_ATTRIBUTES) {
480             break;
481         }
482 
483         type = (igraph_attribute_combination_type_t)va_arg(ap, int);
484         if (type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) {
485             func = va_arg(ap, igraph_function_pointer_t);
486         }
487 
488         if (strlen(name) == 0) {
489             name = 0;
490         }
491 
492         IGRAPH_CHECK(igraph_attribute_combination_add(comb, name, type, func));
493     }
494 
495     va_end(ap);
496 
497     return 0;
498 }
499