igraph/ext/cIGraph_file.c

852 lines
22 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "igraph.h"
#include "ruby.h"
#include "cIGraph.h"
#ifdef __APPLE__
#else
/* call-seq:
* IGraph::FileRead.read_graph_edgelist(file,mode) -> IGraph
*
* Reads an edge list from a File (or any IO) and creates a graph.
*
* This format is simply a series of even number integers separated by
* whitespace. The one edge (ie. two integers) per line format is thus not
* required (but recommended for readability). Edges of directed graphs are
* assumed to be in from, to order.
*/
VALUE cIGraph_read_graph_edgelist(VALUE self, VALUE file, VALUE directed){
VALUE string;
FILE *stream;
VALUE new_graph;
VALUE v_ary;
igraph_t *graph;
igraph_bool_t directed_b = 0;
igraph_vs_t vs;
igraph_vit_t vit;
int vid;
if(directed)
directed_b = 1;
new_graph = cIGraph_alloc(cIGraph);
Data_Get_Struct(new_graph, igraph_t, graph);
string = rb_funcall(file, rb_intern("read"), 0);
stream = fmemopen(RSTRING_PTR(string),RSTRING_LEN(string), "r");
igraph_read_graph_edgelist(graph, stream, 0, directed_b);
fclose(stream);
igraph_vs_all(&vs);
igraph_vit_create(graph, vs, &vit);
v_ary = ((VALUE*)graph->attr)[0];
while (!IGRAPH_VIT_END(vit)) {
vid = IGRAPH_VIT_GET(vit);
rb_ary_push(v_ary,INT2NUM(vid));
IGRAPH_VIT_NEXT(vit);
}
igraph_vit_destroy(&vit);
igraph_vs_destroy(&vs);
return new_graph;
}
/* call-seq:
* graph.write_graph_edgelist(file) -> Integer
*
* Writes an edge list to an IO
*
* This format is simply a series of even number integers separated by
* whitespace. The one edge (ie. two integers) per line format is thus not
* required (but recommended for readability). Edges of directed graphs are
* assumed to be in from, to order.
*/
VALUE cIGraph_write_graph_edgelist(VALUE self, VALUE file){
char *buf;
size_t size;
FILE *stream;
igraph_t *graph;
int e;
Data_Get_Struct(self, igraph_t, graph);
stream = open_memstream(&buf,&size);
e = igraph_write_graph_edgelist(graph, stream);
fflush(stream);
rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size));
fclose(stream);
return e;
}
/* call-seq:
* IGraph::FileRead.read_graph_ncol(file,predefnames,names,weights,directed) -> IGraph
*
* Reads a .ncol file used by LGL, also useful for creating graphs from
* 'named' (and optionally weighted) edge lists.
*
* This format is used by the Large Graph Layout program
* (http://bioinformatics.icmb.utexas.edu/lgl/), and it is simply a symbolic
* weighted edge list. It is a simple text file with one edge per line. An
* edge is defined by two symbolic vertex names separated by whitespace.
* (The symbolic vertex names themselves cannot contain whitespace. They
* might follow by an optional number, this will be the weight of the edge;
* the number can be negative and can be in scientific notation. If there is
* no weight specified to an edge it is assumed to be zero.
*
* The resulting graph is always undirected. LGL cannot deal with files which
* contain multiple or loop edges, this is however not checked here, as
* igraph is happy with these.
*
* file: A File or IO object to read from.
*
* predefnames: Array of the symbolic names of the vertices in the file.
* If empty then vertex ids will be assigned to vertex names in the order of
* their appearence in the .ncol file.
*
* names: Logical value, if TRUE the symbolic names of the vertices will be
* added to the graph as a vertex attribute called 'name'.
*
* weights: Logical value, if TRUE the weights of the edges is added to the
* graph as an edge attribute called 'weight'.
*
* directed: Whether to create a directed graph. As this format was originally
* used only for undirected graphs there is no information in the file about
* the directedness of the graph. Set this parameter to IGRAPH_DIRECTED or
* IGRAPH_UNDIRECTED to create a directed or undirected graph.
*/
VALUE cIGraph_read_graph_ncol(VALUE self, VALUE file, VALUE predefnames, VALUE names, VALUE weights, VALUE directed){
VALUE string;
FILE *stream;
VALUE new_graph;
VALUE v_ary;
VALUE e_ary;
VALUE new_ary;
igraph_t *graph;
igraph_strvector_t names_vec;
igraph_bool_t directed_b = 0;
igraph_bool_t weights_b = 0;
igraph_bool_t names_b = 0;
int i;
if(directed)
directed_b = 1;
if(names)
names_b = 1;
if(weights)
weights_b = 1;
new_graph = cIGraph_alloc(cIGraph);
Data_Get_Struct(new_graph, igraph_t, graph);
igraph_strvector_init(&names_vec,RARRAY_LEN(predefnames));
for(i=0;i<RARRAY_LEN(predefnames);i++){
igraph_strvector_set(&names_vec, i, RSTRING_PTR(RARRAY_PTR(predefnames)[i]));
}
string = rb_funcall(file, rb_intern("read"), 0);
stream = fmemopen(RSTRING_PTR(string),RSTRING_LEN(string), "r");
if (RARRAY_LEN(predefnames) == 0){
igraph_read_graph_ncol(graph, stream, NULL, names_b, weights_b, directed_b);
} else {
igraph_read_graph_ncol(graph, stream, &names_vec, names_b, weights_b, directed_b);
}
fclose(stream);
//Convert the Hash of names to Strings instead
if(names){
v_ary = ((VALUE*)graph->attr)[0];
new_ary = rb_ary_new();
for(i=0;i<RARRAY_LEN(v_ary);i++){
rb_ary_push(new_ary, rb_hash_aref(RARRAY_PTR(v_ary)[i], rb_str_new2("name")));
}
((VALUE*)graph->attr)[0] = new_ary;
}
//Convert the Hash of weights to floats instead
if(weights){
e_ary = ((VALUE*)graph->attr)[1];
new_ary = rb_ary_new();
for(i=0;i<RARRAY_LEN(e_ary);i++){
rb_ary_push(new_ary, rb_hash_aref(RARRAY_PTR(e_ary)[i], rb_str_new2("weight")));
}
((VALUE*)graph->attr)[1] = new_ary;
}
igraph_strvector_destroy(&names_vec);
return new_graph;
}
/* call-seq:
* graph.write_graph_ncol(file,names,weights) -> Integer
*
* Writes the graph to a file in .ncol format
*
* .ncol is a format used by LGL, see igraph_read_graph_ncol() for details.
*
* Note that having multiple or loop edges in an .ncol file breaks the LGL
* software but igraph does not check for this condition.
*
* file: The file object to write to, it should be writable.
*
* names: The name of the vertex attribute, if symbolic names are to be
* written to the file.
*
* weights: The name of the edge attribute, if they are also written to the
* file.
*/
VALUE cIGraph_write_graph_ncol(VALUE self, VALUE file, VALUE names, VALUE weights){
char *buf;
size_t size;
FILE *stream;
igraph_t *graph;
int e, i;
VALUE v_ary = Qnil;
VALUE e_ary = Qnil;
VALUE new_v_ary;
VALUE new_e_ary;
VALUE vertex_h;
VALUE edge_h;
char *weights_b = "0";
char *names_b = "0";
if(names)
names_b = "name";
if(weights)
weights_b = "weight";
Data_Get_Struct(self, igraph_t, graph);
//Convert each object to it's String representation and each edge to
//it's Float
if(names){
v_ary = ((VALUE*)graph->attr)[0];
new_v_ary = rb_ary_new();
for(i=0;i<RARRAY_LEN(v_ary);i++){
vertex_h = rb_hash_new();
rb_hash_aset(vertex_h, rb_str_new2("name"), StringValue(RARRAY_PTR(v_ary)[i]));
rb_ary_push(new_v_ary, vertex_h);
}
((VALUE*)graph->attr)[0] = new_v_ary;
}
if(weights){
e_ary = ((VALUE*)graph->attr)[1];
new_e_ary = rb_ary_new();
for(i=0;i<RARRAY_LEN(e_ary);i++){
edge_h = rb_hash_new();
rb_hash_aset(edge_h, rb_str_new2("weight"), rb_funcall(RARRAY_PTR(e_ary)[i],rb_intern("to_f"),0));
rb_ary_push(new_e_ary, edge_h);
}
((VALUE*)graph->attr)[1] = new_e_ary;
}
stream = open_memstream(&buf,&size);
e = igraph_write_graph_ncol(graph, stream, names_b, weights_b);
fflush(stream);
rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size));
fclose(stream);
//Replace initital vertices and edges
if(names){
((VALUE*)graph->attr)[0] = v_ary;
}
if(weights){
((VALUE*)graph->attr)[0] = e_ary;
}
return e;
}
/* call-seq:
* IGraph::FileRead.read_graph_lgl(file,predefnames,names,weights) -> IGraph
*
* Reads a graph from an .lgl file
*
* The .lgl format is used by the Large Graph Layout visualization software
* (http://bioinformatics.icmb.utexas.edu/lgl/), it can describe undirected
* optionally weighted graphs. From the LGL manual:
*
* The second format is the LGL file format ( .lgl file suffix). This is yet
* another graph file format that tries to be as stingy as possible with
* space, yet keeping the edge file in a human readable (not binary) format.
* The format itself is like the following:
*
* # vertex1name
* vertex2name [optionalWeight]
* vertex3name [optionalWeight]
*
* Here, the first vertex of an edge is preceded with a pound sign '#'.
* Then each vertex that shares an edge with that vertex is listed one per
* line on subsequent lines.
*
* LGL cannot handle loop and multiple edges or directed graphs, but in
* igraph it is not an error to have multiple and loop edges.
*
* file: A File or IO object to read from.
*
* names: Logical value, if TRUE the symbolic names of the vertices will be
* added to the graph as a vertex attribute called $B!H(Bname$B!I(B.
*
* weights: Logical value, if TRUE the weights of the edges is added to the
* graph as an edge attribute called $B!H(Bweight$B!I(B.
*/
VALUE cIGraph_read_graph_lgl(VALUE self, VALUE file, VALUE names, VALUE weights){
VALUE string;
FILE *stream;
VALUE new_graph;
VALUE v_ary;
VALUE e_ary;
VALUE new_ary;
igraph_t *graph;
igraph_bool_t weights_b = 0;
igraph_bool_t names_b = 0;
int i;
if(names)
names_b = 1;
if(weights)
weights_b = 1;
new_graph = cIGraph_alloc(cIGraph);
Data_Get_Struct(new_graph, igraph_t, graph);
string = rb_funcall(file, rb_intern("read"), 0);
stream = fmemopen(RSTRING_PTR(string),RSTRING_LEN(string), "r");
igraph_read_graph_lgl(graph, stream, names_b, weights_b);
fclose(stream);
//Convert the Hash of names to Strings instead
if(names){
v_ary = ((VALUE*)graph->attr)[0];
new_ary = rb_ary_new();
for(i=0;i<RARRAY_LEN(v_ary);i++){
rb_ary_push(new_ary, rb_hash_aref(RARRAY_PTR(v_ary)[i], rb_str_new2("name")));
}
((VALUE*)graph->attr)[0] = new_ary;
}
//Convert the Hash of weights to floats instead
if(weights){
e_ary = ((VALUE*)graph->attr)[1];
new_ary = rb_ary_new();
for(i=0;i<RARRAY_LEN(e_ary);i++){
rb_ary_push(new_ary, rb_hash_aref(RARRAY_PTR(e_ary)[i], rb_str_new2("weight")));
}
((VALUE*)graph->attr)[1] = new_ary;
}
return new_graph;
}
/* call-seq:
* graph.write_graph_lgl(file,names,weights,isolates) -> Integer
*
* Writes the graph to a file in .lgl format
*
* .lgl is a format used by LGL, see read_graph_lgl() for details.
*
* Note that having multiple or loop edges in an .lgl file breaks the LGL
* software but igraph does not check for this condition.
*
* file: The File object to write to, it should be writable.
*
* names: The name of the vertex attribute, if symbolic names are written to
* the file.
*
* weights: The name of the edge attribute, if they are also written to the
* file.
*
* isolates: Logical, if TRUE isolated vertices are also written to the file.
* If FALSE they will be omitted.
*/
VALUE cIGraph_write_graph_lgl(VALUE self, VALUE file, VALUE names, VALUE weights, VALUE isolates){
char *buf;
size_t size;
FILE *stream;
igraph_t *graph;
int e, i;
VALUE v_ary = Qnil;
VALUE e_ary = Qnil;
VALUE new_v_ary;
VALUE new_e_ary;
VALUE vertex_h;
VALUE edge_h;
char *weights_b = "0";
char *names_b = "0";
igraph_bool_t isolates_b;
if(names)
names_b = "name";
if(weights)
weights_b = "weight";
if(isolates)
isolates_b = 1;
Data_Get_Struct(self, igraph_t, graph);
//Convert each object to it's String representation and each edge to
//it's Float
if(names){
v_ary = ((VALUE*)graph->attr)[0];
new_v_ary = rb_ary_new();
for(i=0;i<RARRAY_LEN(v_ary);i++){
vertex_h = rb_hash_new();
rb_hash_aset(vertex_h, rb_str_new2("name"), StringValue(RARRAY_PTR(v_ary)[i]));
rb_ary_push(new_v_ary, vertex_h);
}
((VALUE*)graph->attr)[0] = new_v_ary;
}
if(weights){
e_ary = ((VALUE*)graph->attr)[1];
new_e_ary = rb_ary_new();
for(i=0;i<RARRAY_LEN(e_ary);i++){
edge_h = rb_hash_new();
rb_hash_aset(edge_h, rb_str_new2("weight"), rb_funcall(RARRAY_PTR(e_ary)[i],rb_intern("to_f"),0));
rb_ary_push(new_e_ary, edge_h);
}
((VALUE*)graph->attr)[1] = new_e_ary;
}
stream = open_memstream(&buf,&size);
e = igraph_write_graph_lgl(graph, stream, names_b, weights_b, isolates);
fflush(stream);
rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size));
fclose(stream);
//Replace initital vertices and edges
if(names){
((VALUE*)graph->attr)[0] = v_ary;
}
if(weights){
((VALUE*)graph->attr)[0] = e_ary;
}
return e;
}
/* call-seq:
* IGraph::FileRead.read_graph_dimacs(file,directed) -> IGraph
*
* This function reads the DIMACS file format, more specifically the version
* for network flow problems, see the files at
* ftp://dimacs.rutgers.edu/pub/netflow/general-info/
*
* This is a line-oriented text file (ASCII) format. The first character of
* each line defines the type of the line. If the first character is c the
* line is a comment line and it is ignored. There is one problem line ( p in
* the file, it must appear before any node and arc descriptor lines. The
* problem line has three fields separated by spaces: the problem type
* ( min , max or asn ), the number of vertices and number of edges in the
* graph. Exactly two node identification lines are expected ( n ), one for
* the source, one for the target vertex. These have two fields: the id of
* the vertex and the type of the vertex, either s (=source) or t (=target).
* Arc lines start with a and have three fields: the source vertex, the
* target vertex and the edge capacity.
*
* Vertex ids are numbered from 1. The source, target vertices and edge
* capacities are added as attributes of the graph.
* I.e: g.attributes['source'].
*
* file: The File to read from.
*
* directed: Boolean, whether to create a directed graph.
*/
VALUE cIGraph_read_graph_dimacs(VALUE self, VALUE file, VALUE directed){
VALUE string;
FILE *stream;
VALUE new_graph;
igraph_integer_t source;
igraph_integer_t target;
igraph_vector_t capacity;
igraph_vector_t label;
igraph_strvector_t problem;
igraph_t *graph;
igraph_bool_t directed_b = 0;
igraph_vs_t vs;
igraph_vit_t vit;
VALUE v_ary;
VALUE g_hsh;
int i;
int vid;
if(directed)
directed_b = 1;
igraph_vector_init(&capacity, 0);
igraph_vector_init(&label, 0);
igraph_strvector_init(&problem, 0);
new_graph = cIGraph_alloc(cIGraph);
Data_Get_Struct(new_graph, igraph_t, graph);
string = rb_funcall(file, rb_intern("read"), 0);
stream = fmemopen(RSTRING_PTR(string),RSTRING_LEN(string), "r");
igraph_read_graph_dimacs(graph, stream, &problem, &label, &source, &target, &capacity, directed_b);
fclose(stream);
igraph_vs_all(&vs);
igraph_vit_create(graph, vs, &vit);
//Push Integer onto vertex array
v_ary = ((VALUE*)graph->attr)[0];
while (!IGRAPH_VIT_END(vit)) {
vid = IGRAPH_VIT_GET(vit);
rb_ary_push(v_ary,INT2NUM(vid));
IGRAPH_VIT_NEXT(vit);
}
g_hsh = ((VALUE*)graph->attr)[2];
rb_hash_aset(g_hsh, rb_str_new2("source"), INT2NUM(source));
rb_hash_aset(g_hsh, rb_str_new2("target"), INT2NUM(target));
rb_hash_aset(g_hsh, rb_str_new2("capacity"), rb_ary_new());
for(i=0;i<igraph_vector_size(&capacity);i++){
rb_ary_push(rb_hash_aref(g_hsh, rb_str_new2("capacity")), rb_float_new(VECTOR(capacity)[i]));
}
igraph_vit_destroy(&vit);
igraph_vs_destroy(&vs);
return new_graph;
}
/* call-seq:
* graph.write_graph_dimacs(file,source,target,capacity) -> Integer
*
* This function writes a graph to an output stream in DIMACS format,
* describing a maximum flow problem. See
* ftp://dimacs.rutgers.edu/pub/netflow/general-info/
*
* This file format is discussed in the documentation of read_graph_dimacs(),
* see that for more information.
*
* file: IO object to write to.
*
* source: The source vertex for the maximum flow.
*
* target: The target vertex.
*
* capacity: Array containing the edge capacity values.
*/
VALUE cIGraph_write_graph_dimacs(VALUE self, VALUE file, VALUE source, VALUE target, VALUE capacity){
char *buf;
size_t size;
FILE *stream;
igraph_t *graph;
int e, i;
igraph_vector_t capacity_v;
Data_Get_Struct(self, igraph_t, graph);
igraph_vector_init(&capacity_v,0);
for(i=0;i<RARRAY_LEN(capacity);i++){
igraph_vector_push_back(&capacity_v,NUM2DBL(RARRAY_PTR(capacity)[i]));
}
stream = open_memstream(&buf,&size);
e = igraph_write_graph_dimacs(graph, stream, NUM2INT(source), NUM2INT(target), &capacity_v);
fflush(stream);
rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size));
fclose(stream);
return e;
}
/* call-seq:
* IGraph::FileRead.read_graph_graphdb(file,directed) -> IGraph
*
* Read a graph in the binary graph database format.
*
* file: The IO object to read from.
*
* directed: Boolean, whether to create a directed graph.
*/
VALUE cIGraph_read_graph_graphdb(VALUE self, VALUE file, VALUE directed){
VALUE string;
FILE *stream;
VALUE new_graph;
VALUE v_ary;
igraph_t *graph;
igraph_bool_t directed_b = 0;
igraph_vs_t vs;
igraph_vit_t vit;
int vid;
if(directed)
directed_b = 1;
new_graph = cIGraph_alloc(cIGraph);
Data_Get_Struct(new_graph, igraph_t, graph);
string = rb_funcall(file, rb_intern("read"), 0);
stream = fmemopen(RSTRING_PTR(string),RSTRING_LEN(string), "r");
igraph_read_graph_graphdb(graph, stream, directed_b);
fclose(stream);
igraph_vs_all(&vs);
igraph_vit_create(graph, vs, &vit);
//Push Integer onto vertex array
v_ary = ((VALUE*)graph->attr)[0];
while (!IGRAPH_VIT_END(vit)) {
vid = IGRAPH_VIT_GET(vit);
rb_ary_push(v_ary,INT2NUM(vid));
IGRAPH_VIT_NEXT(vit);
}
igraph_vit_destroy(&vit);
igraph_vs_destroy(&vs);
return new_graph;
}
/* call-seq:
* IGraph::FileRead.read_graph_graphml(file,index) -> IGraph
*
* Reads a graph from a GraphML file specified as the File object file.
*
* GraphML is an XML-based file format for representing various types of
* graphs. Currently only the most basic import functionality is implemented
* in igraph: it can read GraphML files without nested graphs and hyperedges.
*
* If the GraphML file contains more than one graph, the one specified by
* this index will be loaded. Indices start from zero, so supply zero here
* if your GraphML file contains only a single graph.
*/
VALUE cIGraph_read_graph_graphml(VALUE self, VALUE file, VALUE index){
VALUE string;
FILE *stream;
VALUE new_graph;
igraph_t *graph;
new_graph = cIGraph_alloc(cIGraph);
Data_Get_Struct(new_graph, igraph_t, graph);
string = rb_funcall(file, rb_intern("read"), 0);
stream = fmemopen(RSTRING_PTR(string),RSTRING_LEN(string), "r");
igraph_read_graph_graphml(graph, stream, NUM2INT(index));
fclose(stream);
return new_graph;
}
/* call-seq:
* graph.write_graph_graphml(file) -> Integer
*
* Writes the graph to a File in GraphML format
*
* GraphML is an XML-based file format for representing various types of
* graphs. See the GraphML Primer
* (http://graphml.graphdrawing.org/primer/graphml-primer.html) for detailed
* format description.
*/
VALUE cIGraph_write_graph_graphml(VALUE self, VALUE file){
char *buf;
size_t size;
FILE *stream;
igraph_t *graph;
int e;
Data_Get_Struct(self, igraph_t, graph);
stream = open_memstream(&buf,&size);
e = igraph_write_graph_graphml(graph, stream);
fflush(stream);
rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size));
fclose(stream);
return e;
}
/* call-seq:
* IGraph::FileRead.read_graph_gml(file) -> IGraph
*
* Reads a graph from a GraphML file.
*
* GraphML is an XML-based file format for representing various types of
* graphs. Currently only the most basic import functionality is implemented
* in igraph: it can read GraphML files without nested graphs and hyperedges.
*
* file: IO object to read from
*/
VALUE cIGraph_read_graph_gml(VALUE self, VALUE file){
VALUE string;
FILE *stream;
VALUE new_graph;
igraph_t *graph;
new_graph = cIGraph_alloc(cIGraph);
Data_Get_Struct(new_graph, igraph_t, graph);
string = rb_funcall(file, rb_intern("read"), 0);
stream = fmemopen(RSTRING_PTR(string),RSTRING_LEN(string), "r");
igraph_read_graph_gml(graph, stream);
fclose(stream);
return new_graph;
}
/* call-seq:
* graph.write_graph_gml(file) -> IGraph
*
* Writes the graph to a file in GraphML format.
*
* file: IO object to write to
*/
VALUE cIGraph_write_graph_gml(VALUE self, VALUE file){
char *buf;
size_t size;
FILE *stream;
igraph_t *graph;
int e;
Data_Get_Struct(self, igraph_t, graph);
stream = open_memstream(&buf,&size);
e = igraph_write_graph_gml(graph, stream, NULL, 0);
fflush(stream);
rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size));
fclose(stream);
return e;
}
/* call-seq:
* IGraph::FileRead.read_graph_pajek(file) -> IGraph
*
* Reads a file in Pajek format
*
* Only a subset of the Pajek format is implemented. This is partially
* because this format is not very well documented, but also because
* igraph does not support some Pajek features, like multigraphs.
*/
VALUE cIGraph_read_graph_pajek(VALUE self, VALUE file){
VALUE string;
FILE *stream;
VALUE new_graph;
igraph_t *graph;
new_graph = cIGraph_alloc(cIGraph);
Data_Get_Struct(new_graph, igraph_t, graph);
string = rb_funcall(file, rb_intern("read"), 0);
stream = fmemopen(RSTRING_PTR(string),RSTRING_LEN(string), "r");
IGRAPH_CHECK(igraph_read_graph_pajek(graph, stream));
fclose(stream);
return new_graph;
}
/* call-seq:
* graph.write_graph_pajek(file) -> Integer
*
* Writes a graph to a file in Pajek format.
*/
VALUE cIGraph_write_graph_pajek(VALUE self, VALUE file){
char *buf;
size_t size;
FILE *stream;
igraph_t *graph;
int e;
Data_Get_Struct(self, igraph_t, graph);
stream = open_memstream(&buf,&size);
e = igraph_write_graph_pajek(graph, stream);
fflush(stream);
rb_funcall(file, rb_intern("write"), 1, rb_str_new(buf,size));
fclose(stream);
return e;
}
#endif