From a58ec40a47e0fbe68b322f50902351e1f0447b63 Mon Sep 17 00:00:00 2001 From: Kai Zhang <kai@kzhang.org> Date: Tue, 27 Mar 2018 15:09:51 -0700 Subject: [PATCH] rewrite attribute interface --- cbits/bytestring.c | 360 ++++ cbits/bytestring.h | 111 ++ cbits/haskell_attributes.c | 1900 +++++++++++++++++++ cbits/haskell_attributes.h | 218 +++ cbits/haskell_igraph.c | 57 + cbits/{haskelligraph.h => haskell_igraph.h} | 0 cbits/haskelligraph.c | 36 - haskell-igraph.cabal | 18 +- src/IGraph.hs | 81 +- src/IGraph/Exporter/GEXF.hs | 50 +- src/IGraph/Generators.hs | 3 +- src/IGraph/Internal/Arpack.chs | 2 +- src/IGraph/Internal/Attribute.chs | 52 +- src/IGraph/Internal/Data.chs | 51 +- src/IGraph/Internal/Graph.chs | 2 +- src/IGraph/Internal/Initialization.chs | 2 +- src/IGraph/Mutable.hs | 31 +- src/IGraph/Structure.hs | 42 +- stack.yaml | 2 +- tests/Test/Attributes.hs | 47 + tests/Test/Basic.hs | 4 +- tests/test.hs | 2 + 22 files changed, 2911 insertions(+), 160 deletions(-) create mode 100644 cbits/bytestring.c create mode 100644 cbits/bytestring.h create mode 100644 cbits/haskell_attributes.c create mode 100644 cbits/haskell_attributes.h create mode 100644 cbits/haskell_igraph.c rename cbits/{haskelligraph.h => haskell_igraph.h} (100%) delete mode 100644 cbits/haskelligraph.c create mode 100644 tests/Test/Attributes.hs diff --git a/cbits/bytestring.c b/cbits/bytestring.c new file mode 100644 index 0000000..8dd7802 --- /dev/null +++ b/cbits/bytestring.c @@ -0,0 +1,360 @@ +#include "bytestring.h" +#include <assert.h> +#include <string.h> + +int bsvector_init(bsvector_t *sv, long int len) { + long int i; + sv->data=igraph_Calloc(len, bytestring_t*); + if (sv->data==0) { + IGRAPH_ERROR("bsvector init failed", IGRAPH_ENOMEM); + } + for (i=0; i<len; i++) { + sv->data[i]=new_bytestring(0); + if (sv->data[i]==0) { + bsvector_destroy(sv); + IGRAPH_ERROR("bsvector init failed", IGRAPH_ENOMEM); + } + } + sv->len=len; + + return 0; +} + +void bsvector_destroy(bsvector_t *sv) { + long int i; + assert(sv != 0); + if (sv->data != 0) { + for (i=0; i<sv->len; i++) { + if (sv->data[i] != 0) { + destroy_bytestring(sv->data[i]); + } + } + igraph_Free(sv->data); + } +} + +void bsvector_get(const bsvector_t *sv, long int idx, bytestring_t **value) { + assert(sv != 0); + assert(sv->data != 0); + assert(sv->data[idx] != 0); + *value = sv->data[idx]; +} + +int bsvector_set(bsvector_t *sv, long int idx, const bytestring_t *value) { + assert(sv != 0); + assert(sv->data != 0); + + if (sv->data[idx] != 0) { + //destroy_bytestring(sv->data[idx]); + } + sv->data[idx] = new_bytestring(value->len); + + memcpy(sv->data[idx]->value, value->value, value->len * sizeof(char)); + + return 0; +} + +void bsvector_remove_section(bsvector_t *v, long int from, long int to) { + long int i; + + assert(v != 0); + assert(v->data != 0); + + for (i=from; i<to; i++) { + if (v->data[i] != 0) { + destroy_bytestring(v->data[i]); + } + } + for (i=0; i<v->len-to; i++) { + v->data[from+i]=v->data[to+i]; + } + + v->len -= (to-from); +} + +void bsvector_remove(bsvector_t *v, long int elem) { + assert(v != 0); + assert(v->data != 0); + bsvector_remove_section(v, elem, elem+1); +} + +/* +void bsvector_move_interval(bsvector_t *v, long int begin, + long int end, long int to) { + long int i; + assert(v != 0); + assert(v->data != 0); + for (i=to; i<to+end-begin; i++) { + if (v->data[i] != 0) { + destroy_bytestring(v->data[i]); + } + } + for (i=0; i<end-begin; i++) { + if (v->data[begin+i] != 0) { + size_t len=strlen(v->data[begin+i])+1; + v->data[to+i]=igraph_Calloc(len, char); + memcpy(v->data[to+i], v->data[begin+i], sizeof(char)*len); + } + } +} +*/ + +int bsvector_copy(bsvector_t *to, const bsvector_t *from) { + long int i; + bytestring_t *str; + assert(from != 0); +/* assert(from->data != 0); */ + to->data=igraph_Calloc(from->len, bytestring_t*); + if (to->data==0) { + IGRAPH_ERROR("Cannot copy string vector", IGRAPH_ENOMEM); + } + to->len=from->len; + + for (i=0; i<from->len; i++) { + int ret; + bsvector_get(from, i, &str); + ret=bsvector_set(to, i, str); + if (ret != 0) { + bsvector_destroy(to); + IGRAPH_ERROR("cannot copy string vector", ret); + } + } + + return 0; +} + +int bsvector_append(bsvector_t *to, const bsvector_t *from) { + long int len1=bsvector_size(to), len2=bsvector_size(from); + long int i; + igraph_bool_t error=0; + IGRAPH_CHECK(bsvector_resize(to, len1+len2)); + for (i=0; i<len2; i++) { + if (from->data[i]->len > 0) { + bsvector_set(to, len1+i, from->data[i]); + if (!to->data[len1+i]) { + error=1; + break; + } + } + } + if (error) { + bsvector_resize(to, len1); + IGRAPH_ERROR("Cannot append string vector", IGRAPH_ENOMEM); + } + return 0; +} + +void bsvector_clear(bsvector_t *sv) { + long int i, n=bsvector_size(sv); + bytestring_t **tmp; + + for (i=0; i<n; i++) { + destroy_bytestring(sv->data[i]); + } + sv->len=0; + /* try to give back some memory */ + tmp=igraph_Realloc(sv->data, 1, bytestring_t*); + if (tmp != 0) { + sv->data=tmp; + } +} + +int bsvector_resize(bsvector_t* v, long int newsize) { + long int toadd = newsize - v->len, i, j; + bytestring_t **tmp; + long int reallocsize=newsize; + if (reallocsize==0) { reallocsize=1; } + + assert(v != 0); + assert(v->data != 0); + if (newsize < v->len) { + for (i=newsize; i<v->len; i++) { + destroy_bytestring(v->data[i]); + } + /* try to give back some space */ + tmp=igraph_Realloc(v->data, (size_t) reallocsize, bytestring_t*); + if (tmp != 0) { + v->data=tmp; + } + } else if (newsize > v->len) { + igraph_bool_t error=0; + tmp=igraph_Realloc(v->data, (size_t) reallocsize, bytestring_t*); + if (tmp==0) { + IGRAPH_ERROR("cannot resize string vector", IGRAPH_ENOMEM); + } + v->data = tmp; + + for (i=0; i<toadd; i++) { + v->data[v->len+i] = new_bytestring(0); + if (v->data[v->len+i] == 0) { + error=1; + break; + } + } + if (error) { + /* There was an error, free everything we've allocated so far */ + for (j=0; j<i; j++) { + if (v->data[v->len+i] != 0) { + destroy_bytestring(v->data[v->len+i]); + } + } + /* Try to give back space */ + tmp=igraph_Realloc(v->data, (size_t) (v->len), bytestring_t*); + if (tmp != 0) { + v->data=tmp; + } + IGRAPH_ERROR("Cannot resize string vector", IGRAPH_ENOMEM); + } + } + v->len = newsize; + + return 0; +} + +int bsvector_add(bsvector_t *v, const bytestring_t *value) { + long int s=bsvector_size(v); + bytestring_t **tmp; + assert(v != 0); + assert(v->data != 0); + tmp=igraph_Realloc(v->data, (size_t) s+1, bytestring_t*); + if (tmp == 0) { + IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM); + } + v->data=tmp; + v->data[s]=new_bytestring(value->len); + if (v->data[s]==0) { + IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM); + } + bsvector_set(v, s, value); + v->len += 1; + + return 0; +} + +/** + * \ingroup strvector + * \function igraph_strvector_permdelete + * \brief Removes elements from a string vector (for internal use) + */ + +void bsvector_permdelete(bsvector_t *v, const igraph_vector_t *index, + long int nremove) { + long int i; + bytestring_t **tmp; + assert(v != 0); + assert(v->data != 0); + + for (i=0; i<bsvector_size(v); i++) { + if (VECTOR(*index)[i] != 0) { + v->data[ (long int) VECTOR(*index)[i]-1 ] = v->data[i]; + } else { + destroy_bytestring(v->data[i]); + } + } + /* Try to make it shorter */ + tmp=igraph_Realloc(v->data, v->len-nremove ? + (size_t) (v->len-nremove) : 1, bytestring_t*); + if (tmp != 0) { + v->data=tmp; + } + v->len -= nremove; +} + +/** + * \ingroup strvector + * \function igraph_strvector_remove_negidx + * \brief Removes elements from a string vector (for internal use) + */ + +void bsvector_remove_negidx(bsvector_t *v, const igraph_vector_t *neg, + long int nremove) { + long int i, idx=0; + bytestring_t **tmp; + assert(v != 0); + assert(v->data != 0); + for (i=0; i<bsvector_size(v); i++) { + if (VECTOR(*neg)[i] >= 0) { + v->data[idx++] = v->data[i]; + } else { + destroy_bytestring(v->data[i]); + } + } + /* Try to give back some memory */ + tmp=igraph_Realloc(v->data, v->len-nremove ? + (size_t) (v->len-nremove) : 1, bytestring_t*); + if (tmp != 0) { + v->data=tmp; + } + v->len -= nremove; +} + +int bsvector_index(const bsvector_t *v, bsvector_t *newv, + const igraph_vector_t *idx) { + long int i, newlen=igraph_vector_size(idx); + IGRAPH_CHECK(bsvector_resize(newv, newlen)); + + for (i=0; i<newlen; i++) { + long int j=(long int) VECTOR(*idx)[i]; + bytestring_t *str; + bsvector_get(v, j, &str); + bsvector_set(newv, i, str); + } + + return 0; +} + +long int bsvector_size(const bsvector_t *sv) { + assert(sv != 0); + assert(sv->data != 0); + return sv->len; +} + +bytestring_t* new_bytestring(int n) { + bytestring_t *newstr = igraph_Calloc(1, bytestring_t); + char *str = igraph_Calloc(n, char); + newstr->len = n; + newstr->value = str; + return newstr; +} + +void destroy_bytestring(bytestring_t* str) { + if (str != NULL) { + free(str->value); + free(str); + } +} + +char* bytestring_to_char(bytestring_t* from) { + char *str = igraph_Calloc(from->len + sizeof(long int), char); + memcpy(str, &from->len, sizeof(long int)); + memcpy(str + sizeof(long int), from->value, from->len); + return str; +} + +bytestring_t* char_to_bytestring(char* from) { + unsigned long int *n; + memcpy(n, from, sizeof(long int)); + bytestring_t *str = new_bytestring(*n); + memcpy(str->value, from+sizeof(long int), *n); + return str; +} + +igraph_strvector_t* bsvector_to_strvector(bsvector_t* from) { + igraph_strvector_t *str; + size_t i; + igraph_strvector_init(str, from->len); + for (i = 0; i++; i < from->len) { + igraph_strvector_set(str, i, bytestring_to_char(from->data[i])); + } + return str; +} + +bsvector_t* strvector_to_bsvector(igraph_strvector_t* from) { + bsvector_t *str; + size_t i; + bsvector_init(str, from->len); + for (i = 0; i++; i < from->len) { + bsvector_set(str, i, char_to_bytestring(from->data[i])); + } + return str; +} diff --git a/cbits/bytestring.h b/cbits/bytestring.h new file mode 100644 index 0000000..23dbf6b --- /dev/null +++ b/cbits/bytestring.h @@ -0,0 +1,111 @@ +#ifndef HASKELL_IGRAPH_BYTESTRING +#define HASKELL_IGRAPH_BYTESTRING + +#include <igraph/igraph.h> + +typedef struct bytestring_t { + unsigned long int len; + char *value; +} bytestring_t; + +typedef struct bsvector_t { + bytestring_t **data; + long int len; +} bsvector_t; + +#define BSVECTOR_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(bsvector_init(v, size)); \ + IGRAPH_FINALLY( (igraph_finally_func_t*) bsvector_destroy, v); } while (0) + +/** + * \define STR + * Indexing string vectors + * + * This is a macro which allows to query the elements of a string vector in + * simpler way than \ref igraph_strvector_get(). Note this macro cannot be + * used to set an element, for that use \ref igraph_strvector_set(). + * \param sv The string vector + * \param i The the index of the element. + * \return The element at position \p i. + * + * Time complexity: O(1). + */ +#define BS(sv,i) ((const bytestring_t *)((sv).data[(i)])) + +int bsvector_init(bsvector_t *sv, long int len); + +void bsvector_destroy(bsvector_t *sv); + +void bsvector_get(const bsvector_t *sv, long int idx, bytestring_t **value); + +int bsvector_set(bsvector_t *sv, long int idx, const bytestring_t *value); + +void bsvector_remove_section(bsvector_t *v, long int from, long int to); + +void bsvector_remove(bsvector_t *v, long int elem); + +/* +void bsvector_move_interval(bsvector_t *v, long int begin, + long int end, long int to) { + long int i; + assert(v != 0); + assert(v->data != 0); + for (i=to; i<to+end-begin; i++) { + if (v->data[i] != 0) { + destroy_bytestring(v->data[i]); + } + } + for (i=0; i<end-begin; i++) { + if (v->data[begin+i] != 0) { + size_t len=strlen(v->data[begin+i])+1; + v->data[to+i]=igraph_Calloc(len, char); + memcpy(v->data[to+i], v->data[begin+i], sizeof(char)*len); + } + } +} +*/ + +int bsvector_copy(bsvector_t *to, const bsvector_t *from); + +int bsvector_append(bsvector_t *to, const bsvector_t *from); + +void bsvector_clear(bsvector_t *sv); + +int bsvector_resize(bsvector_t* v, long int newsize); + +/** + * \ingroup strvector + * \function igraph_strvector_permdelete + * \brief Removes elements from a string vector (for internal use) + */ + +void bsvector_permdelete(bsvector_t *v, const igraph_vector_t *index, + long int nremove); + +/** + * \ingroup strvector + * \function igraph_strvector_remove_negidx + * \brief Removes elements from a string vector (for internal use) + */ + +void bsvector_remove_negidx(bsvector_t *v, const igraph_vector_t *neg, + long int nremove); + +int bsvector_index(const bsvector_t *v, bsvector_t *newv, + const igraph_vector_t *idx); + +long int bsvector_size(const bsvector_t *sv); + +bytestring_t* new_bytestring(int n); + +void destroy_bytestring(bytestring_t* str); + +char* bytestring_to_char(bytestring_t* from); + +bytestring_t* char_to_bytestring(char* from); + +igraph_strvector_t* bsvector_to_strvector(bsvector_t* from); + +bsvector_t* strvector_to_bsvector(igraph_strvector_t* from); + +#endif diff --git a/cbits/haskell_attributes.c b/cbits/haskell_attributes.c new file mode 100644 index 0000000..0209386 --- /dev/null +++ b/cbits/haskell_attributes.c @@ -0,0 +1,1900 @@ +#include "haskell_attributes.h" + +/* An attribute is either a numeric vector (vector_t) or a string + vector (strvector_t). The attribute itself is stored in a + struct igraph_attribute_record_t, there is one such object for each + attribute. The igraph_t has a pointer to an array of three + vector_ptr_t's which contains pointers to + igraph_haskell_attribute_t's. Graph attributes are first, then vertex + and edge attributes. */ + +igraph_bool_t igraph_haskell_attribute_find(const igraph_vector_ptr_t *ptrvec, + const char *name, long int *idx) { + long int i, n=igraph_vector_ptr_size(ptrvec); + igraph_bool_t l=0; + for (i=0; !l && i<n; i++) { + igraph_attribute_record_t *rec=VECTOR(*ptrvec)[i]; + l= !strcmp(rec->name, name); + } + if (idx) { *idx=i-1; } + return l; +} + +int igraph_haskell_attributes_copy_attribute_record(igraph_attribute_record_t **newrec, + const igraph_attribute_record_t *rec) { + bsvector_t *str, *newstr; + + *newrec=igraph_Calloc(1, igraph_attribute_record_t); + if (!(*newrec)) { IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } + IGRAPH_FINALLY(igraph_free, *newrec); + (*newrec)->type=rec->type; + (*newrec)->name=strdup(rec->name); + if (!(*newrec)->name) { IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } + IGRAPH_FINALLY(igraph_free, (void*)(*newrec)->name); + + if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + str=(bsvector_t*)rec->value; + newstr=igraph_Calloc(1, bsvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newstr); + IGRAPH_CHECK(bsvector_copy(newstr, str)); + IGRAPH_FINALLY(bsvector_destroy, newstr); + (*newrec)->value=newstr; + } else { + IGRAPH_ERROR("Wrong attribute type", IGRAPH_ENOMEM); + } + + IGRAPH_FINALLY_CLEAN(4); + return 0; +} + + +int igraph_haskell_attribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) { + igraph_attribute_record_t *attr_rec; + long int i, n; + igraph_haskell_attributes_t *nattr; + + n = attr ? igraph_vector_ptr_size(attr) : 0; + + nattr=igraph_Calloc(1, igraph_haskell_attributes_t); + if (!nattr) { + IGRAPH_ERROR("Can't init attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, nattr); + + IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->gal, n)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->gal); + IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->val, 0)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->val); + IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->eal, 0)); + IGRAPH_FINALLY_CLEAN(3); + + for (i=0; i<n; i++) { + IGRAPH_CHECK(igraph_haskell_attributes_copy_attribute_record( + &attr_rec, VECTOR(*attr)[i])); + VECTOR(nattr->gal)[i] = attr_rec; + } + + graph->attr=nattr; + + return 0; +} + +void igraph_haskell_attribute_destroy(igraph_t *graph) { + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *als[3]= { &attr->gal, &attr->val, &attr->eal }; + long int i, n, a; + bsvector_t *str; + igraph_attribute_record_t *rec; + for (a=0; a<3; a++) { + n=igraph_vector_ptr_size(als[a]); + for (i=0; i<n; i++) { + rec=VECTOR(*als[a])[i]; + if (rec) { + if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + str=(bsvector_t*)rec->value; + bsvector_destroy(str); + igraph_free(str); + } + igraph_free((char*)rec->name); + igraph_free(rec); + } + } + } + igraph_vector_ptr_destroy(&attr->gal); + igraph_vector_ptr_destroy(&attr->val); + igraph_vector_ptr_destroy(&attr->eal); + igraph_free(graph->attr); + graph->attr=0; +} + +/* Almost the same as destroy, but we might have null pointers */ + +void igraph_haskell_attribute_copy_free(igraph_haskell_attributes_t *attr) { + igraph_vector_ptr_t *als[3] = { &attr->gal, &attr->val, &attr->eal }; + long int i, n, a; + bsvector_t *str; + igraph_attribute_record_t *rec; + for (a=0; a<3; a++) { + n=igraph_vector_ptr_size(als[a]); + for (i=0; i<n; i++) { + rec=VECTOR(*als[a])[i]; + if (!rec) { continue; } + if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + str=(bsvector_t*)rec->value; + bsvector_destroy(str); + igraph_free(str); + } + igraph_free((char*)rec->name); + igraph_free(rec); + } + } +} + +/* No reference counting here. If you use attributes in C you should + know what you're doing. */ + +int igraph_haskell_attribute_copy(igraph_t *to, const igraph_t *from, + igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea) { + igraph_haskell_attributes_t *attrfrom=from->attr, *attrto; + igraph_vector_ptr_t *alto[3], *alfrom[3]={ &attrfrom->gal, &attrfrom->val, + &attrfrom->eal }; + long int i, n, a; + igraph_bool_t copy[3] = { ga, va, ea }; + to->attr=attrto=igraph_Calloc(1, igraph_haskell_attributes_t); + if (!attrto) { + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, attrto); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->gal, 0); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->val, 0); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->eal, 0); + IGRAPH_FINALLY_CLEAN(3); + IGRAPH_FINALLY(igraph_haskell_attribute_copy_free, attrto); + + alto[0]=&attrto->gal; alto[1]=&attrto->val; alto[2]=&attrto->eal; + for (a=0; a<3; a++) { + if (copy[a]) { + n=igraph_vector_ptr_size(alfrom[a]); + IGRAPH_CHECK(igraph_vector_ptr_resize(alto[a], n)); + igraph_vector_ptr_null(alto[a]); + for (i=0; i<n; i++) { + igraph_attribute_record_t *newrec; + IGRAPH_CHECK(igraph_haskell_attributes_copy_attribute_record(&newrec, + VECTOR(*alfrom[a])[i])); + VECTOR(*alto[a])[i]=newrec; + } + } + } + + IGRAPH_FINALLY_CLEAN(2); + return 0; +} + +int igraph_haskell_attribute_add_vertices(igraph_t *graph, long int nv, + igraph_vector_ptr_t *nattr) { + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *val=&attr->val; + long int length=igraph_vector_ptr_size(val); + long int nattrno=nattr==NULL ? 0 : igraph_vector_ptr_size(nattr); + long int origlen=igraph_vcount(graph)-nv; + long int newattrs=0, i; + igraph_vector_t news; + + /* First add the new attributes if any */ + newattrs=0; + IGRAPH_VECTOR_INIT_FINALLY(&news, 0); + for (i=0; i<nattrno; i++) { + igraph_attribute_record_t *nattr_entry=VECTOR(*nattr)[i]; + const char *nname=nattr_entry->name; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(val, nname, &j); + if (!l) { + newattrs++; + IGRAPH_CHECK(igraph_vector_push_back(&news, i)); + } else { + /* check types */ + if (nattr_entry->type != ((igraph_attribute_record_t*)VECTOR(*val)[j])->type) { + IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL); + } + } + } + + /* Add NA/empty string vectors for the existing vertices */ + if (newattrs != 0) { + for (i=0; i<newattrs; i++) { + igraph_attribute_record_t *tmp=VECTOR(*nattr)[(long int)VECTOR(news)[i]]; + igraph_attribute_record_t *newrec=igraph_Calloc(1, igraph_attribute_record_t); + igraph_attribute_type_t type=tmp->type; + if (!newrec) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newrec); + newrec->type=type; + newrec->name=strdup(tmp->name); + if (!newrec->name) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)newrec->name); + if (type==IGRAPH_ATTRIBUTE_STRING) { + bsvector_t *newstr=igraph_Calloc(1, bsvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newstr); + BSVECTOR_INIT_FINALLY(newstr, origlen); + newrec->value=newstr; + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, newrec)); + IGRAPH_FINALLY_CLEAN(4); + } + length=igraph_vector_ptr_size(val); + } + + + /* Now append the new values */ + for (i=0; i<length; i++) { + igraph_attribute_record_t *oldrec=VECTOR(*val)[i]; + igraph_attribute_record_t *newrec=0; + const char *name=oldrec->name; + long int j; + igraph_bool_t l=0; + if (nattr) { l=igraph_haskell_attribute_find(nattr, name, &j); } + if (l) { + /* This attribute is present in nattr */ + bsvector_t *oldstr, *newstr; + newrec=VECTOR(*nattr)[j]; + oldstr=(bsvector_t*)oldrec->value; + newstr=(bsvector_t*)newrec->value; + if (oldrec->type != newrec->type) { + IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL); + } + if (oldrec->type == IGRAPH_ATTRIBUTE_STRING) { + if (nv != bsvector_size(newstr)) { + IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL); + } + IGRAPH_CHECK(bsvector_append(oldstr, newstr)); + } else { + IGRAPH_WARNING("Invalid attribute type"); + } + } else { + /* No such attribute, append NA's */ + bsvector_t *oldstr=(bsvector_t*)oldrec->value; + if (oldrec->type == IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_CHECK(bsvector_resize(oldstr, origlen+nv)); + } else { + IGRAPH_WARNING("Invalid attribute type"); + } + } + } + + igraph_vector_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +void igraph_haskell_attribute_permute_free(igraph_vector_ptr_t *v) { + long int i, n=igraph_vector_ptr_size(v); + for (i=0; i<n; i++) { + igraph_attribute_record_t *rec=VECTOR(*v)[i]; + igraph_Free(rec->name); + if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + bsvector_t *strv= (bsvector_t*) rec->value; + bsvector_destroy(strv); + igraph_Free(strv); + } + igraph_Free(rec); + } + igraph_vector_ptr_clear(v); +} + +int igraph_haskell_attribute_permute_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + + if (graph==newgraph) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *val=&attr->val; + long int valno=igraph_vector_ptr_size(val); + long int i; + + for (i=0; i<valno; i++) { + igraph_attribute_record_t *oldrec=VECTOR(*val)[i]; + igraph_attribute_type_t type=oldrec->type; + bsvector_t *str, *newstr; + if (type == IGRAPH_ATTRIBUTE_STRING) { + str=(bsvector_t*)oldrec->value; + newstr=igraph_Calloc(1, bsvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(bsvector_init(newstr, 0)); + IGRAPH_FINALLY(bsvector_destroy, newstr); + bsvector_index(str, newstr, idx); + oldrec->value=newstr; + bsvector_destroy(str); + igraph_Free(str); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_WARNING("Unknown edge attribute ignored"); + } + } + } else { + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *val=&attr->val; + long int valno=igraph_vector_ptr_size(val); + long int i; + + /* New vertex attributes */ + igraph_haskell_attributes_t *new_attr=newgraph->attr; + igraph_vector_ptr_t *new_val=&new_attr->val; + if (igraph_vector_ptr_size(new_val) != 0) { + IGRAPH_ERROR("Vertex attributes were already copied", + IGRAPH_EATTRIBUTES); + } + IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno)); + + IGRAPH_FINALLY(igraph_haskell_attribute_permute_free, new_val); + + for (i=0; i<valno; i++) { + igraph_attribute_record_t *oldrec=VECTOR(*val)[i]; + igraph_attribute_type_t type=oldrec->type; + bsvector_t *str, *newstr; + + /* The record itself */ + igraph_attribute_record_t *new_rec= + igraph_Calloc(1, igraph_attribute_record_t); + if (!new_rec) { + IGRAPH_ERROR("Cannot create vertex attributes", IGRAPH_ENOMEM); + } + new_rec->name = strdup(oldrec->name); + new_rec->type = oldrec->type; + VECTOR(*new_val)[i]=new_rec; + + /* The data */ + if (type == IGRAPH_ATTRIBUTE_STRING) { + str=(bsvector_t*)oldrec->value; + newstr=igraph_Calloc(1, bsvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot permute vertex attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(bsvector_init(newstr, 0)); + IGRAPH_FINALLY(bsvector_destroy, newstr); + bsvector_index(str, newstr, idx); + new_rec->value=newstr; + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_WARNING("Unknown vertex attribute ignored"); + } + } + } + + IGRAPH_FINALLY_CLEAN(1); + return 0; +} + +int igraph_haskell_attribute_combine_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb) { + IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM); + return 1; +} + +int igraph_haskell_attribute_add_edges(igraph_t *graph, const igraph_vector_t *edges, + igraph_vector_ptr_t *nattr) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *eal=&attr->eal; + long int ealno=igraph_vector_ptr_size(eal); + long int ne=igraph_vector_size(edges)/2; + long int origlen=igraph_ecount(graph)-ne; + long int nattrno= nattr == 0 ? 0 : igraph_vector_ptr_size(nattr); + igraph_vector_t news; + long int newattrs, i; + + /* First add the new attributes if any */ + newattrs=0; + IGRAPH_VECTOR_INIT_FINALLY(&news, 0); + for (i=0; i<nattrno; i++) { + igraph_attribute_record_t *nattr_entry=VECTOR(*nattr)[i]; + const char *nname=nattr_entry->name; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(eal, nname, &j); + if (!l) { + newattrs++; + IGRAPH_CHECK(igraph_vector_push_back(&news, i)); + } else { + /* check types */ + if (nattr_entry->type != ((igraph_attribute_record_t*)VECTOR(*eal)[j])->type) { + IGRAPH_ERROR("You cannot mix attribute types", IGRAPH_EINVAL); + } + } + } + + /* Add NA/empty string vectors for the existing vertices */ + if (newattrs != 0) { + for (i=0; i<newattrs; i++) { + igraph_attribute_record_t *tmp=VECTOR(*nattr)[(long int)VECTOR(news)[i]]; + igraph_attribute_record_t *newrec=igraph_Calloc(1, igraph_attribute_record_t); + igraph_attribute_type_t type=tmp->type; + if (!newrec) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newrec); + newrec->type=type; + newrec->name=strdup(tmp->name); + if (!newrec->name) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)newrec->name); + if (type==IGRAPH_ATTRIBUTE_STRING) { + bsvector_t *newstr=igraph_Calloc(1, bsvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, newstr); + BSVECTOR_INIT_FINALLY(newstr, origlen); + newrec->value=newstr; + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, newrec)); + IGRAPH_FINALLY_CLEAN(4); + } + ealno=igraph_vector_ptr_size(eal); + } + + /* Now append the new values */ + for (i=0; i<ealno; i++) { + igraph_attribute_record_t *oldrec=VECTOR(*eal)[i]; + igraph_attribute_record_t *newrec=0; + const char *name=oldrec->name; + long int j; + igraph_bool_t l=0; + if (nattr) { l=igraph_haskell_attribute_find(nattr, name, &j); } + if (l) { + /* This attribute is present in nattr */ + bsvector_t *oldstr, *newstr; + newrec=VECTOR(*nattr)[j]; + oldstr=(bsvector_t*)oldrec->value; + newstr=(bsvector_t*)newrec->value; + if (oldrec->type != newrec->type) { + IGRAPH_ERROR("Attribute types do not match", IGRAPH_EINVAL); + } + if (oldrec->type == IGRAPH_ATTRIBUTE_STRING) { + if (ne != bsvector_size(newstr)) { + IGRAPH_ERROR("Invalid string attribute length", IGRAPH_EINVAL); + } + IGRAPH_CHECK(bsvector_append(oldstr, newstr)); + } else { + IGRAPH_WARNING("Invalid attribute type"); + } + } else { + /* No such attribute, append NA's */ + bsvector_t *oldstr=(bsvector_t*)oldrec->value; + if (oldrec->type == IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_CHECK(bsvector_resize(oldstr, origlen+ne)); + } else { + IGRAPH_WARNING("Invalid attribute type"); + } + } + } + + igraph_vector_destroy(&news); + IGRAPH_FINALLY_CLEAN(1); + + return 0; +} + +int igraph_haskell_attribute_permute_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { + + if (graph == newgraph) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *eal=&attr->eal; + long int ealno=igraph_vector_ptr_size(eal); + long int i; + + for (i=0; i<ealno; i++) { + igraph_attribute_record_t *oldrec=VECTOR(*eal)[i]; + igraph_attribute_type_t type=oldrec->type; + bsvector_t *str, *newstr; + if (type == IGRAPH_ATTRIBUTE_STRING) { + str=(bsvector_t*)oldrec->value; + newstr=igraph_Calloc(1, bsvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(bsvector_init(newstr, 0)); + IGRAPH_FINALLY(bsvector_destroy, newstr); + bsvector_index(str, newstr, idx); + oldrec->value=newstr; + bsvector_destroy(str); + igraph_Free(str); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_WARNING("Unknown edge attribute ignored"); + } + } + + } else { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *eal=&attr->eal; + long int ealno=igraph_vector_ptr_size(eal); + long int i; + + /* New edge attributes */ + igraph_haskell_attributes_t *new_attr=newgraph->attr; + igraph_vector_ptr_t *new_eal=&new_attr->eal; + IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, ealno)); + + IGRAPH_FINALLY(igraph_haskell_attribute_permute_free, new_eal); + + for (i=0; i<ealno; i++) { + igraph_attribute_record_t *oldrec=VECTOR(*eal)[i]; + igraph_attribute_type_t type=oldrec->type; + bsvector_t *str, *newstr; + + /* The record itself */ + igraph_attribute_record_t *new_rec= igraph_Calloc(1, igraph_attribute_record_t); + if (!new_rec) { + IGRAPH_ERROR("Cannot create edge attributes", IGRAPH_ENOMEM); + } + new_rec->name = strdup(oldrec->name); + new_rec->type = oldrec->type; + VECTOR(*new_eal)[i] = new_rec; + + if (type == IGRAPH_ATTRIBUTE_STRING) { + str=(bsvector_t*)oldrec->value; + newstr=igraph_Calloc(1, bsvector_t); + if (!newstr) { + IGRAPH_ERROR("Cannot permute edge attributes", IGRAPH_ENOMEM); + } + IGRAPH_CHECK(bsvector_init(newstr, 0)); + IGRAPH_FINALLY(bsvector_destroy, newstr); + bsvector_index(str, newstr, idx); + new_rec->value=newstr; + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_WARNING("Unknown edge attribute ignored"); + } + } + IGRAPH_FINALLY_CLEAN(1); + } + + return 0; +} + +int igraph_haskell_attribute_combine_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb) { + + IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); + return 1; +} + +int igraph_haskell_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) { + + igraph_strvector_t *names[3] = { gnames, vnames, enames }; + igraph_vector_t *types[3] = { gtypes, vtypes, etypes }; + igraph_haskell_attributes_t *at=graph->attr; + igraph_vector_ptr_t *attr[3]={ &at->gal, &at->val, &at->eal }; + long int i,j; + + for (i=0; i<3; i++) { + igraph_strvector_t *n=names[i]; + igraph_vector_t *t=types[i]; + igraph_vector_ptr_t *al=attr[i]; + long int len=igraph_vector_ptr_size(al); + + if (n) { + IGRAPH_CHECK(igraph_strvector_resize(n, len)); + } + if (t) { + IGRAPH_CHECK(igraph_vector_resize(t, len)); + } + + for (j=0; j<len; j++) { + igraph_attribute_record_t *rec=VECTOR(*al)[j]; + const char *name=rec->name; + igraph_attribute_type_t type=rec->type; + if (n) { + IGRAPH_CHECK(igraph_strvector_set(n, j, name)); + } + if (t) { + VECTOR(*t)[j]=type; + } + } + } + + return 0; +} + +igraph_bool_t igraph_haskell_attribute_has_attr(const igraph_t *graph, + igraph_attribute_elemtype_t type, + const char *name) { + igraph_haskell_attributes_t *at=graph->attr; + igraph_vector_ptr_t *attr[3]={ &at->gal, &at->val, &at->eal }; + long int attrnum; + + switch (type) { + case IGRAPH_ATTRIBUTE_GRAPH: + attrnum=0; + break; + case IGRAPH_ATTRIBUTE_VERTEX: + attrnum=1; + break; + case IGRAPH_ATTRIBUTE_EDGE: + attrnum=2; + break; + default: + IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL); + break; + } + + return igraph_haskell_attribute_find(attr[attrnum], name, 0); +} + +int igraph_haskell_attribute_gettype(const igraph_t *graph, + igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, + const char *name) { + long int attrnum; + igraph_attribute_record_t *rec; + igraph_haskell_attributes_t *at=graph->attr; + igraph_vector_ptr_t *attr[3]={ &at->gal, &at->val, &at->eal }; + igraph_vector_ptr_t *al; + long int j; + igraph_bool_t l=0; + + switch (elemtype) { + case IGRAPH_ATTRIBUTE_GRAPH: + attrnum=0; + break; + case IGRAPH_ATTRIBUTE_VERTEX: + attrnum=1; + break; + case IGRAPH_ATTRIBUTE_EDGE: + attrnum=2; + break; + default: + IGRAPH_ERROR("Unknown attribute element type", IGRAPH_EINVAL); + break; + } + + al=attr[attrnum]; + l=igraph_haskell_attribute_find(al, name, &j); + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + rec=VECTOR(*al)[j]; + *type=rec->type; + + return 0; +} + +int igraph_haskell_attribute_get_numeric_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_t *value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +int igraph_haskell_attribute_get_bool_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_bool_t *value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +int igraph_haskell_attribute_get_string_graph_attr(const igraph_t *graph, + const char *name, + igraph_strvector_t *value_) { + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *gal=&attr->gal; + long int j; + igraph_attribute_record_t *rec; + bsvector_t *str; + igraph_bool_t l=igraph_haskell_attribute_find(gal, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + rec=VECTOR(*gal)[j]; + str=(bsvector_t*)rec->value; + + bsvector_t *value; + bsvector_init(value, 1); + + IGRAPH_CHECK(bsvector_set(value, 0, BS(*str,0))); + + igraph_strvector_copy(value_, bsvector_to_strvector(value)); + + return 0; +} + +int igraph_haskell_attribute_get_numeric_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_t *value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +int igraph_haskell_attribute_get_bool_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +int igraph_haskell_attribute_get_string_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_strvector_t *value_) { + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *val=&attr->val; + long int j; + igraph_attribute_record_t *rec; + bsvector_t *str; + igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + bsvector_t *value; + bsvector_init(value, 0); + + rec=VECTOR(*val)[j]; + str=(bsvector_t*)rec->value; + if (igraph_vs_is_all(&vs)) { + bsvector_resize(value, 0); + IGRAPH_CHECK(bsvector_append(value, str)); + } else { + igraph_vit_t it; + long int i=0; + IGRAPH_CHECK(igraph_vit_create(graph, vs, &it)); + IGRAPH_FINALLY(igraph_vit_destroy, &it); + IGRAPH_CHECK(bsvector_resize(value, IGRAPH_VIT_SIZE(it))); + for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) { + long int v=IGRAPH_VIT_GET(it); + bytestring_t *s; + bsvector_get(str, v, &s); + IGRAPH_CHECK(bsvector_set(value, i, s)); + } + igraph_vit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_strvector_copy(value_, bsvector_to_strvector(value)); + return 0; +} + +int igraph_haskell_attribute_get_numeric_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_t *value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +int igraph_haskell_attribute_get_string_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_strvector_t *value_) { + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *eal=&attr->eal; + long int j; + igraph_attribute_record_t *rec; + bsvector_t *str; + igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j); + + if (!l) { + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); + } + + bsvector_t *value; + bsvector_init(value, 1); + + rec=VECTOR(*eal)[j]; + str=(bsvector_t*)rec->value; + if (igraph_es_is_all(&es)) { + bsvector_resize(value, 0); + IGRAPH_CHECK(bsvector_append(value, str)); + } else { + igraph_eit_t it; + long int i=0; + IGRAPH_CHECK(igraph_eit_create(graph, es, &it)); + IGRAPH_FINALLY(igraph_eit_destroy, &it); + IGRAPH_CHECK(bsvector_resize(value, IGRAPH_EIT_SIZE(it))); + for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) { + long int e=IGRAPH_EIT_GET(it); + bytestring_t *s; + bsvector_get(str, e, &s); + IGRAPH_CHECK(bsvector_set(value, i, s)); + } + igraph_eit_destroy(&it); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_strvector_copy(value_, bsvector_to_strvector(value)); + return 0; +} + +int igraph_haskell_attribute_get_bool_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_bool_t *value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +igraph_real_t igraph_haskell_attribute_GAN(const igraph_t *graph, const char *name) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_GAB + * Query a boolean graph attribute. + * + * Returns the value of the given numeric graph attribute. + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute to query. + * \return The value of the attribute. + * + * \sa \ref GAB for a simpler interface. + * + * Time complexity: O(Ag), the number of graph attributes. + */ +igraph_bool_t igraph_haskell_attribute_GAB(const igraph_t *graph, const char *name) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_GAS + * Query a string graph attribute. + * + * Returns a <type>const</type> pointer to the string graph attribute + * specified in \p name. + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute to query. + * \return The value of the attribute. + * + * \sa \ref GAS for a simpler interface. + * + * Time complexity: O(Ag), the number of graph attributes. + */ +const bytestring_t* igraph_haskell_attribute_GAS(const igraph_t *graph, const char *name) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *gal=&attr->gal; + long int j; + igraph_attribute_record_t *rec; + bsvector_t *str; + igraph_bool_t l=igraph_haskell_attribute_find(gal, name, &j); + + if (!l) { + igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec=VECTOR(*gal)[j]; + str=(bsvector_t*)rec->value; + return BS(*str, 0); +} + +/** + * \function igraph_haskell_attribute_VAN + * Query a numeric vertex attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param vid The id of the queried vertex. + * \return The value of the attribute. + * + * \sa \ref VAN macro for a simpler interface. + * + * Time complexity: O(Av), the number of vertex attributes. + */ +igraph_real_t igraph_haskell_attribute_VAN(const igraph_t *graph, const char *name, + igraph_integer_t vid) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_VAB + * Query a boolean vertex attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param vid The id of the queried vertex. + * \return The value of the attribute. + * + * \sa \ref VAB macro for a simpler interface. + * + * Time complexity: O(Av), the number of vertex attributes. + */ +igraph_bool_t igraph_haskell_attribute_VAB(const igraph_t *graph, const char *name, + igraph_integer_t vid) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_VAS + * Query a string vertex attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param vid The id of the queried vertex. + * \return The value of the attribute. + * + * \sa The macro \ref VAS for a simpler interface. + * + * Time complexity: O(Av), the number of vertex attributes. + */ +const bytestring_t* igraph_haskell_attribute_VAS(const igraph_t *graph, const char *name, + igraph_integer_t vid) { + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *val=&attr->val; + long int j; + igraph_attribute_record_t *rec; + bsvector_t *str; + igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j); + + if (!l) { + igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec=VECTOR(*val)[j]; + str=(bsvector_t*)rec->value; + return BS(*str, (long int)vid); +} + +/** + * \function igraph_haskell_attribute_EAN + * Query a numeric edge attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param eid The id of the queried edge. + * \return The value of the attribute. + * + * \sa \ref EAN for an easier interface. + * + * Time complexity: O(Ae), the number of edge attributes. + */ +igraph_real_t igraph_haskell_attribute_EAN(const igraph_t *graph, const char *name, + igraph_integer_t eid) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_EAB + * Query a boolean edge attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param eid The id of the queried edge. + * \return The value of the attribute. + * + * \sa \ref EAB for an easier interface. + * + * Time complexity: O(Ae), the number of edge attributes. + */ +igraph_bool_t igraph_haskell_attribute_EAB(const igraph_t *graph, const char *name, + igraph_integer_t eid) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_EAS + * Query a string edge attribute. + * + * The attribute must exist, otherwise an error is triggered. + * \param graph The input graph. + * \param name The name of the attribute. + * \param eid The id of the queried edge. + * \return The value of the attribute. + * + * \se \ref EAS if you want to type less. + * + * Time complexity: O(Ae), the number of edge attributes. + */ +const bytestring_t* igraph_haskell_attribute_EAS(const igraph_t *graph, const char *name, + igraph_integer_t eid) { + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *eal=&attr->eal; + long int j; + igraph_attribute_record_t *rec; + bsvector_t *str; + igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j); + + if (!l) { + igraph_error("Unknown attribute", __FILE__, __LINE__, IGRAPH_EINVAL); + return 0; + } + + rec=VECTOR(*eal)[j]; + str=(bsvector_t*)rec->value; + return BS(*str, (long int)eid); +} + +/** + * \function igraph_haskell_attribute_VANV + * Query a numeric vertex attribute for many vertices + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param vids The vertices to query. + * \param result Pointer to an initialized vector, the result is + * stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(v), where v is the number of vertices in 'vids'. + */ + +int igraph_haskell_attribute_VANV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_t *result) { + + return igraph_haskell_attribute_get_numeric_vertex_attr(graph, name, vids, + result); +} + +/** + * \function igraph_haskell_attribute_VABV + * Query a boolean vertex attribute for many vertices + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param vids The vertices to query. + * \param result Pointer to an initialized boolean vector, the result is + * stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(v), where v is the number of vertices in 'vids'. + */ + +int igraph_haskell_attribute_VABV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_bool_t *result) { + + return igraph_haskell_attribute_get_bool_vertex_attr(graph, name, vids, + result); +} + +/** + * \function igraph_haskell_attribute_EANV + * Query a numeric edge attribute for many edges + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param eids The edges to query. + * \param result Pointer to an initialized vector, the result is + * stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(e), where e is the number of edges in 'eids'. + */ + +int igraph_haskell_attribute_EANV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_t *result) { + + return igraph_haskell_attribute_get_numeric_edge_attr(graph, name, eids, + result); +} + +/** + * \function igraph_haskell_attribute_EABV + * Query a boolean edge attribute for many edges + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param eids The edges to query. + * \param result Pointer to an initialized boolean vector, the result is + * stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(e), where e is the number of edges in 'eids'. + */ + +int igraph_haskell_attribute_EABV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_bool_t *result) { + + return igraph_haskell_attribute_get_bool_edge_attr(graph, name, eids, + result); +} + +/** + * \function igraph_haskell_attribute_VASV + * Query a string vertex attribute for many vertices + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param vids The vertices to query. + * \param result Pointer to an initialized string vector, the result + * is stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(v), where v is the number of vertices in 'vids'. + * (We assume that the string attributes have a bounded length.) + */ + +int igraph_haskell_attribute_VASV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_strvector_t *result) { + + return igraph_haskell_attribute_get_string_vertex_attr(graph, name, vids, + result); +} + +/** + * \function igraph_haskell_attribute_EASV + * Query a string edge attribute for many edges + * + * \param graph The input graph. + * \param name The name of the attribute. + * \param vids The edges to query. + * \param result Pointer to an initialized string vector, the result + * is stored here. It will be resized, if needed. + * \return Error code. + * + * Time complexity: O(e), where e is the number of edges in + * 'eids'. (We assume that the string attributes have a bounded length.) + */ + +int igraph_haskell_attribute_EASV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_strvector_t *result) { + + return igraph_haskell_attribute_get_string_edge_attr(graph, name, eids, + result); +} + +/** + * \function igraph_haskell_attribute_list + * List all attributes + * + * See \ref igraph_attribute_type_t for the various attribute types. + * \param graph The input graph. + * \param gnames String vector, the names of the graph attributes. + * \param gtypes Numeric vector, the types of the graph attributes. + * \param vnames String vector, the names of the vertex attributes. + * \param vtypes Numeric vector, the types of the vertex attributes. + * \param enames String vector, the names of the edge attributes. + * \param etypes Numeric vector, the types of the edge attributes. + * \return Error code. + * + * Naturally, the string vector with the attribute names and the + * numeric vector with the attribute types are in the right order, + * i.e. the first name corresponds to the first type, etc. + * + * Time complexity: O(Ag+Av+Ae), the number of all attributes. + */ +int igraph_haskell_attribute_list(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) { + return igraph_haskell_attribute_get_info(graph, gnames, gtypes, vnames, vtypes, + enames, etypes); +} + +/** + * \function igraph_haskell_attribute_has_attr + * Checks whether a (graph, vertex or edge) attribute exists + * + * \param graph The graph. + * \param type The type of the attribute, \c IGRAPH_ATTRIBUTE_GRAPH, + * \c IGRAPH_ATTRIBUTE_VERTEX or \c IGRAPH_ATTRIBUTE_EDGE. + * \param name Character constant, the name of the attribute. + * \return Logical value, TRUE if the attribute exists, FALSE otherwise. + * + * Time complexity: O(A), the number of (graph, vertex or edge) + * attributes, assuming attribute names are not too long. +igraph_bool_t igraph_haskell_attribute_has_attr(const igraph_t *graph, + igraph_attribute_elemtype_t type, + const char *name) { + return igraph_haskell_attribute_has_attr(graph, type, name); +} + */ + +/** + * \function igraph_haskell_attribute_GAN_set + * Set a numeric graph attribute + * + * \param graph The graph. + * \param name Name of the graph attribute. If there is no such + * attribute yet, then it will be added. + * \param value The (new) value of the graph attribute. + * \return Error code. + * + * \se \ref SETGAN if you want to type less. + * + * Time complexity: O(1). + */ +int igraph_haskell_attribute_GAN_set(igraph_t *graph, const char *name, + igraph_real_t value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_GAB_set + * Set a boolean graph attribute + * + * \param graph The graph. + * \param name Name of the graph attribute. If there is no such + * attribute yet, then it will be added. + * \param value The (new) value of the graph attribute. + * \return Error code. + * + * \se \ref SETGAN if you want to type less. + * + * Time complexity: O(1). + */ +int igraph_haskell_attribute_GAB_set(igraph_t *graph, const char *name, + igraph_bool_t value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_GAS_set + * Set a string graph attribute. + * + * \param graph The graph. + * \param name Name of the graph attribute. If there is no such + * attribute yet, then it will be added. + * \param value The (new) value of the graph attribute. It will be + * copied. + * \return Error code. + * + * \se \ref SETGAS if you want to type less. + * + * Time complexity: O(1). + */ +int igraph_haskell_attribute_GAS_set(igraph_t *graph, const char *name, + const bytestring_t *value) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *gal=&attr->gal; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(gal, name, &j); + + if (l) { + igraph_attribute_record_t *rec=VECTOR(*gal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + bsvector_t *str=(bsvector_t*)rec->value; + IGRAPH_CHECK(bsvector_set(str, 0, value)); + } + } else { + igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); + bsvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name=strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type=IGRAPH_ATTRIBUTE_STRING; + str=igraph_Calloc(1, bsvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + BSVECTOR_INIT_FINALLY(str, 1); + IGRAPH_CHECK(bsvector_set(str, 0, value)); + rec->value=str; + IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_haskell_attribute_VAN_set + * Set a numeric vertex attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all vertices + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param vid Vertices for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETVAN for a simpler way. + * + * Time complexity: O(n), the number of vertices if the attribute is + * new, O(|vid|) otherwise. + */ +int igraph_haskell_attribute_VAN_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_real_t value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_VAB_set + * Set a boolean vertex attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all vertices + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param vid Vertices for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETVAB for a simpler way. + * + * Time complexity: O(n), the number of vertices if the attribute is + * new, O(|vid|) otherwise. + */ +int igraph_haskell_attribute_VAB_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_bool_t value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_VAS_set + * Set a string vertex attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all vertices + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param vid Vertices for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETVAS for a simpler way. + * + * Time complexity: O(n*l), n is the number of vertices, l is the + * length of the string to set. If the attribute if not new then only + * O(|vid|*l). + */ +int igraph_haskell_attribute_VAS_set(igraph_t *graph, const char *name, + igraph_integer_t vid, const bytestring_t *value) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *val=&attr->val; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j); + + if (l) { + igraph_attribute_record_t *rec=VECTOR(*val)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + bsvector_t *str=(bsvector_t*)rec->value; + IGRAPH_CHECK(bsvector_set(str, vid, value)); + } + } else { + igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); + bsvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name=strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type=IGRAPH_ATTRIBUTE_STRING; + str=igraph_Calloc(1, bsvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + BSVECTOR_INIT_FINALLY(str, igraph_vcount(graph)); + IGRAPH_CHECK(bsvector_set(str, vid, value)); + rec->value=str; + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_haskell_attribute_EAN_set + * Set a numeric edge attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all edges + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param eid Edges for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETEAN for a simpler way. + * + * Time complexity: O(e), the number of edges if the attribute is + * new, O(|eid|) otherwise. + */ +int igraph_haskell_attribute_EAN_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_real_t value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_EAB_set + * Set a boolean edge attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all edges + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param eid Edges for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETEAB for a simpler way. + * + * Time complexity: O(e), the number of edges if the attribute is + * new, O(|eid|) otherwise. + */ +int igraph_haskell_attribute_EAB_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_bool_t value) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_EAS_set + * Set a string edge attribute + * + * The attribute will be added if not present already. If present it + * will be overwritten. The same \p value is set for all edges + * included in \p vid. + * \param graph The graph. + * \param name Name of the attribute. + * \param eid Edges for which to set the attribute. + * \param value The (new) value of the attribute. + * \return Error code. + * + * \sa \ref SETEAS for a simpler way. + * + * Time complexity: O(e*l), n is the number of edges, l is the + * length of the string to set. If the attribute if not new then only + * O(|eid|*l). + */ +int igraph_haskell_attribute_EAS_set(igraph_t *graph, const char *name, + igraph_integer_t eid, const bytestring_t *value) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *eal=&attr->eal; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j); + + if (l) { + igraph_attribute_record_t *rec=VECTOR(*eal)[j]; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); + } else { + bsvector_t *str=(bsvector_t*)rec->value; + IGRAPH_CHECK(bsvector_set(str, eid, value)); + } + } else { + igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); + bsvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->name=strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + rec->type=IGRAPH_ATTRIBUTE_STRING; + str=igraph_Calloc(1, bsvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + BSVECTOR_INIT_FINALLY(str, igraph_ecount(graph)); + IGRAPH_CHECK(bsvector_set(str, eid, value)); + rec->value=str; + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_haskell_attribute_VAN_setv + * Set a numeric vertex attribute for all vertices. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param v The new attribute values. The length of this vector must + * match the number of vertices. + * \return Error code. + * + * \sa \ref SETVANV for a simpler way. + * + * Time complexity: O(n), the number of vertices. + */ + +int igraph_haskell_attribute_VAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} +/** + * \function igraph_haskell_attribute_VAB_setv + * Set a boolean vertex attribute for all vertices. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param v The new attribute values. The length of this boolean vector must + * match the number of vertices. + * \return Error code. + * + * \sa \ref SETVANV for a simpler way. + * + * Time complexity: O(n), the number of vertices. + */ + +int igraph_haskell_attribute_VAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_VAS_setv + * Set a string vertex attribute for all vertices. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param sv String vector, the new attribute values. The length of this vector must + * match the number of vertices. + * \return Error code. + * + * \sa \ref SETVASV for a simpler way. + * + * Time complexity: O(n+l), n is the number of vertices, l is the + * total length of the strings. + */ +int igraph_haskell_attribute_VAS_setv(igraph_t *graph, const char *name, + const bsvector_t *sv) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *val=&attr->val; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j); + + /* Check length first */ + if (bsvector_size(sv) != igraph_vcount(graph)) { + IGRAPH_ERROR("Invalid vertex attribute vector length", IGRAPH_EINVAL); + } + + if (l) { + /* Already present, check type */ + igraph_attribute_record_t *rec=VECTOR(*val)[j]; + bsvector_t *str=(bsvector_t *)rec->value; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); + } + bsvector_clear(str); + IGRAPH_CHECK(bsvector_append(str, sv)); + } else { + /* Add it */ + igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); + bsvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->type=IGRAPH_ATTRIBUTE_STRING; + rec->name=strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + str=igraph_Calloc(1, bsvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + rec->value=str; + IGRAPH_CHECK(bsvector_copy(str, sv)); + IGRAPH_FINALLY(bsvector_destroy, str); + IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +/** + * \function igraph_haskell_attribute_EAN_setv + * Set a numeric edge attribute for all vertices. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param v The new attribute values. The length of this vector must + * match the number of edges. + * \return Error code. + * + * \sa \ref SETEANV for a simpler way. + * + * Time complexity: O(e), the number of edges. + */ +int igraph_haskell_attribute_EAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_EAB_setv + * Set a boolean edge attribute for all vertices. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param v The new attribute values. The length of this vector must + * match the number of edges. + * \return Error code. + * + * \sa \ref SETEABV for a simpler way. + * + * Time complexity: O(e), the number of edges. + */ +int igraph_haskell_attribute_EAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v) { + IGRAPH_ERROR("Not implemented", IGRAPH_ENOMEM); + return 1; +} + +/** + * \function igraph_haskell_attribute_EAS_setv + * Set a string edge attribute for all vertices. + * + * The attribute will be added if not present yet. + * \param graph The graph. + * \param name Name of the attribute. + * \param sv String vector, the new attribute values. The length of this vector must + * match the number of edges. + * \return Error code. + * + * \sa \ref SETEASV for a simpler way. + * + * Time complexity: O(e+l), e is the number of edges, l is the + * total length of the strings. + */ +int igraph_haskell_attribute_EAS_setv(igraph_t *graph, const char *name, + const bsvector_t *sv) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *eal=&attr->eal; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j); + + /* Check length first */ + if (bsvector_size(sv) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid edge attribute vector length", IGRAPH_EINVAL); + } + + if (l) { + /* Already present, check type */ + igraph_attribute_record_t *rec=VECTOR(*eal)[j]; + bsvector_t *str=(bsvector_t *)rec->value; + if (rec->type != IGRAPH_ATTRIBUTE_STRING) { + IGRAPH_ERROR("Attribute type mismatch", IGRAPH_EINVAL); + } + bsvector_clear(str); + IGRAPH_CHECK(bsvector_append(str, sv)); + } else { + /* Add it */ + igraph_attribute_record_t *rec=igraph_Calloc(1, igraph_attribute_record_t); + bsvector_t *str; + if (!rec) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + rec->type=IGRAPH_ATTRIBUTE_STRING; + rec->name=strdup(name); + if (!rec->name) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char*)rec->name); + str=igraph_Calloc(1, bsvector_t); + if (!str) { + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, str); + rec->value=str; + IGRAPH_CHECK(bsvector_copy(str, sv)); + IGRAPH_FINALLY(bsvector_destroy, str); + IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); + IGRAPH_FINALLY_CLEAN(4); + } + + return 0; +} + +void igraph_haskell_attribute_free_rec(igraph_attribute_record_t *rec) { + + if (rec->type==IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *num=(igraph_vector_t*)rec->value; + igraph_vector_destroy(num); + } else if (rec->type==IGRAPH_ATTRIBUTE_STRING) { + bsvector_t *str=(bsvector_t*)rec->value; + bsvector_destroy(str); + } else if (rec->type==IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *boolvec=(igraph_vector_bool_t*)rec->value; + igraph_vector_bool_destroy(boolvec); + } + igraph_Free(rec->name); + igraph_Free(rec->value); + igraph_Free(rec); +} + +/** + * \function igraph_haskell_attribute_remove_g + * Remove a graph attribute + * + * \param graph The graph object. + * \param name Name of the graph attribute to remove. + * + * \sa \ref DELGA for a simpler way. + * + */ +void igraph_haskell_attribute_remove_g(igraph_t *graph, const char *name) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *gal=&attr->gal; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(gal, name, &j); + + if (l) { + igraph_haskell_attribute_free_rec(VECTOR(*gal)[j]); + igraph_vector_ptr_remove(gal, j); + } else { + IGRAPH_WARNING("Cannot remove non-existent graph attribute"); + } +} + +/** + * \function igraph_haskell_attribute_remove_v + * Remove a vertex attribute + * + * \param graph The graph object. + * \param name Name of the vertex attribute to remove. + * + * \sa \ref DELVA for a simpler way. + * + */ +void igraph_haskell_attribute_remove_v(igraph_t *graph, const char *name) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *val=&attr->val; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(val, name, &j); + + if (l) { + igraph_haskell_attribute_free_rec(VECTOR(*val)[j]); + igraph_vector_ptr_remove(val, j); + } else { + IGRAPH_WARNING("Cannot remove non-existent graph attribute"); + } +} + +/** + * \function igraph_haskell_attribute_remove_e + * Remove an edge attribute + * + * \param graph The graph object. + * \param name Name of the edge attribute to remove. + * + * \sa \ref DELEA for a simpler way. + * + */ +void igraph_haskell_attribute_remove_e(igraph_t *graph, const char *name) { + + igraph_haskell_attributes_t *attr=graph->attr; + igraph_vector_ptr_t *eal=&attr->eal; + long int j; + igraph_bool_t l=igraph_haskell_attribute_find(eal, name, &j); + + if (l) { + igraph_haskell_attribute_free_rec(VECTOR(*eal)[j]); + igraph_vector_ptr_remove(eal, j); + } else { + IGRAPH_WARNING("Cannot remove non-existent graph attribute"); + } +} + +/** + * \function igraph_haskell_attribute_remove_all + * Remove all graph/vertex/edge attributes + * + * \param graph The graph object. + * \param g Boolean, whether to remove graph attributes. + * \param v Boolean, whether to remove vertex attributes. + * \param e Boolean, whether to remove edge attributes. + * + * \sa \ref DELGAS, \ref DELVAS, \ref DELEAS, \ref DELALL for simpler + * ways. + */ +void igraph_haskell_attribute_remove_all(igraph_t *graph, igraph_bool_t g, + igraph_bool_t v, igraph_bool_t e) { + + igraph_haskell_attributes_t *attr=graph->attr; + + if (g) { + igraph_vector_ptr_t *gal=&attr->gal; + long int i, n=igraph_vector_ptr_size(gal); + for (i=0;i<n;i++) { + igraph_haskell_attribute_free_rec(VECTOR(*gal)[i]); + } + igraph_vector_ptr_clear(gal); + } + if (v) { + igraph_vector_ptr_t *val=&attr->val; + long int i, n=igraph_vector_ptr_size(val); + for (i=0;i<n;i++) { + igraph_haskell_attribute_free_rec(VECTOR(*val)[i]); + } + igraph_vector_ptr_clear(val); + } + if (e) { + igraph_vector_ptr_t *eal=&attr->eal; + long int i, n=igraph_vector_ptr_size(eal); + for (i=0;i<n;i++) { + igraph_haskell_attribute_free_rec(VECTOR(*eal)[i]); + } + igraph_vector_ptr_clear(eal); + } +} diff --git a/cbits/haskell_attributes.h b/cbits/haskell_attributes.h new file mode 100644 index 0000000..861149c --- /dev/null +++ b/cbits/haskell_attributes.h @@ -0,0 +1,218 @@ +#ifndef HASKELL_IGRAPH_ATTRIBUTE +#define HASKELL_IGRAPH_ATTRIBUTE + +#include "igraph/igraph.h" +#include "bytestring.h" + +#include <string.h> + +igraph_bool_t igraph_haskell_attribute_find(const igraph_vector_ptr_t *ptrvec, + const char *name, long int *idx); + +typedef struct igraph_haskell_attributes_t { + igraph_vector_ptr_t gal; + igraph_vector_ptr_t val; + igraph_vector_ptr_t eal; +} igraph_haskell_attributes_t; + +int igraph_haskell_attributes_copy_attribute_record(igraph_attribute_record_t **newrec, + const igraph_attribute_record_t *rec); + + +int igraph_haskell_attribute_init(igraph_t *graph, igraph_vector_ptr_t *attr); + +void igraph_haskell_attribute_destroy(igraph_t *graph); + +void igraph_haskell_attribute_copy_free(igraph_haskell_attributes_t *attr); + +int igraph_haskell_attribute_copy(igraph_t *to, const igraph_t *from, + igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea); + +int igraph_haskell_attribute_add_vertices(igraph_t *graph, long int nv, + igraph_vector_ptr_t *nattr); + +void igraph_haskell_attribute_permute_free(igraph_vector_ptr_t *v); + +int igraph_haskell_attribute_permute_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx); + +int igraph_haskell_attribute_combine_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb); + +int igraph_haskell_attribute_add_edges(igraph_t *graph, const igraph_vector_t *edges, + igraph_vector_ptr_t *nattr); + +int igraph_haskell_attribute_permute_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx); + +int igraph_haskell_attribute_combine_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb); + +int igraph_haskell_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); + +igraph_bool_t igraph_haskell_attribute_has_attr(const igraph_t *graph, + igraph_attribute_elemtype_t type, + const char *name); + +int igraph_haskell_attribute_gettype(const igraph_t *graph, + igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, + const char *name); + +int igraph_haskell_attribute_get_numeric_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_t *value); + +int igraph_haskell_attribute_get_bool_graph_attr(const igraph_t *graph, + const char *name, + igraph_vector_bool_t *value); + +int igraph_haskell_attribute_get_string_graph_attr(const igraph_t *graph, + const char *name, + igraph_strvector_t *value_); + +int igraph_haskell_attribute_get_numeric_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_t *value); + +int igraph_haskell_attribute_get_bool_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value); + +int igraph_haskell_attribute_get_string_vertex_attr(const igraph_t *graph, + const char *name, + igraph_vs_t vs, + igraph_strvector_t *value_); + +int igraph_haskell_attribute_get_numeric_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_t *value); + +int igraph_haskell_attribute_get_string_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_strvector_t *value_); + +int igraph_haskell_attribute_get_bool_edge_attr(const igraph_t *graph, + const char *name, + igraph_es_t es, + igraph_vector_bool_t *value); + +igraph_real_t igraph_haskell_attribute_GAN(const igraph_t *graph, const char *name); + +igraph_bool_t igraph_haskell_attribute_GAB(const igraph_t *graph, const char *name); + +const bytestring_t* igraph_haskell_attribute_GAS(const igraph_t *graph, const char *name); + +igraph_real_t igraph_haskell_attribute_VAN(const igraph_t *graph, const char *name, + igraph_integer_t vid); + +igraph_bool_t igraph_haskell_attribute_VAB(const igraph_t *graph, const char *name, + igraph_integer_t vid); + +const bytestring_t* igraph_haskell_attribute_VAS(const igraph_t *graph, const char *name, + igraph_integer_t vid); + +igraph_real_t igraph_haskell_attribute_EAN(const igraph_t *graph, const char *name, + igraph_integer_t eid); + +igraph_bool_t igraph_haskell_attribute_EAB(const igraph_t *graph, const char *name, + igraph_integer_t eid); + +const bytestring_t* igraph_haskell_attribute_EAS(const igraph_t *graph, const char *name, + igraph_integer_t eid); + +int igraph_haskell_attribute_VANV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_t *result); + +int igraph_haskell_attribute_VABV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_vector_bool_t *result); + +int igraph_haskell_attribute_EANV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_t *result); + +int igraph_haskell_attribute_EABV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_vector_bool_t *result); + +int igraph_haskell_attribute_VASV(const igraph_t *graph, const char *name, + igraph_vs_t vids, igraph_strvector_t *result); + +int igraph_haskell_attribute_EASV(const igraph_t *graph, const char *name, + igraph_es_t eids, igraph_strvector_t *result); + +int igraph_haskell_attribute_list(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); + +int igraph_haskell_attribute_GAN_set(igraph_t *graph, const char *name, + igraph_real_t value); + +int igraph_haskell_attribute_GAB_set(igraph_t *graph, const char *name, + igraph_bool_t value); + +int igraph_haskell_attribute_GAS_set(igraph_t *graph, const char *name, + const bytestring_t *value); + +int igraph_haskell_attribute_VAN_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_real_t value); + +int igraph_haskell_attribute_VAB_set(igraph_t *graph, const char *name, + igraph_integer_t vid, igraph_bool_t value); + +int igraph_haskell_attribute_VAS_set(igraph_t *graph, const char *name, + igraph_integer_t vid, const bytestring_t *value); + +int igraph_haskell_attribute_EAN_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_real_t value); + +int igraph_haskell_attribute_EAB_set(igraph_t *graph, const char *name, + igraph_integer_t eid, igraph_bool_t value); + +int igraph_haskell_attribute_EAS_set(igraph_t *graph, const char *name, + igraph_integer_t eid, const bytestring_t *value); + +int igraph_haskell_attribute_VAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v); + +int igraph_haskell_attribute_VAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v); + +int igraph_haskell_attribute_VAS_setv(igraph_t *graph, const char *name, + const bsvector_t *sv); + +int igraph_haskell_attribute_EAN_setv(igraph_t *graph, const char *name, + const igraph_vector_t *v); + +int igraph_haskell_attribute_EAB_setv(igraph_t *graph, const char *name, + const igraph_vector_bool_t *v); + +int igraph_haskell_attribute_EAS_setv(igraph_t *graph, const char *name, + const bsvector_t *sv); + +void igraph_haskell_attribute_free_rec(igraph_attribute_record_t *rec); + +void igraph_haskell_attribute_remove_g(igraph_t *graph, const char *name); + +void igraph_haskell_attribute_remove_v(igraph_t *graph, const char *name); + +void igraph_haskell_attribute_remove_e(igraph_t *graph, const char *name); + +void igraph_haskell_attribute_remove_all(igraph_t *graph, igraph_bool_t g, + igraph_bool_t v, igraph_bool_t e); +#endif diff --git a/cbits/haskell_igraph.c b/cbits/haskell_igraph.c new file mode 100644 index 0000000..fc065bb --- /dev/null +++ b/cbits/haskell_igraph.c @@ -0,0 +1,57 @@ +#include <igraph/igraph.h> +#include "haskell_attributes.h" + +igraph_integer_t igraph_get_eid_(igraph_t* graph, igraph_integer_t pfrom, igraph_integer_t pto, + igraph_bool_t directed, igraph_bool_t error) +{ + igraph_integer_t eid; + igraph_get_eid(graph, &eid, pfrom, pto, directed, error); + return eid; +} + +char** igraph_strvector_get_(igraph_strvector_t* s, long int i) +{ + char** x = (char**) malloc (sizeof(char*)); + igraph_strvector_get(s, i, x); + return x; +} + +igraph_arpack_options_t* igraph_arpack_new() +{ + igraph_arpack_options_t *arpack = (igraph_arpack_options_t*) malloc(sizeof(igraph_arpack_options_t)); + igraph_arpack_options_init(arpack); + return arpack; +} + +void igraph_arpack_destroy(igraph_arpack_options_t* arpack) +{ + if (arpack) + free(arpack); + arpack = NULL; +} + +const igraph_attribute_table_t igraph_haskell_attribute_table={ + &igraph_haskell_attribute_init, &igraph_haskell_attribute_destroy, + &igraph_haskell_attribute_copy, &igraph_haskell_attribute_add_vertices, + &igraph_haskell_attribute_permute_vertices, + &igraph_haskell_attribute_combine_vertices, &igraph_haskell_attribute_add_edges, + &igraph_haskell_attribute_permute_edges, + &igraph_haskell_attribute_combine_edges, + &igraph_haskell_attribute_get_info, + &igraph_haskell_attribute_has_attr, &igraph_haskell_attribute_gettype, + &igraph_haskell_attribute_get_numeric_graph_attr, + &igraph_haskell_attribute_get_string_graph_attr, + &igraph_haskell_attribute_get_bool_graph_attr, + &igraph_haskell_attribute_get_numeric_vertex_attr, + &igraph_haskell_attribute_get_string_vertex_attr, + &igraph_haskell_attribute_get_bool_vertex_attr, + &igraph_haskell_attribute_get_numeric_edge_attr, + &igraph_haskell_attribute_get_string_edge_attr, + &igraph_haskell_attribute_get_bool_edge_attr +}; + +void haskelligraph_init() +{ + /* attach attribute table */ + igraph_i_set_attribute_table(&igraph_haskell_attribute_table); +} diff --git a/cbits/haskelligraph.h b/cbits/haskell_igraph.h similarity index 100% rename from cbits/haskelligraph.h rename to cbits/haskell_igraph.h diff --git a/cbits/haskelligraph.c b/cbits/haskelligraph.c deleted file mode 100644 index dae88ac..0000000 --- a/cbits/haskelligraph.c +++ /dev/null @@ -1,36 +0,0 @@ -#include <igraph/igraph.h> - -igraph_integer_t igraph_get_eid_(igraph_t* graph, igraph_integer_t pfrom, igraph_integer_t pto, - igraph_bool_t directed, igraph_bool_t error) -{ - igraph_integer_t eid; - igraph_get_eid(graph, &eid, pfrom, pto, directed, error); - return eid; -} - -char** igraph_strvector_get_(igraph_strvector_t* s, long int i) -{ - char** x = (char**) malloc (sizeof(char*)); - igraph_strvector_get(s, i, x); - return x; -} - -igraph_arpack_options_t* igraph_arpack_new() -{ - igraph_arpack_options_t *arpack = (igraph_arpack_options_t*) malloc(sizeof(igraph_arpack_options_t)); - igraph_arpack_options_init(arpack); - return arpack; -} - -void igraph_arpack_destroy(igraph_arpack_options_t* arpack) -{ - if (arpack) - free(arpack); - arpack = NULL; -} - -void haskelligraph_init() -{ - /* attach attribute table */ - igraph_i_set_attribute_table(&igraph_cattribute_table); -} diff --git a/haskell-igraph.cabal b/haskell-igraph.cabal index f179bec..6a0822d 100644 --- a/haskell-igraph.cabal +++ b/haskell-igraph.cabal @@ -1,8 +1,5 @@ --- Initial igraph-bindings.cabal generated by cabal init. For further --- documentation, see http://haskell.org/cabal/users-guide/ - name: haskell-igraph -version: 0.3.1-dev +version: 0.4.0-dev synopsis: Imcomplete igraph bindings description: This is an attempt to create a complete bindings for the igraph<"http://igraph.org/c/"> library. @@ -14,7 +11,10 @@ copyright: (c) 2016-2017 Kai Zhang category: Math build-type: Simple cabal-version: >=1.24 -extra-source-files: cbits/haskelligraph.h +extra-source-files: + cbits/haskell_igraph.h + cbits/bytestring.h + cbits/haskell_attributes.h Flag graphics Description: Enable graphics output @@ -55,9 +55,9 @@ library build-depends: base >=4.0 && <5.0 - , binary , bytestring >=0.9 , bytestring-lexing >=0.5 + , cereal , colour , primitive , unordered-containers @@ -70,7 +70,10 @@ library hs-source-dirs: src default-language: Haskell2010 build-tools: c2hs >=0.25.0 - c-sources: cbits/haskelligraph.c + c-sources: + cbits/haskell_igraph.c + cbits/haskell_attributes.c + cbits/bytestring.c include-dirs: cbits test-suite tests @@ -79,6 +82,7 @@ test-suite tests main-is: test.hs other-modules: Test.Basic + Test.Attributes Test.Structure Test.Isomorphism Test.Motif diff --git a/src/IGraph.hs b/src/IGraph.hs index 59b0ee1..4f53dbe 100644 --- a/src/IGraph.hs +++ b/src/IGraph.hs @@ -28,14 +28,15 @@ module IGraph ) where import Control.Arrow ((***)) -import Control.Monad (forM_, liftM) +import Control.Monad (forM, forM_, liftM) import Control.Monad.Primitive import Control.Monad.ST (runST) -import Data.Binary import Data.Hashable (Hashable) import qualified Data.HashMap.Strict as M -import qualified Data.HashSet as S +import qualified Data.HashSet as S import Data.Maybe +import Data.Serialize +import Foreign (with) import System.IO.Unsafe (unsafePerformIO) import IGraph.Internal.Attribute @@ -53,22 +54,6 @@ data LGraph d v e = LGraph , _labelToNode :: M.HashMap v [Node] } -instance ( Binary v, Hashable v, Read v, Show v, Eq v - , Binary e, Read e, Show e, Graph d) => Binary (LGraph d v e) where - put gr = do - put nlabs - put es - put elabs - where - nlabs = map (nodeLab gr) $ nodes gr - es = edges gr - elabs = map (edgeLab gr) es - get = do - nlabs <- get - es <- get - elabs <- get - return $ mkGraph nlabs $ zip es elabs - class MGraph d => Graph d where isDirected :: LGraph d v e -> Bool isD :: d -> Bool @@ -97,13 +82,14 @@ class MGraph d => Graph d where | otherwise = True {-# INLINE hasEdge #-} - nodeLab :: Read v => LGraph d v e -> Node -> v - nodeLab (LGraph g _) i = read $ igraphCattributeVAS g vertexAttr i + nodeLab :: Serialize v => LGraph d v e -> Node -> v + nodeLab (LGraph g _) i = unsafePerformIO $ + igraphHaskellAttributeVAS g vertexAttr i >>= fromBS {-# INLINE nodeLab #-} - nodeLabMaybe :: Read v => LGraph d v e -> Node -> Maybe v + nodeLabMaybe :: Serialize v => LGraph d v e -> Node -> Maybe v nodeLabMaybe gr@(LGraph g _) i = - if igraphCattributeHasAttr g IgraphAttributeVertex vertexAttr + if igraphHaskellAttributeHasAttr g IgraphAttributeVertex vertexAttr then Just $ nodeLab gr i else Nothing {-# INLINE nodeLabMaybe #-} @@ -112,20 +98,22 @@ class MGraph d => Graph d where getNodes gr x = M.lookupDefault [] x $ _labelToNode gr {-# INLINE getNodes #-} - edgeLab :: Read e => LGraph d v e -> Edge -> e - edgeLab (LGraph g _) (fr,to) = read $ igraphCattributeEAS g edgeAttr $ - igraphGetEid g fr to True True + edgeLab :: Serialize e => LGraph d v e -> Edge -> e + edgeLab (LGraph g _) (fr,to) = unsafePerformIO $ + igraphHaskellAttributeEAS g edgeAttr (igraphGetEid g fr to True True) >>= + fromBS {-# INLINE edgeLab #-} - edgeLabMaybe :: Read e => LGraph d v e -> Edge -> Maybe e + edgeLabMaybe :: Serialize e => LGraph d v e -> Edge -> Maybe e edgeLabMaybe gr@(LGraph g _) i = - if igraphCattributeHasAttr g IgraphAttributeEdge edgeAttr + if igraphHaskellAttributeHasAttr g IgraphAttributeEdge edgeAttr then Just $ edgeLab gr i else Nothing {-# INLINE edgeLabMaybe #-} - edgeLabByEid :: Read e => LGraph d v e -> Int -> e - edgeLabByEid (LGraph g _) i = read $ igraphCattributeEAS g edgeAttr i + edgeLabByEid :: Serialize e => LGraph d v e -> Int -> e + edgeLabByEid (LGraph g _) i = unsafePerformIO $ + igraphHaskellAttributeEAS g edgeAttr i >>= fromBS {-# INLINE edgeLabByEid #-} @@ -137,11 +125,11 @@ instance Graph D where isDirected = const True isD = const True -empty :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e) +empty :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e) => LGraph d v e empty = runST $ new 0 >>= unsafeFreeze -mkGraph :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e) +mkGraph :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e) => [v] -> [(Edge, e)] -> LGraph d v e mkGraph vattr es = runST $ do g <- new 0 @@ -151,7 +139,7 @@ mkGraph vattr es = runST $ do where n = length vattr -fromLabeledEdges :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e) +fromLabeledEdges :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e) => [((v, v), e)] -> LGraph d v e fromLabeledEdges es = mkGraph labels es' where @@ -160,14 +148,17 @@ fromLabeledEdges es = mkGraph labels es' labels = S.toList $ S.fromList $ concat [ [a,b] | ((a,b),_) <- es ] labelToId = M.fromList $ zip labels [0..] -unsafeFreeze :: (Hashable v, Eq v, Read v, PrimMonad m) => MLGraph (PrimState m) d v e -> m (LGraph d v e) +unsafeFreeze :: (Hashable v, Eq v, Serialize v, PrimMonad m) + => MLGraph (PrimState m) d v e -> m (LGraph d v e) unsafeFreeze (MLGraph g) = return $ LGraph g labToId where labToId = M.fromListWith (++) $ zip labels $ map return [0..nV-1] nV = igraphVcount g - labels = map (read . igraphCattributeVAS g vertexAttr) [0 .. nV-1] + labels = unsafePerformIO $ forM [0 .. nV - 1] $ \i -> + igraphHaskellAttributeVAS g vertexAttr i >>= fromBS -freeze :: (Hashable v, Eq v, Read v, PrimMonad m) => MLGraph (PrimState m) d v e -> m (LGraph d v e) +freeze :: (Hashable v, Eq v, Serialize v, PrimMonad m) + => MLGraph (PrimState m) d v e -> m (LGraph d v e) freeze (MLGraph g) = do g' <- unsafePrimToPrim $ igraphCopy g unsafeFreeze (MLGraph g') @@ -200,7 +191,7 @@ pre gr i = unsafePerformIO $ do vitToList vit -- | Keep nodes that satisfy the constraint -filterNodes :: (Hashable v, Eq v, Read v, Graph d) +filterNodes :: (Hashable v, Eq v, Serialize v, Graph d) => (LGraph d v e -> Node -> Bool) -> LGraph d v e -> LGraph d v e filterNodes f gr = runST $ do let deleted = filter (not . f gr) $ nodes gr @@ -209,7 +200,7 @@ filterNodes f gr = runST $ do unsafeFreeze gr' -- | Apply a function to change nodes' labels. -mapNodes :: (Graph d, Read v1, Show v2, Hashable v2, Eq v2, Read v2) +mapNodes :: (Graph d, Serialize v1, Serialize v2, Hashable v2, Eq v2) => (Node -> v1 -> v2) -> LGraph d v1 e -> LGraph d v2 e mapNodes f gr = runST $ do (MLGraph gptr) <- thaw gr @@ -218,7 +209,7 @@ mapNodes f gr = runST $ do unsafeFreeze gr' -- | Apply a function to change edges' labels. -mapEdges :: (Graph d, Read e1, Show e2, Hashable v, Eq v, Read v) +mapEdges :: (Graph d, Serialize e1, Serialize e2, Hashable v, Eq v, Serialize v) => (Edge -> e1 -> e2) -> LGraph d v e1 -> LGraph d v e2 mapEdges f gr = runST $ do (MLGraph gptr) <- thaw gr @@ -230,7 +221,7 @@ mapEdges f gr = runST $ do -- | Keep nodes that satisfy the constraint -filterEdges :: (Hashable v, Eq v, Read v, Graph d) +filterEdges :: (Hashable v, Eq v, Serialize v, Graph d) => (LGraph d v e -> Edge -> Bool) -> LGraph d v e -> LGraph d v e filterEdges f gr = runST $ do let deleted = filter (not . f gr) $ edges gr @@ -239,22 +230,24 @@ filterEdges f gr = runST $ do unsafeFreeze gr' -- | Map a function over the node labels in a graph -nmap :: (Graph d, Read v, Hashable u, Read u, Eq u, Show u) +nmap :: (Graph d, Serialize v, Hashable u, Serialize u, Eq u) => ((Node, v) -> u) -> LGraph d v e -> LGraph d u e nmap fn gr = unsafePerformIO $ do (MLGraph g) <- thaw gr forM_ (nodes gr) $ \i -> do let label = fn (i, nodeLab gr i) - igraphCattributeVASSet g vertexAttr i (show label) + bs <- unsafeToBS label + with bs (igraphHaskellAttributeVASSet g vertexAttr i) unsafeFreeze (MLGraph g) -- | Map a function over the edge labels in a graph -emap :: (Graph d, Read v, Hashable v, Eq v, Read e1, Show e2) +emap :: (Graph d, Serialize v, Hashable v, Eq v, Serialize e1, Serialize e2) => ((Edge, e1) -> e2) -> LGraph d v e1 -> LGraph d v e2 emap fn gr = unsafePerformIO $ do (MLGraph g) <- thaw gr forM_ (edges gr) $ \(fr, to) -> do let label = fn ((fr,to), edgeLabByEid gr i) i = igraphGetEid g fr to True True - igraphCattributeEASSet g edgeAttr i (show label) + bs <- unsafeToBS label + with bs (igraphHaskellAttributeEASSet g edgeAttr i) unsafeFreeze (MLGraph g) diff --git a/src/IGraph/Exporter/GEXF.hs b/src/IGraph/Exporter/GEXF.hs index db5f96a..cf8db8c 100644 --- a/src/IGraph/Exporter/GEXF.hs +++ b/src/IGraph/Exporter/GEXF.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE FlexibleInstances #-} module IGraph.Exporter.GEXF ( NodeAttr(..) , defaultNodeAttributes @@ -7,22 +9,34 @@ module IGraph.Exporter.GEXF , writeGEXF ) where -import Data.Hashable -import Data.Colour (AlphaColour, black, over, alphaChannel, opaque) -import Data.Colour.SRGB (toSRGB24, channelRed, channelBlue, channelGreen) -import Text.XML.HXT.Core -import Data.Tree.NTree.TypeDefs -import Text.XML.HXT.DOM.TypeDefs -import IGraph +import Data.Colour (AlphaColour, alphaChannel, black, + opaque, over) +import Data.Colour.SRGB (channelBlue, channelGreen, + channelRed, toSRGB24) +import Data.Hashable +import Data.Serialize +import Data.Tree.NTree.TypeDefs +import GHC.Generics +import IGraph +import Text.XML.HXT.Core +import Text.XML.HXT.DOM.TypeDefs + +instance Serialize (AlphaColour Double) where + get = do + x <- get + return $ read x + put x = put $ show x data NodeAttr = NodeAttr - { _size :: Double + { _size :: Double , _nodeColour :: AlphaColour Double - , _nodeLabel :: String - , _positionX :: Double - , _positionY :: Double + , _nodeLabel :: String + , _positionX :: Double + , _positionY :: Double , _nodeZindex :: Int - } deriving (Show, Read, Eq) + } deriving (Show, Read, Eq, Generic) + +instance Serialize NodeAttr instance Hashable NodeAttr where hashWithSalt salt at = hashWithSalt salt $ _nodeLabel at @@ -38,12 +52,14 @@ defaultNodeAttributes = NodeAttr } data EdgeAttr = EdgeAttr - { _edgeLabel :: String - , _edgeColour :: AlphaColour Double - , _edgeWeight :: Double + { _edgeLabel :: String + , _edgeColour :: AlphaColour Double + , _edgeWeight :: Double , _edgeArrowLength :: Double - , _edgeZindex :: Int - } deriving (Show, Read, Eq) + , _edgeZindex :: Int + } deriving (Show, Read, Eq, Generic) + +instance Serialize EdgeAttr instance Hashable EdgeAttr where hashWithSalt salt at = hashWithSalt salt $ _edgeLabel at diff --git a/src/IGraph/Generators.hs b/src/IGraph/Generators.hs index 31f36f3..068894b 100644 --- a/src/IGraph/Generators.hs +++ b/src/IGraph/Generators.hs @@ -7,6 +7,7 @@ module IGraph.Generators import Control.Monad (when) import Data.Hashable (Hashable) +import Data.Serialize (Serialize) import IGraph import IGraph.Internal.Constants @@ -42,7 +43,7 @@ degreeSequenceGame out_deg in_deg = do unsafeFreeze $ MLGraph gp -- | Randomly rewires a graph while preserving the degree distribution. -rewire :: (Graph d, Hashable v, Read v, Eq v, Show v, Show e) +rewire :: (Graph d, Hashable v, Serialize v, Eq v, Serialize e) => Int -- ^ Number of rewiring trials to perform. -> LGraph d v e -> IO (LGraph d v e) diff --git a/src/IGraph/Internal/Arpack.chs b/src/IGraph/Internal/Arpack.chs index 0701e41..1296b72 100644 --- a/src/IGraph/Internal/Arpack.chs +++ b/src/IGraph/Internal/Arpack.chs @@ -5,7 +5,7 @@ import Control.Monad import Foreign import Foreign.C.Types -#include "haskelligraph.h" +#include "haskell_igraph.h" {#pointer *igraph_arpack_options_t as ArpackOptPtr foreign finalizer igraph_arpack_destroy newtype#} diff --git a/src/IGraph/Internal/Attribute.chs b/src/IGraph/Internal/Attribute.chs index c7ba66d..8e4b1a0 100644 --- a/src/IGraph/Internal/Attribute.chs +++ b/src/IGraph/Internal/Attribute.chs @@ -2,8 +2,10 @@ module IGraph.Internal.Attribute where import qualified Data.ByteString.Char8 as B +import Data.ByteString.Unsafe import Control.Monad import Control.Applicative +import Data.Serialize (Serialize, decode, encode) import Foreign import Foreign.C.Types import Foreign.C.String @@ -14,17 +16,39 @@ import System.IO.Unsafe (unsafePerformIO) {#import IGraph.Internal.Constants #} #include "igraph/igraph.h" +#include "haskell_attributes.h" -makeAttributeRecord :: Show a +-- The returned object will not be trackced by Haskell's GC. It should be freed +-- by foreign codes. +unsafeToBS :: Serialize a => a -> IO BSLen +unsafeToBS x = unsafeUseAsCStringLen bs $ \(ptr, n) -> do + newPtr <- mallocBytes n + copyBytes newPtr ptr n + return $ BSLen (newPtr, n) + where + bs = encode x +{-# INLINE unsafeToBS #-} + +fromBS :: Serialize a => Ptr BSLen -> IO a +fromBS ptr = do + BSLen x <- peek ptr + result <- decode <$> unsafePackCStringLen x + case result of + Left msg -> error msg + Right r -> return r +{-# INLINE fromBS #-} + +makeAttributeRecord :: Serialize a => String -- ^ name of the attribute -> [a] -- ^ values of the attribute -> AttributeRecord makeAttributeRecord name xs = unsafePerformIO $ do ptr <- newCAString name - value <- listToStrVector $ map (B.pack . show) xs + value <- mapM unsafeToBS xs >>= listToBSVector return $ AttributeRecord ptr 2 value +{-# INLINE makeAttributeRecord #-} -data AttributeRecord = AttributeRecord CString Int StrVectorPtr +data AttributeRecord = AttributeRecord CString Int BSVectorPtr instance Storable AttributeRecord where sizeOf _ = {#sizeof igraph_attribute_record_t #} @@ -34,27 +58,27 @@ instance Storable AttributeRecord where <*> liftM fromIntegral ({#get igraph_attribute_record_t->type #} p) <*> ( do ptr <- {#get igraph_attribute_record_t->value #} p fptr <- newForeignPtr_ . castPtr $ ptr - return $ StrVectorPtr fptr ) + return $ BSVectorPtr fptr ) poke p (AttributeRecord name t vptr) = do {#set igraph_attribute_record_t.name #} p name {#set igraph_attribute_record_t.type #} p $ fromIntegral t - withStrVectorPtr vptr $ \ptr -> + withBSVectorPtr vptr $ \ptr -> {#set igraph_attribute_record_t.value #} p $ castPtr ptr -{#fun pure igraph_cattribute_has_attr as ^ { `IGraphPtr', `AttributeElemtype', `String' } -> `Bool' #} +{#fun pure igraph_haskell_attribute_has_attr as ^ { `IGraphPtr', `AttributeElemtype', `String' } -> `Bool' #} -{#fun igraph_cattribute_GAN_set as ^ { `IGraphPtr', `String', `Double' } -> `Int' #} +{#fun igraph_haskell_attribute_GAN_set as ^ { `IGraphPtr', `String', `Double' } -> `Int' #} -{#fun pure igraph_cattribute_GAN as ^ { `IGraphPtr', `String' } -> `Double' #} +{#fun pure igraph_haskell_attribute_GAN as ^ { `IGraphPtr', `String' } -> `Double' #} -{#fun pure igraph_cattribute_VAS as ^ { `IGraphPtr', `String', `Int' } -> `String' #} +{#fun igraph_haskell_attribute_VAS as ^ { `IGraphPtr', `String', `Int' } -> `Ptr BSLen' castPtr #} -{#fun pure igraph_cattribute_EAN as ^ { `IGraphPtr', `String', `Int' } -> `Double' #} +{#fun pure igraph_haskell_attribute_EAN as ^ { `IGraphPtr', `String', `Int' } -> `Double' #} -{#fun pure igraph_cattribute_EAS as ^ { `IGraphPtr', `String', `Int' } -> `String' #} +{#fun igraph_haskell_attribute_EAS as ^ { `IGraphPtr', `String', `Int' } -> `Ptr BSLen' castPtr #} -{#fun igraph_cattribute_EAS_setv as ^ { `IGraphPtr', `String', `StrVectorPtr' } -> `Int' #} +{#fun igraph_haskell_attribute_EAS_setv as ^ { `IGraphPtr', `String', `BSVectorPtr' } -> `Int' #} -{#fun igraph_cattribute_VAS_set as ^ { `IGraphPtr', `String', `Int', `String' } -> `Int' #} +{#fun igraph_haskell_attribute_VAS_set as ^ { `IGraphPtr', `String', `Int', castPtr `Ptr BSLen' } -> `Int' #} -{#fun igraph_cattribute_EAS_set as ^ { `IGraphPtr', `String', `Int', `String' } -> `Int' #} +{#fun igraph_haskell_attribute_EAS_set as ^ { `IGraphPtr', `String', `Int', castPtr `Ptr BSLen' } -> `Int' #} diff --git a/src/IGraph/Internal/Data.chs b/src/IGraph/Internal/Data.chs index 3d2c0c8..e981a8e 100644 --- a/src/IGraph/Internal/Data.chs +++ b/src/IGraph/Internal/Data.chs @@ -10,7 +10,12 @@ import System.IO.Unsafe (unsafePerformIO) import Data.List (transpose) import Data.List.Split (chunksOf) -#include "haskelligraph.h" +#include "haskell_igraph.h" +#include "bytestring.h" + +-------------------------------------------------------------------------------- +-- Igraph vector +-------------------------------------------------------------------------------- {#pointer *igraph_vector_t as VectorPtr foreign finalizer igraph_vector_destroy newtype#} @@ -82,6 +87,10 @@ vectorPPtrToList vpptr = do vectorPtrToList $ VectorPtr fptr +-------------------------------------------------------------------------------- +-- Igraph string vector +-------------------------------------------------------------------------------- + {#pointer *igraph_strvector_t as StrVectorPtr foreign finalizer igraph_strvector_destroy newtype#} {#fun igraph_strvector_init as igraphStrvectorNew { +, `Int' } -> `StrVectorPtr' #} @@ -105,6 +114,46 @@ listToStrVector xs = do n = length xs +-------------------------------------------------------------------------------- +-- Customized string vector +-------------------------------------------------------------------------------- + +newtype BSLen = BSLen CStringLen + +instance Storable BSLen where + sizeOf _ = {#sizeof bytestring_t #} + alignment _ = {#alignof bytestring_t #} + peek p = do + n <- ({#get bytestring_t->len #} p) + ptr <- {#get bytestring_t->value #} p + return $ BSLen (ptr, fromIntegral n) + poke p (BSLen (ptr, n)) = {#set bytestring_t.len #} p (fromIntegral n) >> + {#set bytestring_t.value #} p ptr + +{#pointer *bsvector_t as BSVectorPtr foreign finalizer bsvector_destroy newtype#} + +{#fun bsvector_init as bsvectorNew { +, `Int' } -> `BSVectorPtr' #} + +--{#fun bsvector_get as bsVectorGet { `BSVectorPtr', `Int', + } -> `Ptr (Ptr BSLen)' id #} + +{- +bsVectorGet :: BSVectorPtr -> Int -> BSLen +bsVectorGet vec i = unsafePerformIO $ do + ptrptr <- bsVectorGet vec i + peek ptrptr >>= peek + -} + +{#fun bsvector_set as ^ { `BSVectorPtr', `Int', `Ptr ()'} -> `()' #} + +listToBSVector :: [BSLen] -> IO BSVectorPtr +listToBSVector xs = do + vec <- bsvectorNew n + forM_ (zip [0..] xs) $ \(i, x) -> with x $ \ptr -> bsvectorSet vec i $ castPtr ptr + return vec + where + n = length xs + + {#pointer *igraph_matrix_t as MatrixPtr foreign finalizer igraph_matrix_destroy newtype#} {#fun igraph_matrix_init as igraphMatrixNew { +, `Int', `Int' } -> `MatrixPtr' #} diff --git a/src/IGraph/Internal/Graph.chs b/src/IGraph/Internal/Graph.chs index 28dc52a..104cb34 100644 --- a/src/IGraph/Internal/Graph.chs +++ b/src/IGraph/Internal/Graph.chs @@ -10,7 +10,7 @@ import System.IO.Unsafe (unsafePerformIO) {#import IGraph.Internal.Data #} {#import IGraph.Internal.Constants #} -#include "haskelligraph.h" +#include "haskell_igraph.h" {#pointer *igraph_t as IGraphPtr foreign finalizer igraph_destroy newtype#} diff --git a/src/IGraph/Internal/Initialization.chs b/src/IGraph/Internal/Initialization.chs index 1c8e989..5e8d5ed 100644 --- a/src/IGraph/Internal/Initialization.chs +++ b/src/IGraph/Internal/Initialization.chs @@ -1,7 +1,7 @@ {-# LANGUAGE ForeignFunctionInterface #-} module IGraph.Internal.Initialization where -#include "haskelligraph.h" +#include "haskell_igraph.h" data HasInit diff --git a/src/IGraph/Mutable.hs b/src/IGraph/Mutable.hs index 9dac6b3..bc39798 100644 --- a/src/IGraph/Mutable.hs +++ b/src/IGraph/Mutable.hs @@ -4,6 +4,7 @@ module IGraph.Mutable where import Control.Monad (when) import Control.Monad.Primitive import qualified Data.ByteString.Char8 as B +import Data.Serialize (Serialize) import Foreign import IGraph.Internal.Attribute @@ -30,16 +31,14 @@ class MGraph d where addNodes :: PrimMonad m => Int -> MLGraph(PrimState m) d v e -> m () addNodes n (MLGraph g) = unsafePrimToPrim $ igraphAddVertices g n nullPtr - addLNodes :: (Show v, PrimMonad m) - => Int -- ^ the number of new vertices add to the graph - -> [v] -- ^ vertices' labels - -> MLGraph (PrimState m) d v e -> m () + addLNodes :: (Serialize v, PrimMonad m) + => Int -- ^ the number of new vertices add to the graph + -> [v] -- ^ vertices' labels + -> MLGraph (PrimState m) d v e -> m () addLNodes n labels (MLGraph g) | n /= length labels = error "addLVertices: incorrect number of labels" | otherwise = unsafePrimToPrim $ do - let attr = makeAttributeRecord vertexAttr labels - alloca $ \ptr -> do - poke ptr attr + with (makeAttributeRecord vertexAttr labels) $ \ptr -> do vptr <- listToVectorP [castPtr ptr] withVectorPPtr vptr $ \p -> igraphAddVertices g n $ castPtr p @@ -52,7 +51,7 @@ class MGraph d where addEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m () - addLEdges :: (PrimMonad m, Show e) => [LEdge e] -> MLGraph (PrimState m) d v e -> m () + addLEdges :: (PrimMonad m, Serialize e) => [LEdge e] -> MLGraph (PrimState m) d v e -> m () delEdges :: PrimMonad m => [(Int, Int)] -> MLGraph (PrimState m) d v e -> m () @@ -113,20 +112,24 @@ instance MGraph D where where eids = flip map es $ \(fr, to) -> igraphGetEid g fr to True True -setNodeAttr :: (PrimMonad m, Show v) +setNodeAttr :: (PrimMonad m, Serialize v) => Int -- ^ Node id -> v -> MLGraph (PrimState m) d v e -> m () setNodeAttr nodeId x (MLGraph gr) = unsafePrimToPrim $ do - err <- igraphCattributeVASSet gr vertexAttr nodeId $ show x - when (err /= 0) $ error "Fail to set node attribute!" + v <- unsafeToBS x + with v $ \vptr -> do + err <- igraphHaskellAttributeVASSet gr vertexAttr nodeId vptr + when (err /= 0) $ error "Fail to set node attribute!" -setEdgeAttr :: (PrimMonad m, Show v) +setEdgeAttr :: (PrimMonad m, Serialize v) => Int -- ^ Edge id -> v -> MLGraph (PrimState m) d v e -> m () setEdgeAttr edgeId x (MLGraph gr) = unsafePrimToPrim $ do - err <- igraphCattributeEASSet gr edgeAttr edgeId $ show x - when (err /= 0) $ error "Fail to set edge attribute!" + v <- unsafeToBS x + with v $ \vptr -> do + err <- igraphHaskellAttributeEASSet gr edgeAttr edgeId vptr + when (err /= 0) $ error "Fail to set edge attribute!" diff --git a/src/IGraph/Structure.hs b/src/IGraph/Structure.hs index cd298e1..be8d1c3 100644 --- a/src/IGraph/Structure.hs +++ b/src/IGraph/Structure.hs @@ -7,24 +7,25 @@ module IGraph.Structure , personalizedPagerank ) where -import Control.Monad -import Foreign -import Foreign.C.Types -import System.IO.Unsafe (unsafePerformIO) -import qualified Data.HashMap.Strict as M -import Data.Hashable (Hashable) +import Control.Monad +import Data.Hashable (Hashable) +import qualified Data.HashMap.Strict as M +import Data.Serialize (Serialize) +import Foreign +import Foreign.C.Types +import System.IO.Unsafe (unsafePerformIO) -import IGraph -import IGraph.Mutable -import IGraph.Internal.Graph -import IGraph.Internal.Data -import IGraph.Internal.Selector -import IGraph.Internal.Structure -import IGraph.Internal.Arpack -import IGraph.Internal.Constants -import IGraph.Internal.Attribute +import IGraph +import IGraph.Internal.Arpack +import IGraph.Internal.Attribute +import IGraph.Internal.Constants +import IGraph.Internal.Data +import IGraph.Internal.Graph +import IGraph.Internal.Selector +import IGraph.Internal.Structure +import IGraph.Mutable -inducedSubgraph :: (Hashable v, Eq v, Read v) => LGraph d v e -> [Int] -> LGraph d v e +inducedSubgraph :: (Hashable v, Eq v, Serialize v) => LGraph d v e -> [Int] -> LGraph d v e inducedSubgraph gr vs = unsafePerformIO $ do vs' <- listToVector $ map fromIntegral vs vsptr <- igraphVsVector vs' @@ -33,7 +34,8 @@ inducedSubgraph gr vs = unsafePerformIO $ do let g' = IGraphPtr gptr labToId = M.fromListWith (++) $ zip labels $ map return [0..nV-1] nV = igraphVcount g' - labels = map (read . igraphCattributeVAS g' vertexAttr) [0 .. nV-1] + labels = unsafePerformIO $ forM [0 .. nV - 1] $ \i -> + igraphHaskellAttributeVAS g' vertexAttr i >>= fromBS return $ LGraph g' labToId -- | closeness centrality @@ -49,7 +51,7 @@ closeness vs gr ws mode normal = unsafePerformIO $ do vptr <- igraphVectorNew 0 ws' <- case ws of Just w -> listToVector w - _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr + _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr igraphCloseness (_graph gr) vptr vsptr mode ws' normal vectorPtrToList vptr @@ -64,7 +66,7 @@ betweenness vs gr ws = unsafePerformIO $ do vptr <- igraphVectorNew 0 ws' <- case ws of Just w -> listToVector w - _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr + _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr igraphBetweenness (_graph gr) vptr vsptr True ws' False vectorPtrToList vptr @@ -76,7 +78,7 @@ eigenvectorCentrality gr ws = unsafePerformIO $ do vptr <- igraphVectorNew 0 ws' <- case ws of Just w -> listToVector w - _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr + _ -> liftM VectorPtr $ newForeignPtr_ $ castPtr nullPtr arparck <- igraphArpackNew igraphEigenvectorCentrality (_graph gr) vptr nullPtr True True ws' arparck vectorPtrToList vptr diff --git a/stack.yaml b/stack.yaml index d194a5d..e0e5b65 100644 --- a/stack.yaml +++ b/stack.yaml @@ -7,4 +7,4 @@ packages: extra-deps: [] -resolver: lts-8.17 +resolver: lts-10.10 diff --git a/tests/Test/Attributes.hs b/tests/Test/Attributes.hs new file mode 100644 index 0000000..f9f40c3 --- /dev/null +++ b/tests/Test/Attributes.hs @@ -0,0 +1,47 @@ +module Test.Attributes + ( tests + ) where + +import Control.Monad +import Control.Monad.ST +import Data.List +import Data.List.Ordered (nubSort) +import Data.Maybe +import Foreign +import System.IO.Unsafe +import Test.Tasty +import Test.Tasty.HUnit +import Test.Utils + +import IGraph +import IGraph.Internal.Attribute +import IGraph.Mutable +import IGraph.Structure + +tests :: TestTree +tests = testGroup "Attribute tests" + [ bsTest + , nodeLabelTest + , labelTest + ] + +bsTest :: TestTree +bsTest = testCase "BS" $ do + let values = [1..10000] :: [Int] + bs <- mapM unsafeToBS values + values' <- forM bs $ \b -> with b $ \ptr -> fromBS ptr + assertBool "" $ values == values' + +nodeLabelTest :: TestTree +nodeLabelTest = testCase "node label test" $ do + let ns = sort $ map show [38..7000] + gr = mkGraph ns [] :: LGraph D String () + assertBool "" $ sort (map (nodeLab gr) $ nodes gr) == ns + +labelTest :: TestTree +labelTest = testCase "edge label test" $ do + dat <- randEdges 1000 10000 + let es = sort $ zipWith (\a b -> (a,b)) dat $ map show [1..] + gr = fromLabeledEdges es :: LGraph D Int String + es' = sort $ map (\(a,b) -> ((nodeLab gr a, nodeLab gr b), edgeLab gr (a,b))) $ edges gr + assertBool "" $ es == es' diff --git a/tests/Test/Basic.hs b/tests/Test/Basic.hs index 092c393..8bb82d7 100644 --- a/tests/Test/Basic.hs +++ b/tests/Test/Basic.hs @@ -42,12 +42,12 @@ graphCreationLabeled = testGroup "Graph creation -- with labels" (nodeLab gr fr, nodeLab gr to)) $ edges gr) ] where - edgeList = sort $ map (\(a,b) -> (show a, show b)) $ unsafePerformIO $ randEdges 10000 1000 + edgeList = sort $ map (\(a,b) -> (show a, show b)) $ unsafePerformIO $ + randEdges 10000 1000 n = length $ nubSort $ concatMap (\(a,b) -> [a,b]) edgeList m = length edgeList gr = fromLabeledEdges $ zip edgeList $ repeat () :: LGraph D String () - graphEdit :: TestTree graphEdit = testGroup "Graph editing" [ testCase "" $ [(1,2)] @=? (sort $ edges simple') ] diff --git a/tests/test.hs b/tests/test.hs index 1e2ce0c..c681d04 100644 --- a/tests/test.hs +++ b/tests/test.hs @@ -2,6 +2,7 @@ import qualified Test.Basic as Basic import qualified Test.Isomorphism as Isomorphism import qualified Test.Motif as Motif import qualified Test.Structure as Structure +import qualified Test.Attributes as Attributes import Test.Tasty main :: IO () @@ -10,4 +11,5 @@ main = defaultMain $ testGroup "Haskell-igraph Tests" , Structure.tests , Motif.tests , Isomorphism.tests + , Attributes.tests ] -- 2.21.0