COIN-OR::LEMON - Graph Library

Ticket #35: lgf_io-single.patch

File lgf_io-single.patch, 40.0 KB (added by Alpar Juttner, 17 years ago)

The changesets of lgf_io.hg merged into a single one.

  • demo/Makefile.am

    # HG changeset patch
    # User Balazs Dezso <deba@inf.elte.hu>
    # Date 1208030668 -3600
    # Node ID 7f0124fc9658c70d00d7f9e1c7ec310e7335c058
    # Parent  ae7785fe84315de6be35a182420b6369e851f00d
    Redesigned .lgf reader/writer
    
    diff --git a/demo/Makefile.am b/demo/Makefile.am
    a b  
    44if WANT_DEMO
    55
    66noinst_PROGRAMS += \
    7         demo/arg_parser_demo
     7        demo/arg_parser_demo \
     8        demo/lgf_demo
    89
    910endif WANT_DEMO
    1011
    1112demo_arg_parser_demo_SOURCES = demo/arg_parser_demo.cc
     13demo_lgf_demo_SOURCES = demo/lgf_demo.cc
    1214
  • new file demo/lgf_demo.cc

    diff --git a/demo/lgf_demo.cc b/demo/lgf_demo.cc
    new file mode 100644
    - +  
     1/* -*- C++ -*-
     2 *
     3 * This file is a part of LEMON, a generic C++ optimization library
     4 *
     5 * Copyright (C) 2003-2008
     6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7 * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8 *
     9 * Permission to use, modify and distribute this software is granted
     10 * provided that this copyright notice appears in all copies. For
     11 * precise terms see the accompanying LICENSE file.
     12 *
     13 * This software is provided "AS IS" with no warranty of any kind,
     14 * express or implied, and with no claim as to its suitability for any
     15 * purpose.
     16 *
     17 */
     18
     19///\ingroup demos
     20///\file
     21///\brief Demonstrating graph input and output
     22///
     23/// This simple demo program gives an example of how to read and write
     24/// a graph and additional maps (on the nodes or the edges) from/to a
     25/// stream.
     26///
     27/// \include reader_writer_demo.cc
     28
     29#include <iostream>
     30#include <lemon/smart_graph.h>
     31#include <lemon/lgf_reader.h>
     32#include <lemon/lgf_writer.h>
     33#include <lemon/random.h>
     34
     35
     36using namespace lemon;
     37
     38int main(int argc, const char *argv[]) {
     39  SmartDigraph digraph;
     40
     41  std::stringstream ss;
     42
     43  try {
     44
     45    typedef SmartDigraph Digraph;
     46    typedef Digraph::Node Node;
     47    typedef Digraph::Arc Arc;
     48    typedef Digraph::ArcIt ArcIt;
     49
     50    typedef Digraph::NodeMap<int> PotentialMap;
     51    typedef Digraph::ArcMap<int> CapacityMap;
     52    typedef Digraph::ArcMap<std::string> NameMap;
     53
     54    const int n = argc > 1 ? std::atoi(argv[1]) : 20;
     55    const int e = argc > 2 ? std::atoi(argv[2]) : static_cast<int>(n * log(n));
     56    const int m = argc > 3 ? std::atoi(argv[3]) : 100;
     57
     58    Digraph digraph;
     59    PotentialMap potential(digraph);
     60    CapacityMap capacity(digraph);
     61    NameMap name(digraph);
     62
     63    std::vector<Node> nodes;
     64    for (int i = 0; i < n; ++i) {
     65      Node node = digraph.addNode();
     66      potential[node] = rnd[m];
     67      nodes.push_back(node);
     68    }
     69
     70    std::vector<Arc> arcs;
     71    for (int i = 0; i < e; ++i) {
     72      int s = rnd[n];
     73      int t = rnd[n];
     74      int c = rnd[m];
     75      Arc arc = digraph.addArc(nodes[s], nodes[t]);
     76      capacity[arc] = c;
     77      std::ostringstream os;
     78      os << "arc \t" << i << std::endl;
     79      name[arc] = os.str();
     80      arcs.push_back(arc);
     81    }
     82
     83
     84    DigraphWriter<Digraph>(ss, digraph).
     85      nodeMap("potential", potential).
     86      arcMap("capacity", capacity).
     87      arcMap("name", name).
     88      node("source", nodes[0]).
     89      node("target", nodes[1]).
     90      arc("bottleneck", arcs[e / 2]).
     91      attribute("creator", "lemon library").
     92      run();
     93
     94  } catch (DataFormatError& error) {
     95    std::cerr << error.what() << std::endl;
     96  }
     97
     98  try {
     99
     100    typedef SmartDigraph Digraph;
     101    typedef Digraph::Node Node;
     102    typedef Digraph::Arc Arc;
     103    typedef Digraph::ArcIt ArcIt;
     104
     105    typedef Digraph::NodeMap<int> PotentialMap;
     106    typedef Digraph::ArcMap<int> CapacityMap;
     107    typedef Digraph::ArcMap<std::string> NameMap;
     108
     109    Digraph digraph;
     110    PotentialMap potential(digraph);
     111    CapacityMap capacity(digraph);
     112    NameMap name(digraph);
     113
     114    Node s, t;
     115    Arc a;
     116   
     117    std::string creator;
     118
     119    DigraphReader<Digraph>(ss, digraph).
     120      nodeMap("potential", potential).
     121      arcMap("capacity", capacity).
     122      arcMap("name", name).
     123      node("source", s).
     124      node("target", t).
     125      arc("bottleneck", a).
     126      attribute("creator", creator).
     127      run();
     128
     129    DigraphWriter<Digraph>(std::cout, digraph).
     130      nodeMap("potential", potential).
     131      arcMap("capacity", capacity).
     132      arcMap("name", name).
     133      node("source", s).
     134      node("target", t).
     135      arc("bottleneck", a).
     136      attribute("creator", creator).
     137      run();
     138
     139  } catch (DataFormatError& error) {
     140    std::cerr << error.what() << std::endl;
     141  }
     142
     143
     144  return 0;
     145}
  • lemon/Makefile.am

    diff --git a/lemon/Makefile.am b/lemon/Makefile.am
    a b  
    2727        lemon/error.h \
    2828        lemon/graph_utils.h \
    2929        lemon/kruskal.h \
     30        lemon/lgf_reader.h \
    3031        lemon/list_graph.h \
    3132        lemon/maps.h \
    3233        lemon/math.h \
  • new file lemon/lgf_reader.h

    diff --git a/lemon/lgf_reader.h b/lemon/lgf_reader.h
    new file mode 100644
    - +  
     1/* -*- C++ -*-
     2 *
     3 * This file is a part of LEMON, a generic C++ optimization library
     4 *
     5 * Copyright (C) 2003-2008
     6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7 * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8 *
     9 * Permission to use, modify and distribute this software is granted
     10 * provided that this copyright notice appears in all copies. For
     11 * precise terms see the accompanying LICENSE file.
     12 *
     13 * This software is provided "AS IS" with no warranty of any kind,
     14 * express or implied, and with no claim as to its suitability for any
     15 * purpose.
     16 *
     17 */
     18
     19///\ingroup lemon_io
     20///\file
     21///\brief Lemon Graph Format reader.
     22
     23
     24#ifndef LEMON_LGF_READER_H
     25#define LEMON_LGF_READER_H
     26
     27#include <iostream>
     28#include <fstream>
     29#include <sstream>
     30
     31#include <map>
     32#include <typeinfo>
     33
     34#include <lemon/assert.h>
     35#include <lemon/graph_utils.h>
     36
     37#include <lemon/concept_check.h>
     38#include <lemon/concepts/maps.h>
     39
     40namespace lemon {
     41
     42  namespace _reader_bits {
     43
     44    template <typename Value>
     45    struct DefaultConverter {
     46      Value operator()(const std::string& str) {
     47        std::istringstream is(str);
     48        Value value;
     49        is >> value;
     50
     51        char c;
     52        if (is >> std::ws >> c) {
     53          throw DataFormatError("Remaining characters in token");
     54        }
     55        return value;
     56      }
     57    };
     58
     59    template <>
     60    struct DefaultConverter<std::string> {
     61      std::string operator()(const std::string& str) {
     62        return str;
     63      }
     64    };
     65
     66    template <typename _Item>   
     67    class MapStorageBase {
     68    public:
     69      typedef _Item Item;
     70
     71    private:     
     72      bool _touched;
     73
     74    public:
     75      MapStorageBase() : _touched(false) {}
     76      virtual ~MapStorageBase() {}
     77
     78      void touch(bool value = true) { _touched = value; }
     79      bool touched() const { return _touched; }
     80
     81      virtual void set(const Item& item, const std::string& value) = 0;
     82
     83    };
     84
     85    template <typename _Item, typename _Map,
     86              typename _Converter = DefaultConverter<typename _Map::Value> >
     87    class MapStorage : public MapStorageBase<_Item> {
     88    public:
     89      typedef _Map Map;
     90      typedef _Converter Converter;
     91      typedef _Item Item;
     92     
     93    private:
     94      Map& _map;
     95      Converter _converter;
     96
     97    public:
     98      MapStorage(Map& map, const Converter& converter = Converter())
     99        : _map(map), _converter(converter) {}
     100      virtual ~MapStorage() {}
     101
     102      virtual void set(const Item& item ,const std::string& value) {
     103        _map.set(item, _converter(value));
     104      }
     105    };
     106
     107    class ValueStorageBase {
     108    private:
     109      bool _touched;
     110
     111    public:
     112      ValueStorageBase() : _touched(false) {}
     113      virtual ~ValueStorageBase() {}
     114
     115      void touch() { _touched = true; }
     116      bool touched() const { return _touched; }
     117
     118      virtual void set(const std::string&) = 0;
     119    };
     120
     121    template <typename _Value, typename _Converter = DefaultConverter<_Value> >
     122    class ValueStorage : public ValueStorageBase {
     123    public:
     124      typedef _Value Value;
     125      typedef _Converter Converter;
     126
     127    private:
     128      Value& _value;
     129      Converter _converter;
     130
     131    public:
     132      ValueStorage(Value& value, const Converter& converter = Converter())
     133        : _value(value), _converter(converter) {}
     134
     135      virtual void set(const std::string& value) {
     136        _value = _converter(value);
     137      }
     138    };
     139
     140    template <typename Value>
     141    struct MapLookUpConverter {
     142      const std::map<std::string, Value>& _map;
     143
     144      MapLookUpConverter(const std::map<std::string, Value>& map)
     145        : _map(map) {}
     146
     147      Value operator()(const std::string& str) {
     148        typename std::map<std::string, Value>::const_iterator it =
     149          _map.find(str);
     150        if (it == _map.end()) {
     151          std::ostringstream msg;
     152          msg << "Item not found: " << str;
     153          throw DataFormatError(msg.str().c_str());
     154        }
     155        return it->second;
     156      }
     157    };
     158
     159    bool isWhiteSpace(char c) {
     160      return c == ' ' || c == '\t' || c == '\v' ||
     161        c == '\n' || c == '\r' || c == '\f';
     162    }
     163   
     164    bool isOct(char c) {
     165      return '0' <= c && c <='7';
     166    }
     167   
     168    int valueOct(char c) {
     169      LEMON_ASSERT(isOct(c), "The character is not octal.");
     170      return c - '0';
     171    }
     172
     173    bool isHex(char c) {
     174      return ('0' <= c && c <= '9') ||
     175        ('a' <= c && c <= 'z') ||
     176        ('A' <= c && c <= 'Z');
     177    }
     178   
     179    int valueHex(char c) {
     180      LEMON_ASSERT(isHex(c), "The character is not hexadecimal.");
     181      if ('0' <= c && c <= '9') return c - '0';
     182      if ('a' <= c && c <= 'z') return c - 'a' + 10;
     183      return c - 'A' + 10;
     184    }
     185
     186    char readEscape(std::istream& is) {
     187      char c;
     188      if (!is.get(c))
     189        throw DataFormatError("Escape format error");
     190
     191      switch (c) {
     192      case '\\':
     193        return '\\';
     194      case '\"':
     195        return '\"';
     196      case '\'':
     197        return '\'';
     198      case '\?':
     199        return '\?';
     200      case 'a':
     201        return '\a';
     202      case 'b':
     203        return '\b';
     204      case 'f':
     205        return '\f';
     206      case 'n':
     207        return '\n';
     208      case 'r':
     209        return '\r';
     210      case 't':
     211        return '\t';
     212      case 'v':
     213        return '\v';
     214      case 'x':
     215        {
     216          int code;
     217          if (!is.get(c) || !isHex(c))
     218            throw DataFormatError("Escape format error");
     219          else if (code = valueHex(c), !is.get(c) || !isHex(c)) is.putback(c);
     220          else code = code * 16 + valueHex(c);
     221          return code;
     222        }
     223      default:
     224        {
     225          int code;
     226          if (!isOct(c))
     227            throw DataFormatError("Escape format error");
     228          else if (code = valueOct(c), !is.get(c) || !isOct(c))
     229            is.putback(c);
     230          else if (code = code * 8 + valueOct(c), !is.get(c) || !isOct(c))
     231            is.putback(c);
     232          else code = code * 8 + valueOct(c);
     233          return code;
     234        }             
     235      }
     236    }
     237   
     238    std::istream& readToken(std::istream& is, std::string& str) {
     239      std::ostringstream os;
     240
     241      char c;
     242      is >> std::ws;
     243     
     244      if (!is.get(c))
     245        throw DataFormatError("Token not found");
     246
     247      if (c == '\"') {
     248        while (is.get(c) && c != '\"') {
     249          if (c == '\\')
     250            c = readEscape(is);
     251          os << c;
     252        }
     253        if (!is)
     254          throw DataFormatError("Quoted format error");
     255      } else {
     256        is.putback(c);
     257        while (is.get(c) && !isWhiteSpace(c)) {
     258          if (c == '\\')
     259            c = readEscape(is);
     260          os << c;
     261        }
     262        if (!is) {
     263          is.clear();
     264        } else {
     265          is.putback(c);
     266        }
     267      }
     268      str = os.str();
     269      return is;
     270    }
     271
     272  }
     273 
     274  /// \e
     275  template <typename _Digraph>
     276  class DigraphReader {
     277  public:
     278
     279    typedef _Digraph Digraph;
     280    GRAPH_TYPEDEFS(typename Digraph);
     281   
     282  private:
     283
     284
     285    std::istream* _is;
     286    bool local_is;
     287
     288    Digraph& _digraph;
     289
     290    std::string _nodes_caption;
     291    std::string _arcs_caption;
     292    std::string _attributes_caption;
     293
     294    typedef std::map<std::string, Node> NodeIndex;
     295    NodeIndex _node_index;
     296    typedef std::map<std::string, Arc> ArcIndex;
     297    ArcIndex _arc_index;
     298   
     299    typedef std::multimap<std::string, _reader_bits::MapStorageBase<Node>* >
     300      NodeMaps;   
     301    NodeMaps _node_maps;
     302
     303    typedef std::multimap<std::string, _reader_bits::MapStorageBase<Arc>* >
     304      ArcMaps;
     305    ArcMaps _arc_maps;
     306
     307    typedef std::multimap<std::string, _reader_bits::ValueStorageBase*>
     308      Attributes;
     309    Attributes _attributes;
     310
     311    int line_num;
     312    std::istringstream line;
     313
     314  public:
     315
     316    /// \e
     317    DigraphReader(std::istream& is, Digraph& digraph)
     318      : _is(&is), local_is(false), _digraph(digraph) {}
     319
     320    /// \e
     321    DigraphReader(const std::string& fn, Digraph& digraph)
     322      : _is(new std::ifstream(fn.c_str())), local_is(true), _digraph(digraph) {}
     323
     324    /// \e
     325    DigraphReader(const char* fn, Digraph& digraph)
     326      : _is(new std::ifstream(fn)), local_is(true), _digraph(digraph) {}
     327
     328    DigraphReader(DigraphReader& other)
     329      : _is(other._is), local_is(other.local_is), _digraph(other._digraph) {
     330
     331      other.is = 0;
     332      other.local_is = false;
     333
     334      _node_index.swap(other._node_index);
     335      _arc_index.swap(other._arc_index);
     336
     337      _node_maps.swap(other._node_maps);
     338      _arc_maps.swap(other._arc_maps);
     339      _attributes.swap(other._attributes);
     340
     341      _nodes_caption = other._nodes_caption;
     342      _arcs_caption = other._arcs_caption;
     343      _attributes_caption = other._attributes_caption;
     344    }
     345
     346    /// \e
     347    ~DigraphReader() {
     348      for (typename NodeMaps::iterator it = _node_maps.begin();
     349           it != _node_maps.end(); ++it) {
     350        delete it->second;
     351      }
     352
     353      for (typename ArcMaps::iterator it = _arc_maps.begin();
     354           it != _arc_maps.end(); ++it) {
     355        delete it->second;
     356      }
     357
     358      for (typename Attributes::iterator it = _attributes.begin();
     359           it != _attributes.end(); ++it) {
     360        delete it->second;
     361      }
     362
     363      if (local_is) {
     364        delete _is;
     365      }
     366    }
     367
     368  private:
     369   
     370    DigraphReader& operator=(const DigraphReader&);
     371
     372  public:
     373
     374    /// \e
     375    template <typename Map>
     376    DigraphReader& nodeMap(const std::string& caption, Map& map) {
     377      checkConcept<concepts::WriteMap<Node, typename Map::Value>, Map>();
     378      _reader_bits::MapStorageBase<Node>* storage =
     379        new _reader_bits::MapStorage<Node, Map>(map);
     380      _node_maps.insert(std::make_pair(caption, storage));
     381      return *this;
     382    }
     383
     384    /// \e
     385    template <typename Map, typename Converter>
     386    DigraphReader& nodeMap(const std::string& caption, Map& map,
     387                           const Converter& converter = Converter()) {
     388      checkConcept<concepts::WriteMap<Node, typename Map::Value>, Map>();
     389      _reader_bits::MapStorageBase<Node>* storage =
     390        new _reader_bits::MapStorage<Node, Map, Converter>(map, converter);
     391      _node_maps.insert(std::make_pair(caption, storage));
     392      return *this;
     393    }
     394
     395    /// \e
     396    template <typename Map>
     397    DigraphReader& arcMap(const std::string& caption, Map& map) {
     398      checkConcept<concepts::WriteMap<Arc, typename Map::Value>, Map>();
     399      _reader_bits::MapStorageBase<Arc>* storage =
     400        new _reader_bits::MapStorage<Arc, Map>(map);
     401      _arc_maps.insert(std::make_pair(caption, storage));
     402      return *this;
     403    }
     404
     405    /// \e
     406    template <typename Map, typename Converter>
     407    DigraphReader& arcMap(const std::string& caption, Map& map,
     408                          const Converter& converter = Converter()) {
     409      checkConcept<concepts::WriteMap<Arc, typename Map::Value>, Map>();
     410      _reader_bits::MapStorageBase<Arc>* storage =
     411        new _reader_bits::MapStorage<Arc, Map, Converter>(map, converter);
     412      _arc_maps.insert(std::make_pair(caption, storage));
     413      return *this;
     414    }
     415
     416    /// \e
     417    template <typename Value>
     418    DigraphReader& attribute(const std::string& caption, Value& value) {
     419      _reader_bits::ValueStorageBase* storage =
     420        new _reader_bits::ValueStorage<Value>(value);
     421      _attributes.insert(std::make_pair(caption, storage));
     422      return *this;
     423    }
     424
     425    /// \e
     426    template <typename Value, typename Converter>
     427    DigraphReader& attribute(const std::string& caption, Value& value,
     428                             const Converter& converter = Converter()) {
     429      _reader_bits::ValueStorageBase* storage =
     430        new _reader_bits::ValueStorage<Value, Converter>(value, converter);
     431      _attributes.insert(std::make_pair(caption, storage));
     432      return *this;
     433    }
     434
     435    /// \e
     436    DigraphReader& node(const std::string& caption, Node& node) {
     437      typedef _reader_bits::MapLookUpConverter<Node> Converter;
     438      Converter converter(_node_index);
     439      _reader_bits::ValueStorageBase* storage =
     440        new _reader_bits::ValueStorage<Node, Converter>(node, converter);
     441      _attributes.insert(std::make_pair(caption, storage));
     442      return *this;
     443    }
     444
     445    /// \e
     446    DigraphReader& arc(const std::string& caption, Arc& arc) {
     447      typedef _reader_bits::MapLookUpConverter<Arc> Converter;
     448      Converter converter(_arc_index);
     449      _reader_bits::ValueStorageBase* storage =
     450        new _reader_bits::ValueStorage<Arc, Converter>(arc, converter);
     451      _attributes.insert(std::make_pair(caption, storage));
     452      return *this;
     453    }
     454
     455    /// \e
     456    DigraphReader& nodes(const std::string& caption) {
     457      _nodes_caption = caption;
     458      return *this;
     459    }
     460
     461    /// \e
     462    DigraphReader& arcs(const std::string& caption) {
     463      _arcs_caption = caption;
     464      return *this;
     465    }
     466
     467    /// \e
     468    DigraphReader& attributes(const std::string& caption) {
     469      _attributes_caption = caption;
     470      return *this;
     471    }
     472
     473  private:
     474
     475    bool readLine() {
     476      std::string str;
     477      while(++line_num, std::getline(*_is, str)) {
     478        line.clear(); line.str(str);
     479        char c;
     480        if (line >> std::ws >> c && c != '#') {
     481          line.putback(c);
     482          return true;
     483        }
     484      }
     485      return false;
     486    }
     487
     488    bool readSuccess() {
     489      return static_cast<bool>(*_is);
     490    }
     491   
     492    void skipSection() {
     493      char c;
     494      while (readSuccess() && line >> c && c != '@') {
     495        readLine();
     496      }
     497      line.putback(c);
     498    }
     499
     500    void readNodes() {
     501
     502      std::vector<std::vector<_reader_bits::MapStorageBase<Node>*> > maps;
     503      int label = -1;
     504
     505      if (readLine()) {
     506        std::string map;
     507        while (line >> map) {
     508
     509          if (map == "label") {
     510            label = maps.size();
     511          }
     512
     513          maps.push_back(std::vector<_reader_bits::MapStorageBase<Node>*>());
     514
     515          typename NodeMaps::iterator it = _node_maps.lower_bound(map);
     516          while (it != _node_maps.end() && it->first == map) {
     517            if (it->second->touched()) {
     518              std::ostringstream msg;
     519              msg << "Multiple occurence of node map " << map;
     520              throw DataFormatError(msg.str().c_str());
     521            }
     522            maps.back().push_back(it->second);
     523            it->second->touch();
     524            ++it;
     525          }
     526
     527        }
     528
     529        for (typename NodeMaps::iterator it = _node_maps.begin();
     530             it != _node_maps.end(); ++it) {
     531          if (!it->second->touched()) {
     532            std::ostringstream msg;
     533            msg << "Map not found in file: " << it->first;
     534            throw IoParameterError(msg.str().c_str());
     535          }
     536        }
     537      }
     538
     539      char c;
     540      while (readLine() && line >> c && c != '@') {
     541        line.putback(c);
     542        Node n = _digraph.addNode();
     543
     544        for (int i = 0; i < static_cast<int>(maps.size()); ++i) {
     545          std::string token;
     546          _reader_bits::readToken(line, token);
     547
     548          for (int j = 0; j < static_cast<int>(maps[i].size()); ++j) {
     549            maps[i][j]->set(n, token);
     550          }
     551         
     552          if (label == i) {
     553            _node_index.insert(std::make_pair(token, n));
     554          }
     555
     556        }
     557
     558        if (line >> std::ws >> c) {
     559          throw DataFormatError("Extra character on the end of line");
     560        }
     561      }
     562      if (readSuccess()) {
     563        line.putback(c);
     564      }
     565    }
     566
     567    void readArcs() {
     568
     569      std::vector<std::vector<_reader_bits::MapStorageBase<Arc>*> > maps;
     570      int label = -1;
     571
     572      if (readLine()) {
     573        std::string map;
     574        while (line >> map) {
     575
     576          if (map == "label") {
     577            label = maps.size();
     578          }
     579
     580          maps.push_back(std::vector<_reader_bits::MapStorageBase<Arc>*>());
     581
     582          typename ArcMaps::iterator it = _arc_maps.lower_bound(map);
     583          while (it != _arc_maps.end() && it->first == map) {
     584            if (it->second->touched()) {
     585              std::ostringstream msg;
     586              msg << "Multiple occurence of arc map " << map;
     587              throw DataFormatError(msg.str().c_str());
     588            }
     589            maps.back().push_back(it->second);
     590            it->second->touch();
     591            ++it;
     592          }
     593        }
     594
     595        for (typename ArcMaps::iterator it = _arc_maps.begin();
     596             it != _arc_maps.end(); ++it) {
     597          if (!it->second->touched()) {
     598            std::ostringstream msg;
     599            msg << "Map not found in file: " << it->first;
     600            throw IoParameterError(msg.str().c_str());
     601          }
     602        }
     603      }
     604     
     605      char c;
     606      while (readLine() && line >> c && c != '@') {
     607        line.putback(c);
     608
     609        Node s, t;
     610        {
     611          std::string token;
     612          typename NodeIndex::iterator it;
     613         
     614          _reader_bits::readToken(line, token);
     615          it = _node_index.find(token);
     616          if (it == _node_index.end()) {
     617            std::ostringstream msg;
     618            msg << "Item not found: " << token;
     619            throw DataFormatError(msg.str().c_str());
     620          }
     621          s = it->second;
     622
     623          _reader_bits::readToken(line, token);
     624          it = _node_index.find(token);
     625          if (it == _node_index.end()) {
     626            std::ostringstream msg;
     627            msg << "Item not found: " << token;
     628            throw DataFormatError(msg.str().c_str());
     629          }
     630          t = it->second;
     631        }
     632
     633        Arc a = _digraph.addArc(s, t);
     634
     635        for (int i = 0; i < static_cast<int>(maps.size()); ++i) {
     636          std::string token;
     637          _reader_bits::readToken(line, token);
     638                 
     639          for (int j = 0; j < static_cast<int>(maps[i].size()); ++j) {
     640            maps[i][j]->set(a, token);
     641          }
     642
     643          if (label == i) {
     644            _arc_index.insert(std::make_pair(token, a));
     645          }
     646         
     647        }
     648
     649        if (line >> std::ws >> c) {
     650          throw DataFormatError("Extra character on the end of line");
     651        }
     652      }
     653      if (readSuccess()) {
     654        line.putback(c);
     655      }
     656    }
     657
     658    void readAttributes() {
     659
     660      std::vector<std::string> maps;
     661
     662      char c;
     663      while (readLine() && line >> c && c != '@') {
     664        line.putback(c);
     665       
     666        std::string attr, token;
     667        line >> attr;
     668        _reader_bits::readToken(line, token);
     669        if (line >> c) {
     670          throw DataFormatError("Extra character on the end of line");   
     671        }
     672       
     673        typename Attributes::iterator it = _attributes.lower_bound(attr);
     674        while (it != _attributes.end() && it->first == attr) {
     675          if (it->second->touched()) {
     676            std::ostringstream msg;
     677            msg << "Multiple occurence of attribute " << attr;
     678            throw DataFormatError(msg.str().c_str());
     679          }
     680          it->second->set(token);
     681          it->second->touch();
     682          ++it;
     683        }
     684      }
     685      if (readSuccess()) {
     686        line.putback(c);
     687      }
     688      for (typename Attributes::iterator it = _attributes.begin();
     689           it != _attributes.end(); ++it) {
     690        if (!it->second->touched()) {
     691          std::ostringstream msg;
     692          msg << "Attribute not found in file: " << it->first;
     693          throw IoParameterError(msg.str().c_str());
     694        }       
     695      }
     696    }
     697
     698  public:
     699   
     700    /// \e
     701    void run() {
     702      LEMON_ASSERT(_is != 0, "This reader assigned to an other reader");
     703     
     704      bool nodes_done = false;
     705      bool arcs_done = false;
     706      bool attributes_done = false;
     707     
     708      readLine();
     709
     710      while (readSuccess()) {
     711        skipSection();
     712        try {
     713          std::string section, caption;
     714          line >> section >> caption;
     715
     716          if (section == "@nodes" && !nodes_done) {
     717            if (_nodes_caption.empty() || _nodes_caption == caption) {
     718              readNodes();
     719              nodes_done = true;
     720            }
     721          } else if ((section == "@arcs" || section == "@edges") &&
     722                     !arcs_done) {
     723            if (_arcs_caption.empty() || _arcs_caption == caption) {
     724              readArcs();
     725              arcs_done = true;
     726            }
     727          } else if (section == "@attributes" && !attributes_done) {
     728            if (_attributes_caption.empty() || _attributes_caption == caption) {
     729              readAttributes();
     730              attributes_done = true;
     731            }
     732          }
     733         
     734          skipSection();
     735        } catch (DataFormatError& error) {
     736          error.line(line_num);
     737          throw;
     738        }       
     739      }
     740
     741      if (!nodes_done) {
     742        throw DataFormatError("Section @nodes not found");
     743      }
     744
     745      if (!arcs_done) {
     746        throw DataFormatError("Section @arcs not found");
     747      }
     748
     749      if (!attributes_done && !_attributes.empty()) {
     750        throw DataFormatError("Section @attributes not found");
     751      }
     752
     753    }
     754   
     755  };
     756
     757  template <typename Digraph>
     758  DigraphReader<Digraph> digraphReader(std::istream& is, Digraph& digraph) {
     759    return DigraphReader<Digraph>(is, digraph);
     760  }
     761
     762  template <typename Digraph>
     763  DigraphReader<Digraph> digraphReader(const std::string& fn,
     764                                       Digraph& digraph) {
     765    return DigraphReader<Digraph>(fn, digraph);
     766  }
     767
     768  template <typename Digraph>
     769  DigraphReader<Digraph> digraphReader(const char* fn, Digraph& digraph) {
     770    return DigraphReader<Digraph>(fn, digraph);
     771  }
     772}
     773
     774#endif
  • new file lemon/lgf_writer.h

    diff --git a/lemon/lgf_writer.h b/lemon/lgf_writer.h
    new file mode 100644
    - +  
     1/* -*- C++ -*-
     2 *
     3 * This file is a part of LEMON, a generic C++ optimization library
     4 *
     5 * Copyright (C) 2003-2008
     6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7 * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8 *
     9 * Permission to use, modify and distribute this software is granted
     10 * provided that this copyright notice appears in all copies. For
     11 * precise terms see the accompanying LICENSE file.
     12 *
     13 * This software is provided "AS IS" with no warranty of any kind,
     14 * express or implied, and with no claim as to its suitability for any
     15 * purpose.
     16 *
     17 */
     18
     19///\ingroup lemon_io
     20///\file
     21///\brief Lemon Graph Format writer.
     22
     23
     24#ifndef LEMON_LGF_WRITER_H
     25#define LEMON_LGF_WRITER_H
     26
     27#include <iostream>
     28#include <fstream>
     29#include <sstream>
     30
     31#include <algorithm>
     32
     33#include <vector>
     34#include <functional>
     35
     36#include <lemon/assert.h>
     37#include <lemon/graph_utils.h>
     38
     39namespace lemon {
     40
     41  namespace _writer_bits {
     42
     43    template <typename Value>
     44    struct DefaultConverter {
     45      std::string operator()(const Value& value) {
     46        std::ostringstream os;
     47        os << value;
     48        return os.str();
     49      }
     50    };
     51
     52    template <typename T>
     53    bool operator<(const T&, const T&) {
     54      throw DataFormatError("Label map is not comparable");
     55    }
     56
     57    template <typename _Map>
     58    class MapLess {
     59    public:
     60      typedef _Map Map;
     61      typedef typename Map::Key Item;
     62
     63    private:
     64      const Map& _map;
     65     
     66    public:
     67      MapLess(const Map& map) : _map(map) {}
     68
     69      bool operator()(const Item& left, const Item& right) {
     70        return _map[left] < _map[right];
     71      }
     72    };
     73
     74    template <typename _Item>   
     75    class MapStorageBase {
     76    public:
     77      typedef _Item Item;
     78
     79    public:
     80      MapStorageBase() {}
     81      virtual ~MapStorageBase() {}
     82
     83      virtual std::string get(const Item& item) = 0;
     84      virtual void sort(std::vector<Item>&) = 0;
     85    };
     86
     87    template <typename _Item, typename _Map,
     88              typename _Converter = DefaultConverter<typename _Map::Value> >
     89    class MapStorage : public MapStorageBase<_Item> {
     90    public:
     91      typedef _Map Map;
     92      typedef _Converter Converter;
     93      typedef _Item Item;
     94     
     95    private:
     96      const Map& _map;
     97      Converter _converter;
     98
     99    public:
     100      MapStorage(const Map& map, const Converter& converter = Converter())
     101        : _map(map), _converter(converter) {}
     102      virtual ~MapStorage() {}
     103
     104      virtual std::string get(const Item& item) {
     105        return _converter(_map[item]);
     106      }
     107      virtual void sort(std::vector<Item>& items) {
     108        MapLess<Map> less(_map);
     109        std::sort(items.begin(), items.end(), less);
     110      }
     111    };
     112
     113    class ValueStorageBase {
     114    public:
     115      ValueStorageBase() {}
     116      virtual ~ValueStorageBase() {}
     117
     118      virtual std::string get() = 0;     
     119    };
     120
     121    template <typename _Value, typename _Converter = DefaultConverter<_Value> >
     122    class ValueStorage : public ValueStorageBase {
     123    public:
     124      typedef _Value Value;
     125      typedef _Converter Converter;
     126
     127    private:
     128      const Value& _value;
     129      Converter _converter;
     130
     131    public:
     132      ValueStorage(const Value& value, const Converter& converter = Converter())
     133        : _value(value), _converter(converter) {}
     134
     135      virtual std::string get() {
     136        return _converter(_value);
     137      }
     138    };
     139
     140    template <typename Value>
     141    struct MapLookUpConverter {
     142      const std::map<Value, std::string>& _map;
     143     
     144      MapLookUpConverter(const std::map<Value, std::string>& map)
     145        : _map(map) {}
     146     
     147      std::string operator()(const Value& str) {
     148        typename std::map<Value, std::string>::const_iterator it =
     149          _map.find(str);
     150        if (it == _map.end()) {
     151          throw DataFormatError("Item not found");
     152        }
     153        return it->second;
     154      }
     155    };
     156
     157    bool isWhiteSpace(char c) {
     158      return c == ' ' || c == '\t' || c == '\v' ||
     159        c == '\n' || c == '\r' || c == '\f';
     160    }
     161
     162    bool isEscaped(char c) {
     163      return c == '\\' || c == '\"' || c == '\'' ||
     164        c == '\a' || c == '\b';
     165    }
     166
     167    static void writeEscape(std::ostream& os, char c) {
     168      switch (c) {
     169      case '\\':
     170        os << "\\\\";
     171        return;
     172      case '\"':
     173        os << "\\\"";
     174        return;
     175      case '\a':
     176        os << "\\a";
     177        return;
     178      case '\b':
     179        os << "\\b";
     180        return;
     181      case '\f':
     182        os << "\\f";
     183        return;
     184      case '\r':
     185        os << "\\r";
     186        return;
     187      case '\n':
     188        os << "\\n";
     189        return;
     190      case '\t':
     191        os << "\\t";
     192        return;
     193      case '\v':
     194        os << "\\v";
     195        return;
     196      default:
     197        if (c < 0x20) {
     198          os << '\\' << std::oct << static_cast<int>(c);
     199        } else {
     200          os << c;
     201        }
     202        return;
     203      }     
     204    }
     205
     206    bool requireEscape(const std::string& str) {
     207      std::istringstream is(str);
     208      char c;
     209      while (is.get(c)) {
     210        if (isWhiteSpace(c) || isEscaped(c)) {
     211          return true;
     212        }
     213      }
     214      return false;
     215    }
     216   
     217    std::ostream& writeToken(std::ostream& os, const std::string& str) {
     218
     219      if (requireEscape(str)) {
     220        os << '\"';
     221        for (std::string::const_iterator it = str.begin();
     222             it != str.end(); ++it) {
     223          writeEscape(os, *it);
     224        }       
     225        os << '\"';
     226      } else {
     227        os << str;
     228      }
     229      return os;
     230    }
     231
     232  }
     233 
     234  /// \e
     235  template <typename _Digraph>
     236  class DigraphWriter {
     237  public:
     238
     239    typedef _Digraph Digraph;
     240    GRAPH_TYPEDEFS(typename Digraph);
     241   
     242  private:
     243
     244
     245    std::ostream* _os;
     246    bool local_os;
     247
     248    Digraph& _digraph;
     249
     250    std::string _nodes_caption;
     251    std::string _arcs_caption;
     252    std::string _attributes_caption;
     253   
     254    typedef std::map<Node, std::string> NodeIndex;
     255    NodeIndex _node_index;
     256    typedef std::map<Arc, std::string> ArcIndex;
     257    ArcIndex _arc_index;
     258
     259    typedef std::vector<std::pair<std::string,
     260      _writer_bits::MapStorageBase<Node>* > > NodeMaps;   
     261    NodeMaps _node_maps;
     262
     263    typedef std::vector<std::pair<std::string,
     264      _writer_bits::MapStorageBase<Arc>* > >ArcMaps;
     265    ArcMaps _arc_maps;
     266
     267    typedef std::vector<std::pair<std::string,
     268      _writer_bits::ValueStorageBase*> > Attributes;
     269    Attributes _attributes;
     270
     271  public:
     272
     273    /// \e
     274    DigraphWriter(std::ostream& is, Digraph& digraph)
     275      : _os(&is), local_os(false), _digraph(digraph) {}
     276
     277    /// \e
     278    DigraphWriter(const std::string& fn, Digraph& digraph)
     279      : _os(new std::ofstream(fn.c_str())), local_os(true), _digraph(digraph) {}
     280
     281    /// \e
     282    DigraphWriter(const char* fn, Digraph& digraph)
     283      : _os(new std::ofstream(fn)), local_os(true), _digraph(digraph) {}
     284
     285    DigraphWriter(DigraphWriter& other)
     286      : _os(other._os), local_os(other.local_os), _digraph(other._digraph) {
     287
     288      other.is = 0;
     289      other.local_os = false;
     290
     291      _node_index.swap(other._node_index);
     292      _arc_index.swap(other._arc_index);
     293
     294      _node_maps.swap(other._node_maps);
     295      _arc_maps.swap(other._arc_maps);
     296      _attributes.swap(other._attributes);
     297
     298      _nodes_caption = other._nodes_caption;
     299      _arcs_caption = other._arcs_caption;
     300      _attributes_caption = other._attributes_caption;
     301    }
     302
     303    /// \e
     304    ~DigraphWriter() {
     305      for (typename NodeMaps::iterator it = _node_maps.begin();
     306           it != _node_maps.end(); ++it) {
     307        delete it->second;
     308      }
     309
     310      for (typename ArcMaps::iterator it = _arc_maps.begin();
     311           it != _arc_maps.end(); ++it) {
     312        delete it->second;
     313      }
     314
     315      for (typename Attributes::iterator it = _attributes.begin();
     316           it != _attributes.end(); ++it) {
     317        delete it->second;
     318      }
     319
     320      if (local_os) {
     321        delete _os;
     322      }
     323    }
     324
     325  private:
     326   
     327    DigraphWriter& operator=(const DigraphWriter&);
     328
     329  public:
     330
     331    /// \e
     332    template <typename Map>
     333    DigraphWriter& nodeMap(const std::string& caption, const Map& map) {
     334      checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
     335      _writer_bits::MapStorageBase<Node>* storage =
     336        new _writer_bits::MapStorage<Node, Map>(map);
     337      _node_maps.push_back(std::make_pair(caption, storage));
     338      return *this;
     339    }
     340
     341    /// \e
     342    template <typename Map, typename Converter>
     343    DigraphWriter& nodeMap(const std::string& caption, const Map& map,
     344                           const Converter& converter = Converter()) {
     345      checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
     346      _writer_bits::MapStorageBase<Node>* storage =
     347        new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
     348      _node_maps.push_back(std::make_pair(caption, storage));
     349      return *this;
     350    }
     351
     352    /// \e
     353    template <typename Map>
     354    DigraphWriter& arcMap(const std::string& caption, const Map& map) {
     355      checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
     356      _writer_bits::MapStorageBase<Arc>* storage =
     357        new _writer_bits::MapStorage<Arc, Map>(map);
     358      _arc_maps.push_back(std::make_pair(caption, storage));
     359      return *this;
     360    }
     361
     362    /// \e
     363    template <typename Map, typename Converter>
     364    DigraphWriter& arcMap(const std::string& caption, const Map& map,
     365                          const Converter& converter = Converter()) {
     366      checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
     367      _writer_bits::MapStorageBase<Arc>* storage =
     368        new _writer_bits::MapStorage<Arc, Map, Converter>(map, converter);
     369      _arc_maps.push_back(std::make_pair(caption, storage));
     370      return *this;
     371    }
     372
     373    /// \e
     374    template <typename Value>
     375    DigraphWriter& attribute(const std::string& caption, const Value& value) {
     376      _writer_bits::ValueStorageBase* storage =
     377        new _writer_bits::ValueStorage<Value>(value);
     378      _attributes.push_back(std::make_pair(caption, storage));
     379      return *this;
     380    }
     381
     382    /// \e
     383    template <typename Value, typename Converter>
     384    DigraphWriter& attribute(const std::string& caption, const Value& value,
     385                             const Converter& converter = Converter()) {
     386      _writer_bits::ValueStorageBase* storage =
     387        new _writer_bits::ValueStorage<Value, Converter>(value, converter);
     388      _attributes.push_back(std::make_pair(caption, storage));
     389      return *this;
     390    }
     391
     392    /// \e
     393    DigraphWriter& node(const std::string& caption, const Node& node) {
     394      typedef _writer_bits::MapLookUpConverter<Node> Converter;
     395      Converter converter(_node_index);
     396      _writer_bits::ValueStorageBase* storage =
     397        new _writer_bits::ValueStorage<Node, Converter>(node, converter);
     398      _attributes.push_back(std::make_pair(caption, storage));
     399      return *this;
     400    }
     401
     402    /// \e
     403    DigraphWriter& arc(const std::string& caption, const Arc& arc) {
     404      typedef _writer_bits::MapLookUpConverter<Arc> Converter;
     405      Converter converter(_arc_index);
     406      _writer_bits::ValueStorageBase* storage =
     407        new _writer_bits::ValueStorage<Arc, Converter>(arc, converter);
     408      _attributes.push_back(std::make_pair(caption, storage));
     409      return *this;
     410    }
     411
     412    /// \e
     413    DigraphWriter& nodes(const std::string& caption) {
     414      _nodes_caption = caption;
     415      return *this;
     416    }
     417
     418    /// \e
     419    DigraphWriter& arcs(const std::string& caption) {
     420      _arcs_caption = caption;
     421      return *this;
     422    }
     423
     424    /// \e
     425    DigraphWriter& attributes(const std::string& caption) {
     426      _attributes_caption = caption;
     427      return *this;
     428    }
     429
     430  private:
     431
     432    void writeNodes() {
     433      _writer_bits::MapStorageBase<Node>* label = 0;
     434      for (typename NodeMaps::iterator it = _node_maps.begin();
     435           it != _node_maps.end(); ++it) {
     436        if (it->first == "label") {
     437          label = it->second;
     438          break;
     439        }
     440      }
     441
     442      *_os << "@nodes";
     443      if (!_nodes_caption.empty()) {
     444        *_os << ' ' << _nodes_caption;
     445      }
     446      *_os << std::endl;
     447
     448      if (label == 0) {
     449        *_os << "label" << '\t';
     450      }
     451      for (typename NodeMaps::iterator it = _node_maps.begin();
     452           it != _node_maps.end(); ++it) {
     453        *_os << it->first << '\t';
     454      }
     455      *_os << std::endl;
     456
     457      std::vector<Node> nodes;
     458      for (NodeIt n(_digraph); n != INVALID; ++n) {
     459        nodes.push_back(n);
     460      }
     461     
     462      if (label == 0) {
     463        IdMap<Digraph, Node> id_map(_digraph);
     464        _writer_bits::MapLess<IdMap<Digraph, Node> > id_less(id_map);
     465        std::sort(nodes.begin(), nodes.end(), id_less);
     466      } else {
     467        label->sort(nodes);
     468      }
     469
     470      for (int i = 0; i < static_cast<int>(nodes.size()); ++i) {
     471        Node n = nodes[i];
     472        if (label == 0) {
     473          std::ostringstream os;
     474          os << _digraph.id(n);
     475          _writer_bits::writeToken(*_os, os.str());
     476          *_os << '\t';
     477          _node_index.insert(std::make_pair(n, os.str()));
     478        }
     479        for (typename NodeMaps::iterator it = _node_maps.begin();
     480             it != _node_maps.end(); ++it) {
     481          std::string value = it->second->get(n);
     482          _writer_bits::writeToken(*_os, value);
     483          if (it->first == "label") {
     484            _node_index.insert(std::make_pair(n, value));
     485          }
     486          *_os << '\t';
     487        }
     488        *_os << std::endl;
     489      }
     490    }
     491
     492    void writeArcs() {
     493      _writer_bits::MapStorageBase<Arc>* label = 0;
     494      for (typename ArcMaps::iterator it = _arc_maps.begin();
     495           it != _arc_maps.end(); ++it) {
     496        if (it->first == "label") {
     497          label = it->second;
     498          break;
     499        }
     500      }
     501
     502      *_os << "@arcs";
     503      if (!_arcs_caption.empty()) {
     504        *_os << ' ' << _arcs_caption;
     505      }
     506      *_os << std::endl;
     507
     508      *_os << '\t' << '\t';
     509      if (label == 0) {
     510        *_os << "label" << '\t';
     511      }
     512      for (typename ArcMaps::iterator it = _arc_maps.begin();
     513           it != _arc_maps.end(); ++it) {
     514        *_os << it->first << '\t';
     515      }
     516      *_os << std::endl;
     517
     518      std::vector<Arc> arcs;
     519      for (ArcIt n(_digraph); n != INVALID; ++n) {
     520        arcs.push_back(n);
     521      }
     522     
     523      if (label == 0) {
     524        IdMap<Digraph, Arc> id_map(_digraph);
     525        _writer_bits::MapLess<IdMap<Digraph, Arc> > id_less(id_map);
     526        std::sort(arcs.begin(), arcs.end(), id_less);
     527      } else {
     528        label->sort(arcs);
     529      }
     530
     531      for (int i = 0; i < static_cast<int>(arcs.size()); ++i) {
     532        Arc a = arcs[i];
     533        _writer_bits::writeToken(*_os, _node_index.
     534                                 find(_digraph.source(a))->second);
     535        *_os << '\t';
     536        _writer_bits::writeToken(*_os, _node_index.
     537                                 find(_digraph.target(a))->second);
     538        *_os << '\t';
     539        if (label == 0) {
     540          std::ostringstream os;
     541          os << _digraph.id(a);
     542          _writer_bits::writeToken(*_os, os.str());
     543          *_os << '\t';
     544          _arc_index.insert(std::make_pair(a, os.str()));
     545        }
     546        for (typename ArcMaps::iterator it = _arc_maps.begin();
     547             it != _arc_maps.end(); ++it) {
     548          std::string value = it->second->get(a);
     549          _writer_bits::writeToken(*_os, value);
     550          if (it->first == "label") {
     551            _arc_index.insert(std::make_pair(a, value));
     552          }
     553          *_os << '\t';
     554        }
     555        *_os << std::endl;
     556      }
     557    }
     558
     559    void writeAttributes() {
     560      if (_attributes.empty()) return;
     561      *_os << "@attributes";
     562      if (!_attributes_caption.empty()) {
     563        *_os << ' ' << _attributes_caption;
     564      }
     565      *_os << std::endl;
     566      for (typename Attributes::iterator it = _attributes.begin();
     567           it != _attributes.end(); ++it) {
     568        *_os << it->first << ' ';
     569        _writer_bits::writeToken(*_os, it->second->get());
     570        *_os << std::endl;
     571      }
     572    }
     573   
     574  public:
     575   
     576    /// \e
     577    void run() {
     578      writeNodes();
     579      writeArcs();
     580      writeAttributes();
     581    }
     582   
     583  };
     584
     585  template <typename Digraph>
     586  DigraphWriter<Digraph> digraphWriter(std::istream& is, Digraph& digraph) {
     587    return DigraphWriter<Digraph>(is, digraph);
     588  }
     589
     590  template <typename Digraph>
     591  DigraphWriter<Digraph> digraphWriter(const std::string& fn,
     592                                       Digraph& digraph) {
     593    return DigraphWriter<Digraph>(fn, digraph);
     594  }
     595
     596  template <typename Digraph>
     597  DigraphWriter<Digraph> digraphWriter(const char* fn, Digraph& digraph) {
     598    return DigraphWriter<Digraph>(fn, digraph);
     599  }
     600}
     601
     602#endif