src/lemon/graph_reader.h
author athos
Thu, 07 Apr 2005 10:44:32 +0000
changeset 1316 daaf6b5c28d6
parent 1287 984723507b86
child 1333 2640cf6547ff
permissions -rw-r--r--
Makefile has changed.
     1 /* -*- C++ -*-
     2  * src/lemon/graph_reader.h - Part of LEMON, a generic C++ optimization library
     3  *
     4  * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     5  * (Egervary Combinatorial Optimization Research Group, EGRES).
     6  *
     7  * Permission to use, modify and distribute this software is granted
     8  * provided that this copyright notice appears in all copies. For
     9  * precise terms see the accompanying LICENSE file.
    10  *
    11  * This software is provided "AS IS" with no warranty of any kind,
    12  * express or implied, and with no claim as to its suitability for any
    13  * purpose.
    14  *
    15  */
    16 
    17 ///\ingroup io_group
    18 ///\file
    19 ///\brief Lemon Graph Format reader.
    20 
    21 #ifndef LEMON_GRAPH_READER_H
    22 #define LEMON_GRAPH_READER_H
    23 
    24 #include <iostream>
    25 #include <sstream>
    26 
    27 #include <map>
    28 #include <vector>
    29 
    30 #include <memory>
    31 
    32 #include <lemon/error.h>
    33 
    34 
    35 namespace lemon {
    36 
    37 
    38   /// \brief Standard ReaderTraits for the GraphReader class.
    39   ///
    40   /// Standard ReaderTraits for the GraphReader class.
    41   /// It defines standard reading method for all type of value. 
    42   struct DefaultReaderTraits {
    43 
    44    /// \brief Template class for reading an value.
    45     ///
    46     /// Template class for reading an value.
    47     template <typename _Value>
    48     struct Reader {
    49       /// The value type.
    50       typedef _Value Value;
    51       /// \brief Reads a value from the given stream.
    52       ///
    53       /// Reads a value from the given stream.
    54       void read(std::istream& is, Value& value) {
    55 	if (!(is >> value)) 
    56 	  throw DataFormatError("Default reader format exception");
    57       }
    58     };
    59 
    60     /// The reader class for the not needed maps.
    61     typedef Reader<std::string> DefaultReader;
    62 
    63   };
    64 
    65   /// \brief Reader class for quoted strings.
    66   ///
    67   /// Reader class for quoted strings. It can process the escape
    68   /// sequences in the string.
    69   class QuotedStringReader {
    70   public:
    71     typedef std::string Value;
    72     
    73     /// \brief Constructor for the reader.
    74     ///
    75     /// Constructor for the reader. If the given parameter is true
    76     /// the reader processes the escape sequences.
    77     QuotedStringReader(bool _escaped = true) : escaped(_escaped) {}
    78     
    79     /// \brief Reads a quoted string from the given stream.
    80     ///
    81     /// Reads a quoted string from the given stream.
    82     void read(std::istream& is, std::string& value) {
    83       char c;
    84       value.clear();
    85       is >> std::ws;
    86       if (!is.get(c) || c != '\"') 
    87 	throw DataFormatError("Quoted string format error");
    88       while (is.get(c) && c != '\"') {
    89 	if (escaped && c == '\\') {
    90 	  value += readEscape(is);
    91 	} else {
    92 	  value += c;
    93 	}
    94       }
    95       if (!is) throw DataFormatError("Quoted string format error");
    96     }
    97 
    98   private:
    99     
   100     static char readEscape(std::istream& is) {
   101       char c;
   102       switch (is.get(c), c) {
   103       case '\\':
   104 	return '\\';
   105       case '\"':
   106 	return '\"';
   107       case '\'':
   108 	return '\'';
   109       case '\?':
   110 	return '\?';
   111       case 'a':
   112 	return '\a';
   113       case 'b':
   114 	return '\b';
   115       case 'f':
   116 	return '\f';
   117       case 'n':
   118 	return '\n';
   119       case 'r':
   120 	return '\r';
   121       case 't':
   122 	return '\t';
   123       case 'v':
   124 	return '\v';
   125       case 'x':
   126 	{
   127 	  int code;
   128 	  if (!is.get(c) || !isHex(c)) 
   129 	    throw DataFormatError("Escape format error");
   130 	  else if (code = valueHex(c), !is.get(c) || !isHex(c)) is.putback(c);
   131 	  else code = code * 16 + valueHex(c);
   132 	  return code;
   133 	}
   134       default:
   135 	{
   136 	  int code;
   137 	  if (!isOct(c)) 
   138 	    throw DataFormatError("Escape format error");
   139 	  else if (code = valueOct(c), !is.get(c) || !isOct(c)) 
   140 	    is.putback(c);
   141 	  else if (code = code * 8 + valueOct(c), !is.get(c) || !isOct(c)) 
   142 	    is.putback(c);
   143 	  else code = code * 8 + valueOct(c);
   144 	  return code;
   145 	}	      
   146       } 
   147     }
   148 
   149     static bool isOct(char c) {
   150       return '0' <= c && c <='7'; 
   151     }
   152     
   153     static int valueOct(char c) {
   154       return c - '0';
   155     }
   156 
   157    static bool isHex(char c) {
   158       return ('0' <= c && c <= '9') || 
   159 	('a' <= c && c <= 'z') || 
   160 	('A' <= c && c <= 'Z'); 
   161     }
   162     
   163     static int valueHex(char c) {
   164       if ('0' <= c && c <= '9') return c - '0';
   165       if ('a' <= c && c <= 'z') return c - 'a' + 10;
   166       return c - 'A' + 10;
   167     }
   168 
   169     bool escaped;
   170   };
   171 
   172   /// \brief The graph reader class.
   173   ///
   174   /// \ingroup io_group
   175   /// The reader class for the graph input.
   176   /// \see DefaultReaderTraits
   177   /// \see QuotedStringReader
   178   /// \see \ref GraphWriter
   179   /// \see \ref graph-io-page
   180   template <typename _Graph, typename _ReaderTraits = DefaultReaderTraits> 
   181   class GraphReader {
   182   public:
   183     
   184     typedef _Graph Graph;
   185     typedef typename Graph::Node Node;
   186     typedef typename Graph::Edge Edge;
   187 
   188     typedef _ReaderTraits ReaderTraits;
   189     typedef typename ReaderTraits::DefaultReader DefaultReader;
   190 
   191     /// \brief Construct a new GraphReader.
   192     ///
   193     /// Construct a new GraphReader. It reads into the given graph
   194     /// and it use the given reader as the default skipper.
   195     GraphReader(std::istream& _is, Graph& _graph, 
   196 		const DefaultReader& _reader = DefaultReader()) 
   197       : is(_is), graph(_graph), nodeSkipper(_reader), edgeSkipper(_reader) {}
   198 
   199     /// \brief Destruct the graph reader.
   200     ///
   201     /// Destruct the graph reader.
   202     ~GraphReader() {
   203 
   204       for (typename NodeMapReaders::iterator it = node_map_readers.begin(); 
   205 	   it != node_map_readers.end(); ++it) {
   206 	delete it->second;
   207       }
   208 
   209       for (typename EdgeMapReaders::iterator it = edge_map_readers.begin(); 
   210 	   it != edge_map_readers.end(); ++it) {
   211 	delete it->second;
   212       }
   213 
   214     }
   215 
   216     /// \brief Add a new node map reader command for the reader.
   217     ///
   218     /// Add a new node map reader command for the reader.
   219     template <typename Map>
   220     GraphReader& addNodeMap(std::string name, Map& map) {
   221       return addNodeMap<typename ReaderTraits::template 
   222 	Reader<typename Map::Value>, Map>(name, map);
   223     }
   224 
   225     /// \brief Add a new node map reader command for the reader.
   226     ///
   227     /// Add a new node map reader command for the reader.
   228     template <typename Reader, typename Map>
   229     GraphReader& addNodeMap(std::string name, Map& map, 
   230 			     const Reader& reader = Reader()) {
   231       if (node_map_readers.find(name) != node_map_readers.end()) {
   232 	ErrorMessage msg;
   233 	msg << "Multiple read rule for node map: " << name;
   234 	throw IOParameterError(msg.message());
   235       }
   236       node_map_readers.insert(
   237         make_pair(name, new MapReader<Node, Map, Reader>(map, reader)));
   238       return *this;
   239     }
   240 
   241     /// \brief Add a new node map skipper command for the reader.
   242     ///
   243     /// Add a new node map skipper command for the reader.
   244     template <typename Reader>
   245     GraphReader& skipNodeMap(std::string name, 
   246 			     const Reader& reader = Reader()) {
   247       if (node_map_readers.find(name) != node_map_readers.end()) {
   248 	ErrorMessage msg;
   249 	msg << "Multiple read rule for node map: " << name;
   250 	throw IOParameterError(msg.message());
   251       }
   252       node_map_readers.insert(
   253         make_pair(name, new SkipReader<Node, Reader>(reader)));
   254       return *this;
   255     }
   256 
   257     /// \brief Add a new edge map reader command for the reader.
   258     ///
   259     /// Add a new edge map reader command for the reader.
   260     template <typename Map>
   261     GraphReader& addEdgeMap(std::string name, Map& map) { 
   262       return addEdgeMap<typename ReaderTraits::template
   263 	Reader<typename Map::Value>, Map>(name, map);
   264     }
   265 
   266 
   267     /// \brief Add a new edge map reader command for the reader.
   268     ///
   269     /// Add a new edge map reader command for the reader.
   270     template <typename Reader, typename Map>
   271     GraphReader& addEdgeMap(std::string name, Map& map,
   272 			     const Reader& reader = Reader()) {
   273       if (edge_map_readers.find(name) != edge_map_readers.end()) {
   274 	ErrorMessage msg;
   275 	msg << "Multiple read rule for edge map: " << name;
   276 	throw IOParameterError(msg.message());
   277       }
   278       edge_map_readers.insert(
   279         make_pair(name, new MapReader<Edge, Map, Reader>(map, reader)));
   280       return *this;
   281     }
   282 
   283     /// \brief Add a new edge map skipper command for the reader.
   284     ///
   285     /// Add a new edge map skipper command for the reader.
   286     template <typename Reader>
   287     GraphReader& skipEdgeMap(std::string name,
   288 			     const Reader& reader = Reader()) {
   289       if (edge_map_readers.find(name) != edge_map_readers.end()) {
   290 	ErrorMessage msg;
   291 	msg << "Multiple read rule for edge map: " << name;
   292 	throw IOParameterError(msg.message());
   293       }
   294       edge_map_readers.insert(
   295         make_pair(name, new SkipReader<Edge, Reader>(reader)));
   296       return *this;
   297     }
   298 
   299     /// \brief Add a new labeled node reader for the reader.
   300     ///
   301     /// Add a new labeled node reader for the reader.
   302     GraphReader& addNode(std::string name, Node& node) {
   303       if (node_readers.find(name) != node_readers.end()) {
   304 	ErrorMessage msg;
   305 	msg << "Multiple read rule for node: " << name;
   306 	throw IOParameterError(msg.message());
   307       }
   308       node_readers.insert(make_pair(name, &node));
   309       return *this;
   310     }
   311 
   312     /// \brief Add a new labeled edge reader for the reader.
   313     ///
   314     /// Add a new labeled edge reader for the reader.
   315     GraphReader& addEdge(std::string name, Edge& edge) {
   316       if (edge_readers.find(name) != edge_readers.end()) {
   317 	ErrorMessage msg;
   318 	msg << "Multiple read rule for edge: " << name;
   319 	throw IOParameterError(msg.message());
   320       }
   321       edge_readers.insert(make_pair(name, &edge));
   322       return *this;
   323     }
   324 
   325     /// \brief Executes the reader commands.
   326     ///
   327     /// Executes the reader commands.
   328     void run() {
   329       int line_num = 0;
   330       std::auto_ptr<InverterBase<Node> > nodeInverter;
   331       std::auto_ptr<InverterBase<Edge> > edgeInverter;
   332       try {
   333 	std::string line = readNotEmptyLine(is, line_num);
   334 	if (line.find("@nodeset") == 0) {
   335 	  line = readNodeSet(line_num, nodeInverter);
   336 	} 
   337 	if (line.find("@edgeset") == 0) {
   338 	  line = readEdgeSet(line_num, edgeInverter, nodeInverter);
   339 	}
   340 	if (line.find("@nodes") == 0) {
   341 	  line = readNodes(line_num, nodeInverter);
   342 	}
   343 	if (line.find("@edges") == 0) {
   344 	  line = readEdges(line_num, edgeInverter);
   345 	}
   346 	if (line.find("@end") != 0) {
   347 	  throw DataFormatError("Invalid control sequence error");
   348 	}
   349       } catch (DataFormatError e) {
   350 	e.line(line_num);
   351 	throw e;
   352       }
   353     }
   354 
   355   private:
   356 
   357     template <typename Item> class InverterBase;
   358 
   359     std::string readNodeSet(int& line_num, 
   360 			    std::auto_ptr<InverterBase<Node> >& nodeInverter) {
   361       std::vector<ReaderBase<Node>* > index;
   362       {
   363 	std::string line = readNotEmptyLine(is, line_num);    
   364 	std::string id;
   365 	std::istringstream ls(line);	
   366 	while (ls >> id) {
   367 	  if (id[0] == '#') break;
   368 	  typename NodeMapReaders::iterator it = node_map_readers.find(id);
   369 	  if (it != node_map_readers.end()) {
   370 	    index.push_back(it->second);
   371 	    node_map_readers.erase(it);
   372 	  } else {
   373 	    index.push_back(&nodeSkipper);
   374 	  }
   375 	}
   376       }
   377 
   378       if (index.size() == 0) {
   379 	throw DataFormatError("Cannot find node id map");
   380       }
   381 
   382       nodeInverter = 
   383 	std::auto_ptr<InverterBase<Node> >(index[0]->getInverter());
   384       std::string line;
   385       while (line = readNotEmptyLine(is, line_num), line[0] != '@') {
   386 	Node node = graph.addNode();
   387 	std::istringstream ls(line);
   388 	nodeInverter->read(ls, node);
   389 	for (int i = 1; i < (int)index.size(); ++i) {
   390 	  index[i]->read(ls, node);
   391 	}
   392       }
   393       return line;
   394     }
   395 
   396     std::string readEdgeSet(int& line_num, 
   397 			    std::auto_ptr<InverterBase<Edge> >& edgeInverter, 
   398 			    std::auto_ptr<InverterBase<Node> >& nodeInverter) {
   399       std::vector<ReaderBase<Edge>*> index;
   400       {
   401 	std::string line = readNotEmptyLine(is, line_num);    
   402 	std::string id;
   403 	std::istringstream ls(line);	
   404 	while (ls >> id) {
   405 	  if (id[0] == '#') break;
   406 	  typename EdgeMapReaders::iterator it = edge_map_readers.find(id);
   407 	  if (it != edge_map_readers.end()) {
   408 	    index.push_back(it->second);
   409 	    edge_map_readers.erase(it);
   410 	  } else {
   411 	    index.push_back(&edgeSkipper);
   412 	  }
   413 	}
   414       }
   415 
   416       if (index.size() == 0) {
   417 	throw DataFormatError("Cannot find edge id map");
   418       }
   419 
   420       edgeInverter = 
   421 	std::auto_ptr<InverterBase<Edge> >(index[0]->getInverter());
   422       std::string line;
   423       while (line = readNotEmptyLine(is, line_num), line[0] != '@') {	
   424 	std::istringstream ls(line);
   425 	Node source = nodeInverter->read(ls);
   426 	Node target = nodeInverter->read(ls);
   427 	Edge edge = graph.addEdge(source, target);
   428 	edgeInverter->read(ls, edge);
   429 	for (int i = 1; i < (int)index.size(); ++i) {
   430 	  index[i]->read(ls, edge);
   431 	}
   432       }      
   433       return line;
   434     }
   435 
   436     std::string readNodes(int& line_num, 
   437 			  std::auto_ptr<InverterBase<Node> >& nodeInverter) {
   438       std::string line;
   439       while (line = readNotEmptyLine(is, line_num), line[0] != '@') {
   440 	std::istringstream ls(line);
   441 	std::string name;
   442 	ls >> name;
   443 	typename NodeReaders::iterator it = node_readers.find(name);
   444 	if (it != node_readers.end()) {
   445 	  *(it -> second) = nodeInverter->read(ls);
   446 	} 
   447       }        
   448       return line;
   449     }
   450 
   451     std::string readEdges(int& line_num, 
   452 			  std::auto_ptr<InverterBase<Edge> >& edgeInverter) {
   453       std::string line;
   454       while (line = readNotEmptyLine(is, line_num), line[0] != '@') {
   455 	std::istringstream ls(line);
   456 	std::string name;
   457 	ls >> name;
   458 	typename EdgeReaders::iterator it = edge_readers.find(name);
   459 	if (it != edge_readers.end()) {
   460 	  *(it -> second) = edgeInverter->read(ls);
   461 	} 
   462       }        
   463       return line;    
   464     }
   465 
   466     std::string readNotEmptyLine(std::istream& is, int& line_num) {
   467       std::string line;
   468       while (++line_num, getline(is, line)) {	
   469 	int vi = line.find_first_not_of(" \t");
   470 	if (vi != (int)std::string::npos && line[vi] != '#') {
   471 	  return line.substr(vi);
   472 	}
   473       }
   474       throw DataFormatError("End of stream error");
   475     }
   476     
   477     // Inverters store and give back the Item from the id,
   478     // and may put the ids into a map.
   479     
   480     template <typename _Item>
   481     class InverterBase {
   482     public:
   483       typedef _Item Item;
   484       virtual void read(std::istream&, const Item&) = 0;
   485       virtual Item read(std::istream&) = 0;
   486     };
   487 
   488     template <typename _Item, typename _Map, typename _Reader>
   489     class MapReaderInverter : public InverterBase<_Item> {
   490     public:
   491       typedef _Item Item;
   492       typedef _Reader Reader;
   493       typedef typename Reader::Value Value;
   494       typedef _Map Map;
   495       typedef std::map<Value, Item> Inverse;
   496 
   497       Map& map;
   498       Reader reader;
   499       Inverse inverse;
   500 
   501       MapReaderInverter(Map& _map, const Reader& _reader) 
   502 	: map(_map), reader(_reader) {}
   503 
   504       virtual ~MapReaderInverter() {}
   505 
   506       virtual void read(std::istream& is, const Item& item) {
   507 	Value value;
   508 	reader.read(is, value);
   509 	map.set(item, value);
   510 	typename Inverse::iterator it = inverse.find(value);
   511 	if (it == inverse.end()) {
   512 	  inverse.insert(std::make_pair(value, item));
   513 	} else {
   514 	  throw DataFormatError("Multiple ID occurence");
   515 	}
   516       }
   517 
   518       virtual Item read(std::istream& is) {
   519 	Value value;
   520 	reader.read(is, value);	
   521 	typename Inverse::const_iterator it = inverse.find(value);
   522 	if (it != inverse.end()) {
   523 	  return it->second;
   524 	} else {
   525 	  throw DataFormatError("Invalid ID error");
   526 	}
   527       }      
   528     };
   529 
   530     template <typename _Item, typename _Reader>
   531     class SkipReaderInverter : public InverterBase<_Item> {
   532     public:
   533       typedef _Item Item;
   534       typedef _Reader Reader;
   535       typedef typename Reader::Value Value;
   536       typedef std::map<Value, Item> Inverse;
   537 
   538       Reader reader;
   539 
   540       SkipReaderInverter(const Reader& _reader) 
   541 	: reader(_reader) {}
   542 
   543       virtual ~SkipReaderInverter() {}
   544 
   545       virtual void read(std::istream& is, const Item& item) {
   546 	Value value;
   547 	reader.read(is, value);
   548 	typename Inverse::iterator it = inverse.find(value);
   549 	if (it == inverse.end()) {
   550 	  inverse.insert(std::make_pair(value, item));
   551 	} else {
   552 	  throw DataFormatError("Multiple ID occurence error");
   553 	}
   554       }
   555 
   556       virtual Item read(std::istream& is) {
   557 	Value value;
   558 	reader.read(is, value);	
   559 	typename Inverse::const_iterator it = inverse.find(value);
   560 	if (it != inverse.end()) {
   561 	  return it->second;
   562 	} else {
   563 	  throw DataFormatError("Invalid ID error");
   564 	}
   565       }      
   566     private:
   567       Inverse inverse;
   568     };
   569 
   570     // Readers
   571 
   572     template <typename _Item>    
   573     class ReaderBase {
   574     public:
   575       typedef _Item Item;
   576 
   577       //      virtual ~ReaderBase() {}
   578 
   579       virtual void read(std::istream& is, const Item& item) = 0;
   580       virtual InverterBase<_Item>* getInverter() = 0;
   581     };
   582 
   583     template <typename _Item, typename _Map, typename _Reader>
   584     class MapReader : public ReaderBase<_Item> {
   585     public:
   586       typedef _Map Map;
   587       typedef _Reader Reader;
   588       typedef typename Reader::Value Value;
   589       typedef _Item Item;
   590       
   591       Map& map;
   592       Reader reader;
   593 
   594       MapReader(Map& _map, const Reader& _reader) 
   595 	: map(_map), reader(_reader) {}
   596 
   597       virtual ~MapReader() {}
   598 
   599       virtual void read(std::istream& is, const Item& item) {
   600 	Value value;
   601 	reader.read(is, value);
   602 	map.set(item, value);
   603       }
   604 
   605       virtual InverterBase<_Item>* getInverter() {
   606 	return new MapReaderInverter<Item, Map, Reader>(map, reader);
   607       }
   608     };
   609 
   610 
   611     template <typename _Item, typename _Reader>
   612     class SkipReader : public ReaderBase<_Item> {
   613     public:
   614       typedef _Reader Reader;
   615       typedef typename Reader::Value Value;
   616       typedef _Item Item;
   617 
   618       Reader reader;
   619       SkipReader(const Reader& _reader) : reader(_reader) {}
   620 
   621       virtual ~SkipReader() {}
   622 
   623       virtual void read(std::istream& is, const Item&) {
   624 	Value value;
   625 	reader.read(is, value);
   626       }      
   627 
   628       virtual InverterBase<Item>* getInverter() {
   629 	return new SkipReaderInverter<Item, Reader>(reader);
   630       }
   631     };
   632 
   633 
   634     typedef std::map<std::string, ReaderBase<Node>*> NodeMapReaders;
   635     NodeMapReaders node_map_readers;
   636 
   637     typedef std::map<std::string, ReaderBase<Edge>*> EdgeMapReaders;
   638     EdgeMapReaders edge_map_readers;
   639 
   640     typedef std::map<std::string, Node*> NodeReaders;
   641     NodeReaders node_readers;
   642 
   643     typedef std::map<std::string, Edge*> EdgeReaders;
   644     EdgeReaders edge_readers;
   645 
   646     std::istream& is;
   647     Graph& graph;
   648 
   649     SkipReader<Node, DefaultReader> nodeSkipper;
   650     SkipReader<Edge, DefaultReader> edgeSkipper;
   651 
   652   };
   653 
   654   /// Ready to use reader function.  
   655   template<typename Graph, typename CapacityMap, typename CostMap>
   656   void readGraph(std::istream& is, Graph &g, CapacityMap& capacity, 
   657 		  typename Graph::Node &s, typename Graph::Node &t, 
   658 		  CostMap& cost) {
   659     GraphReader<Graph> reader(is, g);
   660     reader.addEdgeMap("capacity", capacity);
   661     reader.addEdgeMap("cost", cost);
   662     reader.addNode("source", s);
   663     reader.addNode("target", t);
   664     reader.run();
   665   }
   666 
   667   template<typename Graph, typename CapacityMap>
   668   void readGraph(std::istream& is, Graph &g, CapacityMap& capacity, 
   669 		  typename Graph::Node &s, typename Graph::Node &t) {
   670     GraphReader<Graph> reader(is, g);
   671     reader.addEdgeMap("capacity", capacity);
   672     reader.addNode("source", s);
   673     reader.addNode("target", t);
   674     reader.run();
   675   }
   676 
   677   template<typename Graph, typename CapacityMap>
   678   void readGraph(std::istream& is, Graph &g, CapacityMap& capacity, 
   679 		  typename Graph::Node &s) {
   680     GraphReader<Graph> reader(is, g);
   681     reader.addEdgeMap("capacity", capacity);
   682     reader.addNode("source", s);
   683     reader.run();
   684   }
   685 
   686   template<typename Graph, typename CapacityMap>
   687   void readGraph(std::istream& is, Graph &g, CapacityMap& capacity) {
   688     GraphReader<Graph> reader(is, g);
   689     reader.addEdgeMap("capacity", capacity);
   690     reader.run();
   691   }
   692 
   693   template<typename Graph>
   694   void readGraph(std::istream& is, Graph &g) {
   695     GraphReader<Graph> reader(is, g);
   696     reader.run();
   697   }
   698 
   699 }
   700 
   701 #endif