lemon/lgf_writer.h
author Daniel Poroszkai <poroszd@inf.elte.hu>
Sun, 05 Feb 2012 00:04:44 +0100
changeset 1029 374a9519986b
parent 1024 b84e68af8248
child 1030 4936be66d2f5
permissions -rw-r--r--
Update LGF reader to work with typesafe bipartite node sets (#69)
     1 /* -*- mode: C++; indent-tabs-mode: nil; -*-
     2  *
     3  * This file is a part of LEMON, a generic C++ optimization library.
     4  *
     5  * Copyright (C) 2003-2010
     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 \ref lgf-format "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/core.h>
    37 #include <lemon/maps.h>
    38 
    39 #include <lemon/concept_check.h>
    40 #include <lemon/concepts/maps.h>
    41 
    42 namespace lemon {
    43 
    44   namespace _writer_bits {
    45 
    46     template <typename Value>
    47     struct DefaultConverter {
    48       std::string operator()(const Value& value) {
    49         std::ostringstream os;
    50         os << value;
    51         return os.str();
    52       }
    53     };
    54 
    55     template <typename T>
    56     bool operator<(const T&, const T&) {
    57       throw FormatError("Label map is not comparable");
    58     }
    59 
    60     template <typename _Map>
    61     class MapLess {
    62     public:
    63       typedef _Map Map;
    64       typedef typename Map::Key Item;
    65 
    66     private:
    67       const Map& _map;
    68 
    69     public:
    70       MapLess(const Map& map) : _map(map) {}
    71 
    72       bool operator()(const Item& left, const Item& right) {
    73         return _map[left] < _map[right];
    74       }
    75     };
    76 
    77     template <typename _Graph, bool _dir, typename _Map>
    78     class GraphArcMapLess {
    79     public:
    80       typedef _Map Map;
    81       typedef _Graph Graph;
    82       typedef typename Graph::Edge Item;
    83 
    84     private:
    85       const Graph& _graph;
    86       const Map& _map;
    87 
    88     public:
    89       GraphArcMapLess(const Graph& graph, const Map& map)
    90         : _graph(graph), _map(map) {}
    91 
    92       bool operator()(const Item& left, const Item& right) {
    93         return _map[_graph.direct(left, _dir)] <
    94           _map[_graph.direct(right, _dir)];
    95       }
    96     };
    97 
    98     template <typename _Item>
    99     class MapStorageBase {
   100     public:
   101       typedef _Item Item;
   102 
   103     public:
   104       MapStorageBase() {}
   105       virtual ~MapStorageBase() {}
   106 
   107       virtual std::string get(const Item& item) = 0;
   108       virtual void sort(std::vector<Item>&) = 0;
   109     };
   110 
   111     template <typename _Item, typename _Map,
   112               typename _Converter = DefaultConverter<typename _Map::Value> >
   113     class MapStorage : public MapStorageBase<_Item> {
   114     public:
   115       typedef _Map Map;
   116       typedef _Converter Converter;
   117       typedef _Item Item;
   118 
   119     private:
   120       const Map& _map;
   121       Converter _converter;
   122 
   123     public:
   124       MapStorage(const Map& map, const Converter& converter = Converter())
   125         : _map(map), _converter(converter) {}
   126       virtual ~MapStorage() {}
   127 
   128       virtual std::string get(const Item& item) {
   129         return _converter(_map[item]);
   130       }
   131       virtual void sort(std::vector<Item>& items) {
   132         MapLess<Map> less(_map);
   133         std::sort(items.begin(), items.end(), less);
   134       }
   135     };
   136 
   137     template <typename _Graph, bool _dir, typename _Map,
   138               typename _Converter = DefaultConverter<typename _Map::Value> >
   139     class GraphArcMapStorage : public MapStorageBase<typename _Graph::Edge> {
   140     public:
   141       typedef _Map Map;
   142       typedef _Converter Converter;
   143       typedef _Graph Graph;
   144       typedef typename Graph::Edge Item;
   145       static const bool dir = _dir;
   146 
   147     private:
   148       const Graph& _graph;
   149       const Map& _map;
   150       Converter _converter;
   151 
   152     public:
   153       GraphArcMapStorage(const Graph& graph, const Map& map,
   154                          const Converter& converter = Converter())
   155         : _graph(graph), _map(map), _converter(converter) {}
   156       virtual ~GraphArcMapStorage() {}
   157 
   158       virtual std::string get(const Item& item) {
   159         return _converter(_map[_graph.direct(item, dir)]);
   160       }
   161       virtual void sort(std::vector<Item>& items) {
   162         GraphArcMapLess<Graph, dir, Map> less(_graph, _map);
   163         std::sort(items.begin(), items.end(), less);
   164       }
   165     };
   166 
   167     class ValueStorageBase {
   168     public:
   169       ValueStorageBase() {}
   170       virtual ~ValueStorageBase() {}
   171 
   172       virtual std::string get() = 0;
   173     };
   174 
   175     template <typename _Value, typename _Converter = DefaultConverter<_Value> >
   176     class ValueStorage : public ValueStorageBase {
   177     public:
   178       typedef _Value Value;
   179       typedef _Converter Converter;
   180 
   181     private:
   182       const Value& _value;
   183       Converter _converter;
   184 
   185     public:
   186       ValueStorage(const Value& value, const Converter& converter = Converter())
   187         : _value(value), _converter(converter) {}
   188 
   189       virtual std::string get() {
   190         return _converter(_value);
   191       }
   192     };
   193 
   194     template <typename Value>
   195     struct MapLookUpConverter {
   196       const std::map<Value, std::string>& _map;
   197 
   198       MapLookUpConverter(const std::map<Value, std::string>& map)
   199         : _map(map) {}
   200 
   201       std::string operator()(const Value& str) {
   202         typename std::map<Value, std::string>::const_iterator it =
   203           _map.find(str);
   204         if (it == _map.end()) {
   205           throw FormatError("Item not found");
   206         }
   207         return it->second;
   208       }
   209     };
   210 
   211     template <typename Graph>
   212     struct GraphArcLookUpConverter {
   213       const Graph& _graph;
   214       const std::map<typename Graph::Edge, std::string>& _map;
   215 
   216       GraphArcLookUpConverter(const Graph& graph,
   217                               const std::map<typename Graph::Edge,
   218                                              std::string>& map)
   219         : _graph(graph), _map(map) {}
   220 
   221       std::string operator()(const typename Graph::Arc& val) {
   222         typename std::map<typename Graph::Edge, std::string>
   223           ::const_iterator it = _map.find(val);
   224         if (it == _map.end()) {
   225           throw FormatError("Item not found");
   226         }
   227         return (_graph.direction(val) ? '+' : '-') + it->second;
   228       }
   229     };
   230 
   231     inline bool isWhiteSpace(char c) {
   232       return c == ' ' || c == '\t' || c == '\v' ||
   233         c == '\n' || c == '\r' || c == '\f';
   234     }
   235 
   236     inline bool isEscaped(char c) {
   237       return c == '\\' || c == '\"' || c == '\'' ||
   238         c == '\a' || c == '\b';
   239     }
   240 
   241     inline static void writeEscape(std::ostream& os, char c) {
   242       switch (c) {
   243       case '\\':
   244         os << "\\\\";
   245         return;
   246       case '\"':
   247         os << "\\\"";
   248         return;
   249       case '\a':
   250         os << "\\a";
   251         return;
   252       case '\b':
   253         os << "\\b";
   254         return;
   255       case '\f':
   256         os << "\\f";
   257         return;
   258       case '\r':
   259         os << "\\r";
   260         return;
   261       case '\n':
   262         os << "\\n";
   263         return;
   264       case '\t':
   265         os << "\\t";
   266         return;
   267       case '\v':
   268         os << "\\v";
   269         return;
   270       default:
   271         if (c < 0x20) {
   272           std::ios::fmtflags flags = os.flags();
   273           os << '\\' << std::oct << static_cast<int>(c);
   274           os.flags(flags);
   275         } else {
   276           os << c;
   277         }
   278         return;
   279       }
   280     }
   281 
   282     inline bool requireEscape(const std::string& str) {
   283       if (str.empty() || str[0] == '@') return true;
   284       std::istringstream is(str);
   285       char c;
   286       while (is.get(c)) {
   287         if (isWhiteSpace(c) || isEscaped(c)) {
   288           return true;
   289         }
   290       }
   291       return false;
   292     }
   293 
   294     inline std::ostream& writeToken(std::ostream& os, const std::string& str) {
   295 
   296       if (requireEscape(str)) {
   297         os << '\"';
   298         for (std::string::const_iterator it = str.begin();
   299              it != str.end(); ++it) {
   300           writeEscape(os, *it);
   301         }
   302         os << '\"';
   303       } else {
   304         os << str;
   305       }
   306       return os;
   307     }
   308 
   309     class Section {
   310     public:
   311       virtual ~Section() {}
   312       virtual void process(std::ostream& os) = 0;
   313     };
   314 
   315     template <typename Functor>
   316     class LineSection : public Section {
   317     private:
   318 
   319       Functor _functor;
   320 
   321     public:
   322 
   323       LineSection(const Functor& functor) : _functor(functor) {}
   324       virtual ~LineSection() {}
   325 
   326       virtual void process(std::ostream& os) {
   327         std::string line;
   328         while (!(line = _functor()).empty()) os << line << std::endl;
   329       }
   330     };
   331 
   332     template <typename Functor>
   333     class StreamSection : public Section {
   334     private:
   335 
   336       Functor _functor;
   337 
   338     public:
   339 
   340       StreamSection(const Functor& functor) : _functor(functor) {}
   341       virtual ~StreamSection() {}
   342 
   343       virtual void process(std::ostream& os) {
   344         _functor(os);
   345       }
   346     };
   347 
   348   }
   349 
   350   template <typename DGR>
   351   class DigraphWriter;
   352 
   353   template <typename TDGR>
   354   DigraphWriter<TDGR> digraphWriter(const TDGR& digraph,
   355                                    std::ostream& os = std::cout);
   356   template <typename TDGR>
   357   DigraphWriter<TDGR> digraphWriter(const TDGR& digraph, const std::string& fn);
   358 
   359   template <typename TDGR>
   360   DigraphWriter<TDGR> digraphWriter(const TDGR& digraph, const char* fn);
   361 
   362 
   363   /// \ingroup lemon_io
   364   ///
   365   /// \brief \ref lgf-format "LGF" writer for directed graphs
   366   ///
   367   /// This utility writes an \ref lgf-format "LGF" file.
   368   ///
   369   /// The writing method does a batch processing. The user creates a
   370   /// writer object, then various writing rules can be added to the
   371   /// writer, and eventually the writing is executed with the \c run()
   372   /// member function. A map writing rule can be added to the writer
   373   /// with the \c nodeMap() or \c arcMap() members. An optional
   374   /// converter parameter can also be added as a standard functor
   375   /// converting from the value type of the map to \c std::string. If it
   376   /// is set, it will determine how the value type of the map is written to
   377   /// the output stream. If the functor is not set, then a default
   378   /// conversion will be used. The \c attribute(), \c node() and \c
   379   /// arc() functions are used to add attribute writing rules.
   380   ///
   381   ///\code
   382   /// DigraphWriter<DGR>(digraph, std::cout).
   383   ///   nodeMap("coordinates", coord_map).
   384   ///   nodeMap("size", size).
   385   ///   nodeMap("title", title).
   386   ///   arcMap("capacity", cap_map).
   387   ///   node("source", src).
   388   ///   node("target", trg).
   389   ///   attribute("caption", caption).
   390   ///   run();
   391   ///\endcode
   392   ///
   393   ///
   394   /// By default, the writer does not write additional captions to the
   395   /// sections, but they can be give as an optional parameter of
   396   /// the \c nodes(), \c arcs() or \c
   397   /// attributes() functions.
   398   ///
   399   /// The \c skipNodes() and \c skipArcs() functions forbid the
   400   /// writing of the sections. If two arc sections should be written
   401   /// to the output, it can be done in two passes, the first pass
   402   /// writes the node section and the first arc section, then the
   403   /// second pass skips the node section and writes just the arc
   404   /// section to the stream. The output stream can be retrieved with
   405   /// the \c ostream() function, hence the second pass can append its
   406   /// output to the output of the first pass.
   407   template <typename DGR>
   408   class DigraphWriter {
   409   public:
   410 
   411     typedef DGR Digraph;
   412     TEMPLATE_DIGRAPH_TYPEDEFS(DGR);
   413 
   414   private:
   415 
   416 
   417     std::ostream* _os;
   418     bool local_os;
   419 
   420     const DGR& _digraph;
   421 
   422     std::string _nodes_caption;
   423     std::string _arcs_caption;
   424     std::string _attributes_caption;
   425 
   426     typedef std::map<Node, std::string> NodeIndex;
   427     NodeIndex _node_index;
   428     typedef std::map<Arc, std::string> ArcIndex;
   429     ArcIndex _arc_index;
   430 
   431     typedef std::vector<std::pair<std::string,
   432       _writer_bits::MapStorageBase<Node>* > > NodeMaps;
   433     NodeMaps _node_maps;
   434 
   435     typedef std::vector<std::pair<std::string,
   436       _writer_bits::MapStorageBase<Arc>* > >ArcMaps;
   437     ArcMaps _arc_maps;
   438 
   439     typedef std::vector<std::pair<std::string,
   440       _writer_bits::ValueStorageBase*> > Attributes;
   441     Attributes _attributes;
   442 
   443     bool _skip_nodes;
   444     bool _skip_arcs;
   445 
   446   public:
   447 
   448     /// \brief Constructor
   449     ///
   450     /// Construct a directed graph writer, which writes to the given
   451     /// output stream.
   452     DigraphWriter(const DGR& digraph, std::ostream& os = std::cout)
   453       : _os(&os), local_os(false), _digraph(digraph),
   454         _skip_nodes(false), _skip_arcs(false) {}
   455 
   456     /// \brief Constructor
   457     ///
   458     /// Construct a directed graph writer, which writes to the given
   459     /// output file.
   460     DigraphWriter(const DGR& digraph, const std::string& fn)
   461       : _os(new std::ofstream(fn.c_str())), local_os(true), _digraph(digraph),
   462         _skip_nodes(false), _skip_arcs(false) {
   463       if (!(*_os)) {
   464         delete _os;
   465         throw IoError("Cannot write file", fn);
   466       }
   467     }
   468 
   469     /// \brief Constructor
   470     ///
   471     /// Construct a directed graph writer, which writes to the given
   472     /// output file.
   473     DigraphWriter(const DGR& digraph, const char* fn)
   474       : _os(new std::ofstream(fn)), local_os(true), _digraph(digraph),
   475         _skip_nodes(false), _skip_arcs(false) {
   476       if (!(*_os)) {
   477         delete _os;
   478         throw IoError("Cannot write file", fn);
   479       }
   480     }
   481 
   482     /// \brief Destructor
   483     ~DigraphWriter() {
   484       for (typename NodeMaps::iterator it = _node_maps.begin();
   485            it != _node_maps.end(); ++it) {
   486         delete it->second;
   487       }
   488 
   489       for (typename ArcMaps::iterator it = _arc_maps.begin();
   490            it != _arc_maps.end(); ++it) {
   491         delete it->second;
   492       }
   493 
   494       for (typename Attributes::iterator it = _attributes.begin();
   495            it != _attributes.end(); ++it) {
   496         delete it->second;
   497       }
   498 
   499       if (local_os) {
   500         delete _os;
   501       }
   502     }
   503 
   504   private:
   505 
   506     template <typename TDGR>
   507     friend DigraphWriter<TDGR> digraphWriter(const TDGR& digraph,
   508                                              std::ostream& os);
   509     template <typename TDGR>
   510     friend DigraphWriter<TDGR> digraphWriter(const TDGR& digraph,
   511                                              const std::string& fn);
   512     template <typename TDGR>
   513     friend DigraphWriter<TDGR> digraphWriter(const TDGR& digraph,
   514                                              const char *fn);
   515 
   516     DigraphWriter(DigraphWriter& other)
   517       : _os(other._os), local_os(other.local_os), _digraph(other._digraph),
   518         _skip_nodes(other._skip_nodes), _skip_arcs(other._skip_arcs) {
   519 
   520       other._os = 0;
   521       other.local_os = false;
   522 
   523       _node_index.swap(other._node_index);
   524       _arc_index.swap(other._arc_index);
   525 
   526       _node_maps.swap(other._node_maps);
   527       _arc_maps.swap(other._arc_maps);
   528       _attributes.swap(other._attributes);
   529 
   530       _nodes_caption = other._nodes_caption;
   531       _arcs_caption = other._arcs_caption;
   532       _attributes_caption = other._attributes_caption;
   533     }
   534 
   535     DigraphWriter& operator=(const DigraphWriter&);
   536 
   537   public:
   538 
   539     /// \name Writing Rules
   540     /// @{
   541 
   542     /// \brief Node map writing rule
   543     ///
   544     /// Add a node map writing rule to the writer.
   545     template <typename Map>
   546     DigraphWriter& nodeMap(const std::string& caption, const Map& map) {
   547       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
   548       _writer_bits::MapStorageBase<Node>* storage =
   549         new _writer_bits::MapStorage<Node, Map>(map);
   550       _node_maps.push_back(std::make_pair(caption, storage));
   551       return *this;
   552     }
   553 
   554     /// \brief Node map writing rule
   555     ///
   556     /// Add a node map writing rule with specialized converter to the
   557     /// writer.
   558     template <typename Map, typename Converter>
   559     DigraphWriter& nodeMap(const std::string& caption, const Map& map,
   560                            const Converter& converter = Converter()) {
   561       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
   562       _writer_bits::MapStorageBase<Node>* storage =
   563         new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
   564       _node_maps.push_back(std::make_pair(caption, storage));
   565       return *this;
   566     }
   567 
   568     /// \brief Arc map writing rule
   569     ///
   570     /// Add an arc map writing rule to the writer.
   571     template <typename Map>
   572     DigraphWriter& arcMap(const std::string& caption, const Map& map) {
   573       checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
   574       _writer_bits::MapStorageBase<Arc>* storage =
   575         new _writer_bits::MapStorage<Arc, Map>(map);
   576       _arc_maps.push_back(std::make_pair(caption, storage));
   577       return *this;
   578     }
   579 
   580     /// \brief Arc map writing rule
   581     ///
   582     /// Add an arc map writing rule with specialized converter to the
   583     /// writer.
   584     template <typename Map, typename Converter>
   585     DigraphWriter& arcMap(const std::string& caption, const Map& map,
   586                           const Converter& converter = Converter()) {
   587       checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
   588       _writer_bits::MapStorageBase<Arc>* storage =
   589         new _writer_bits::MapStorage<Arc, Map, Converter>(map, converter);
   590       _arc_maps.push_back(std::make_pair(caption, storage));
   591       return *this;
   592     }
   593 
   594     /// \brief Attribute writing rule
   595     ///
   596     /// Add an attribute writing rule to the writer.
   597     template <typename Value>
   598     DigraphWriter& attribute(const std::string& caption, const Value& value) {
   599       _writer_bits::ValueStorageBase* storage =
   600         new _writer_bits::ValueStorage<Value>(value);
   601       _attributes.push_back(std::make_pair(caption, storage));
   602       return *this;
   603     }
   604 
   605     /// \brief Attribute writing rule
   606     ///
   607     /// Add an attribute writing rule with specialized converter to the
   608     /// writer.
   609     template <typename Value, typename Converter>
   610     DigraphWriter& attribute(const std::string& caption, const Value& value,
   611                              const Converter& converter = Converter()) {
   612       _writer_bits::ValueStorageBase* storage =
   613         new _writer_bits::ValueStorage<Value, Converter>(value, converter);
   614       _attributes.push_back(std::make_pair(caption, storage));
   615       return *this;
   616     }
   617 
   618     /// \brief Node writing rule
   619     ///
   620     /// Add a node writing rule to the writer.
   621     DigraphWriter& node(const std::string& caption, const Node& node) {
   622       typedef _writer_bits::MapLookUpConverter<Node> Converter;
   623       Converter converter(_node_index);
   624       _writer_bits::ValueStorageBase* storage =
   625         new _writer_bits::ValueStorage<Node, Converter>(node, converter);
   626       _attributes.push_back(std::make_pair(caption, storage));
   627       return *this;
   628     }
   629 
   630     /// \brief Arc writing rule
   631     ///
   632     /// Add an arc writing rule to writer.
   633     DigraphWriter& arc(const std::string& caption, const Arc& arc) {
   634       typedef _writer_bits::MapLookUpConverter<Arc> Converter;
   635       Converter converter(_arc_index);
   636       _writer_bits::ValueStorageBase* storage =
   637         new _writer_bits::ValueStorage<Arc, Converter>(arc, converter);
   638       _attributes.push_back(std::make_pair(caption, storage));
   639       return *this;
   640     }
   641 
   642     /// \name Section Captions
   643     /// @{
   644 
   645     /// \brief Add an additional caption to the \c \@nodes section
   646     ///
   647     /// Add an additional caption to the \c \@nodes section.
   648     DigraphWriter& nodes(const std::string& caption) {
   649       _nodes_caption = caption;
   650       return *this;
   651     }
   652 
   653     /// \brief Add an additional caption to the \c \@arcs section
   654     ///
   655     /// Add an additional caption to the \c \@arcs section.
   656     DigraphWriter& arcs(const std::string& caption) {
   657       _arcs_caption = caption;
   658       return *this;
   659     }
   660 
   661     /// \brief Add an additional caption to the \c \@attributes section
   662     ///
   663     /// Add an additional caption to the \c \@attributes section.
   664     DigraphWriter& attributes(const std::string& caption) {
   665       _attributes_caption = caption;
   666       return *this;
   667     }
   668 
   669     /// \name Skipping Section
   670     /// @{
   671 
   672     /// \brief Skip writing the node set
   673     ///
   674     /// The \c \@nodes section will not be written to the stream.
   675     DigraphWriter& skipNodes() {
   676       LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member");
   677       _skip_nodes = true;
   678       return *this;
   679     }
   680 
   681     /// \brief Skip writing arc set
   682     ///
   683     /// The \c \@arcs section will not be written to the stream.
   684     DigraphWriter& skipArcs() {
   685       LEMON_ASSERT(!_skip_arcs, "Multiple usage of skipArcs() member");
   686       _skip_arcs = true;
   687       return *this;
   688     }
   689 
   690     /// @}
   691 
   692   private:
   693 
   694     void writeNodes() {
   695       _writer_bits::MapStorageBase<Node>* label = 0;
   696       for (typename NodeMaps::iterator it = _node_maps.begin();
   697            it != _node_maps.end(); ++it) {
   698         if (it->first == "label") {
   699           label = it->second;
   700           break;
   701         }
   702       }
   703 
   704       *_os << "@nodes";
   705       if (!_nodes_caption.empty()) {
   706         _writer_bits::writeToken(*_os << ' ', _nodes_caption);
   707       }
   708       *_os << std::endl;
   709 
   710       if (label == 0) {
   711         *_os << "label" << '\t';
   712       }
   713       for (typename NodeMaps::iterator it = _node_maps.begin();
   714            it != _node_maps.end(); ++it) {
   715         _writer_bits::writeToken(*_os, it->first) << '\t';
   716       }
   717       *_os << std::endl;
   718 
   719       std::vector<Node> nodes;
   720       for (NodeIt n(_digraph); n != INVALID; ++n) {
   721         nodes.push_back(n);
   722       }
   723 
   724       if (label == 0) {
   725         IdMap<DGR, Node> id_map(_digraph);
   726         _writer_bits::MapLess<IdMap<DGR, Node> > id_less(id_map);
   727         std::sort(nodes.begin(), nodes.end(), id_less);
   728       } else {
   729         label->sort(nodes);
   730       }
   731 
   732       for (int i = 0; i < static_cast<int>(nodes.size()); ++i) {
   733         Node n = nodes[i];
   734         if (label == 0) {
   735           std::ostringstream os;
   736           os << _digraph.id(n);
   737           _writer_bits::writeToken(*_os, os.str());
   738           *_os << '\t';
   739           _node_index.insert(std::make_pair(n, os.str()));
   740         }
   741         for (typename NodeMaps::iterator it = _node_maps.begin();
   742              it != _node_maps.end(); ++it) {
   743           std::string value = it->second->get(n);
   744           _writer_bits::writeToken(*_os, value);
   745           if (it->first == "label") {
   746             _node_index.insert(std::make_pair(n, value));
   747           }
   748           *_os << '\t';
   749         }
   750         *_os << std::endl;
   751       }
   752     }
   753 
   754     void createNodeIndex() {
   755       _writer_bits::MapStorageBase<Node>* label = 0;
   756       for (typename NodeMaps::iterator it = _node_maps.begin();
   757            it != _node_maps.end(); ++it) {
   758         if (it->first == "label") {
   759           label = it->second;
   760           break;
   761         }
   762       }
   763 
   764       if (label == 0) {
   765         for (NodeIt n(_digraph); n != INVALID; ++n) {
   766           std::ostringstream os;
   767           os << _digraph.id(n);
   768           _node_index.insert(std::make_pair(n, os.str()));
   769         }
   770       } else {
   771         for (NodeIt n(_digraph); n != INVALID; ++n) {
   772           std::string value = label->get(n);
   773           _node_index.insert(std::make_pair(n, value));
   774         }
   775       }
   776     }
   777 
   778     void writeArcs() {
   779       _writer_bits::MapStorageBase<Arc>* label = 0;
   780       for (typename ArcMaps::iterator it = _arc_maps.begin();
   781            it != _arc_maps.end(); ++it) {
   782         if (it->first == "label") {
   783           label = it->second;
   784           break;
   785         }
   786       }
   787 
   788       *_os << "@arcs";
   789       if (!_arcs_caption.empty()) {
   790         _writer_bits::writeToken(*_os << ' ', _arcs_caption);
   791       }
   792       *_os << std::endl;
   793 
   794       *_os << '\t' << '\t';
   795       if (label == 0) {
   796         *_os << "label" << '\t';
   797       }
   798       for (typename ArcMaps::iterator it = _arc_maps.begin();
   799            it != _arc_maps.end(); ++it) {
   800         _writer_bits::writeToken(*_os, it->first) << '\t';
   801       }
   802       *_os << std::endl;
   803 
   804       std::vector<Arc> arcs;
   805       for (ArcIt n(_digraph); n != INVALID; ++n) {
   806         arcs.push_back(n);
   807       }
   808 
   809       if (label == 0) {
   810         IdMap<DGR, Arc> id_map(_digraph);
   811         _writer_bits::MapLess<IdMap<DGR, Arc> > id_less(id_map);
   812         std::sort(arcs.begin(), arcs.end(), id_less);
   813       } else {
   814         label->sort(arcs);
   815       }
   816 
   817       for (int i = 0; i < static_cast<int>(arcs.size()); ++i) {
   818         Arc a = arcs[i];
   819         _writer_bits::writeToken(*_os, _node_index.
   820                                  find(_digraph.source(a))->second);
   821         *_os << '\t';
   822         _writer_bits::writeToken(*_os, _node_index.
   823                                  find(_digraph.target(a))->second);
   824         *_os << '\t';
   825         if (label == 0) {
   826           std::ostringstream os;
   827           os << _digraph.id(a);
   828           _writer_bits::writeToken(*_os, os.str());
   829           *_os << '\t';
   830           _arc_index.insert(std::make_pair(a, os.str()));
   831         }
   832         for (typename ArcMaps::iterator it = _arc_maps.begin();
   833              it != _arc_maps.end(); ++it) {
   834           std::string value = it->second->get(a);
   835           _writer_bits::writeToken(*_os, value);
   836           if (it->first == "label") {
   837             _arc_index.insert(std::make_pair(a, value));
   838           }
   839           *_os << '\t';
   840         }
   841         *_os << std::endl;
   842       }
   843     }
   844 
   845     void createArcIndex() {
   846       _writer_bits::MapStorageBase<Arc>* label = 0;
   847       for (typename ArcMaps::iterator it = _arc_maps.begin();
   848            it != _arc_maps.end(); ++it) {
   849         if (it->first == "label") {
   850           label = it->second;
   851           break;
   852         }
   853       }
   854 
   855       if (label == 0) {
   856         for (ArcIt a(_digraph); a != INVALID; ++a) {
   857           std::ostringstream os;
   858           os << _digraph.id(a);
   859           _arc_index.insert(std::make_pair(a, os.str()));
   860         }
   861       } else {
   862         for (ArcIt a(_digraph); a != INVALID; ++a) {
   863           std::string value = label->get(a);
   864           _arc_index.insert(std::make_pair(a, value));
   865         }
   866       }
   867     }
   868 
   869     void writeAttributes() {
   870       if (_attributes.empty()) return;
   871       *_os << "@attributes";
   872       if (!_attributes_caption.empty()) {
   873         _writer_bits::writeToken(*_os << ' ', _attributes_caption);
   874       }
   875       *_os << std::endl;
   876       for (typename Attributes::iterator it = _attributes.begin();
   877            it != _attributes.end(); ++it) {
   878         _writer_bits::writeToken(*_os, it->first) << ' ';
   879         _writer_bits::writeToken(*_os, it->second->get());
   880         *_os << std::endl;
   881       }
   882     }
   883 
   884   public:
   885 
   886     /// \name Execution of the Writer
   887     /// @{
   888 
   889     /// \brief Start the batch processing
   890     ///
   891     /// This function starts the batch processing.
   892     void run() {
   893       if (!_skip_nodes) {
   894         writeNodes();
   895       } else {
   896         createNodeIndex();
   897       }
   898       if (!_skip_arcs) {
   899         writeArcs();
   900       } else {
   901         createArcIndex();
   902       }
   903       writeAttributes();
   904     }
   905 
   906     /// \brief Give back the stream of the writer
   907     ///
   908     /// Give back the stream of the writer.
   909     std::ostream& ostream() {
   910       return *_os;
   911     }
   912 
   913     /// @}
   914   };
   915 
   916   /// \ingroup lemon_io
   917   ///
   918   /// \brief Return a \ref DigraphWriter class
   919   ///
   920   /// This function just returns a \ref DigraphWriter class.
   921   ///
   922   /// With this function a digraph can be write to a file or output
   923   /// stream in \ref lgf-format "LGF" format with several maps and
   924   /// attributes. For example, with the following code a network flow
   925   /// problem can be written to the standard output, i.e. a digraph
   926   /// with a \e capacity map on the arcs and \e source and \e target
   927   /// nodes:
   928   ///
   929   ///\code
   930   ///ListDigraph digraph;
   931   ///ListDigraph::ArcMap<int> cap(digraph);
   932   ///ListDigraph::Node src, trg;
   933   ///  // Setting the capacity map and source and target nodes
   934   ///digraphWriter(digraph, std::cout).
   935   ///  arcMap("capacity", cap).
   936   ///  node("source", src).
   937   ///  node("target", trg).
   938   ///  run();
   939   ///\endcode
   940   ///
   941   /// For a complete documentation, please see the \ref DigraphWriter
   942   /// class documentation.
   943   /// \warning Don't forget to put the \ref DigraphWriter::run() "run()"
   944   /// to the end of the parameter list.
   945   /// \relates DigraphWriter
   946   /// \sa digraphWriter(const TDGR& digraph, const std::string& fn)
   947   /// \sa digraphWriter(const TDGR& digraph, const char* fn)
   948   template <typename TDGR>
   949   DigraphWriter<TDGR> digraphWriter(const TDGR& digraph, std::ostream& os) {
   950     DigraphWriter<TDGR> tmp(digraph, os);
   951     return tmp;
   952   }
   953 
   954   /// \brief Return a \ref DigraphWriter class
   955   ///
   956   /// This function just returns a \ref DigraphWriter class.
   957   /// \relates DigraphWriter
   958   /// \sa digraphWriter(const TDGR& digraph, std::ostream& os)
   959   template <typename TDGR>
   960   DigraphWriter<TDGR> digraphWriter(const TDGR& digraph,
   961                                     const std::string& fn) {
   962     DigraphWriter<TDGR> tmp(digraph, fn);
   963     return tmp;
   964   }
   965 
   966   /// \brief Return a \ref DigraphWriter class
   967   ///
   968   /// This function just returns a \ref DigraphWriter class.
   969   /// \relates DigraphWriter
   970   /// \sa digraphWriter(const TDGR& digraph, std::ostream& os)
   971   template <typename TDGR>
   972   DigraphWriter<TDGR> digraphWriter(const TDGR& digraph, const char* fn) {
   973     DigraphWriter<TDGR> tmp(digraph, fn);
   974     return tmp;
   975   }
   976 
   977   template <typename GR>
   978   class GraphWriter;
   979 
   980   template <typename TGR>
   981   GraphWriter<TGR> graphWriter(const TGR& graph, std::ostream& os = std::cout);
   982   template <typename TGR>
   983   GraphWriter<TGR> graphWriter(const TGR& graph, const std::string& fn);
   984   template <typename TGR>
   985   GraphWriter<TGR> graphWriter(const TGR& graph, const char* fn);
   986 
   987   /// \ingroup lemon_io
   988   ///
   989   /// \brief \ref lgf-format "LGF" writer for undirected graphs
   990   ///
   991   /// This utility writes an \ref lgf-format "LGF" file.
   992   ///
   993   /// It can be used almost the same way as \c DigraphWriter.
   994   /// The only difference is that this class can handle edges and
   995   /// edge maps as well as arcs and arc maps.
   996   ///
   997   /// The arc maps are written into the file as two columns, the
   998   /// caption of the columns are the name of the map prefixed with \c
   999   /// '+' and \c '-'. The arcs are written into the \c \@attributes
  1000   /// section as a \c '+' or a \c '-' prefix (depends on the direction
  1001   /// of the arc) and the label of corresponding edge.
  1002   template <typename GR>
  1003   class GraphWriter {
  1004   public:
  1005 
  1006     typedef GR Graph;
  1007     TEMPLATE_GRAPH_TYPEDEFS(GR);
  1008 
  1009   private:
  1010 
  1011 
  1012     std::ostream* _os;
  1013     bool local_os;
  1014 
  1015     const GR& _graph;
  1016 
  1017     std::string _nodes_caption;
  1018     std::string _edges_caption;
  1019     std::string _attributes_caption;
  1020 
  1021     typedef std::map<Node, std::string> NodeIndex;
  1022     NodeIndex _node_index;
  1023     typedef std::map<Edge, std::string> EdgeIndex;
  1024     EdgeIndex _edge_index;
  1025 
  1026     typedef std::vector<std::pair<std::string,
  1027       _writer_bits::MapStorageBase<Node>* > > NodeMaps;
  1028     NodeMaps _node_maps;
  1029 
  1030     typedef std::vector<std::pair<std::string,
  1031       _writer_bits::MapStorageBase<Edge>* > >EdgeMaps;
  1032     EdgeMaps _edge_maps;
  1033 
  1034     typedef std::vector<std::pair<std::string,
  1035       _writer_bits::ValueStorageBase*> > Attributes;
  1036     Attributes _attributes;
  1037 
  1038     bool _skip_nodes;
  1039     bool _skip_edges;
  1040 
  1041   public:
  1042 
  1043     /// \brief Constructor
  1044     ///
  1045     /// Construct an undirected graph writer, which writes to the
  1046     /// given output stream.
  1047     GraphWriter(const GR& graph, std::ostream& os = std::cout)
  1048       : _os(&os), local_os(false), _graph(graph),
  1049         _skip_nodes(false), _skip_edges(false) {}
  1050 
  1051     /// \brief Constructor
  1052     ///
  1053     /// Construct a undirected graph writer, which writes to the given
  1054     /// output file.
  1055     GraphWriter(const GR& graph, const std::string& fn)
  1056       : _os(new std::ofstream(fn.c_str())), local_os(true), _graph(graph),
  1057         _skip_nodes(false), _skip_edges(false) {
  1058       if (!(*_os)) {
  1059         delete _os;
  1060         throw IoError("Cannot write file", fn);
  1061       }
  1062     }
  1063 
  1064     /// \brief Constructor
  1065     ///
  1066     /// Construct a undirected graph writer, which writes to the given
  1067     /// output file.
  1068     GraphWriter(const GR& graph, const char* fn)
  1069       : _os(new std::ofstream(fn)), local_os(true), _graph(graph),
  1070         _skip_nodes(false), _skip_edges(false) {
  1071       if (!(*_os)) {
  1072         delete _os;
  1073         throw IoError("Cannot write file", fn);
  1074       }
  1075     }
  1076 
  1077     /// \brief Destructor
  1078     ~GraphWriter() {
  1079       for (typename NodeMaps::iterator it = _node_maps.begin();
  1080            it != _node_maps.end(); ++it) {
  1081         delete it->second;
  1082       }
  1083 
  1084       for (typename EdgeMaps::iterator it = _edge_maps.begin();
  1085            it != _edge_maps.end(); ++it) {
  1086         delete it->second;
  1087       }
  1088 
  1089       for (typename Attributes::iterator it = _attributes.begin();
  1090            it != _attributes.end(); ++it) {
  1091         delete it->second;
  1092       }
  1093 
  1094       if (local_os) {
  1095         delete _os;
  1096       }
  1097     }
  1098 
  1099   private:
  1100 
  1101     template <typename TGR>
  1102     friend GraphWriter<TGR> graphWriter(const TGR& graph, std::ostream& os);
  1103     template <typename TGR>
  1104     friend GraphWriter<TGR> graphWriter(const TGR& graph,
  1105                                         const std::string& fn);
  1106     template <typename TGR>
  1107     friend GraphWriter<TGR> graphWriter(const TGR& graph, const char *fn);
  1108 
  1109     GraphWriter(GraphWriter& other)
  1110       : _os(other._os), local_os(other.local_os), _graph(other._graph),
  1111         _skip_nodes(other._skip_nodes), _skip_edges(other._skip_edges) {
  1112 
  1113       other._os = 0;
  1114       other.local_os = false;
  1115 
  1116       _node_index.swap(other._node_index);
  1117       _edge_index.swap(other._edge_index);
  1118 
  1119       _node_maps.swap(other._node_maps);
  1120       _edge_maps.swap(other._edge_maps);
  1121       _attributes.swap(other._attributes);
  1122 
  1123       _nodes_caption = other._nodes_caption;
  1124       _edges_caption = other._edges_caption;
  1125       _attributes_caption = other._attributes_caption;
  1126     }
  1127 
  1128     GraphWriter& operator=(const GraphWriter&);
  1129 
  1130   public:
  1131 
  1132     /// \name Writing Rules
  1133     /// @{
  1134 
  1135     /// \brief Node map writing rule
  1136     ///
  1137     /// Add a node map writing rule to the writer.
  1138     template <typename Map>
  1139     GraphWriter& nodeMap(const std::string& caption, const Map& map) {
  1140       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
  1141       _writer_bits::MapStorageBase<Node>* storage =
  1142         new _writer_bits::MapStorage<Node, Map>(map);
  1143       _node_maps.push_back(std::make_pair(caption, storage));
  1144       return *this;
  1145     }
  1146 
  1147     /// \brief Node map writing rule
  1148     ///
  1149     /// Add a node map writing rule with specialized converter to the
  1150     /// writer.
  1151     template <typename Map, typename Converter>
  1152     GraphWriter& nodeMap(const std::string& caption, const Map& map,
  1153                            const Converter& converter = Converter()) {
  1154       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
  1155       _writer_bits::MapStorageBase<Node>* storage =
  1156         new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
  1157       _node_maps.push_back(std::make_pair(caption, storage));
  1158       return *this;
  1159     }
  1160 
  1161     /// \brief Edge map writing rule
  1162     ///
  1163     /// Add an edge map writing rule to the writer.
  1164     template <typename Map>
  1165     GraphWriter& edgeMap(const std::string& caption, const Map& map) {
  1166       checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
  1167       _writer_bits::MapStorageBase<Edge>* storage =
  1168         new _writer_bits::MapStorage<Edge, Map>(map);
  1169       _edge_maps.push_back(std::make_pair(caption, storage));
  1170       return *this;
  1171     }
  1172 
  1173     /// \brief Edge map writing rule
  1174     ///
  1175     /// Add an edge map writing rule with specialized converter to the
  1176     /// writer.
  1177     template <typename Map, typename Converter>
  1178     GraphWriter& edgeMap(const std::string& caption, const Map& map,
  1179                           const Converter& converter = Converter()) {
  1180       checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
  1181       _writer_bits::MapStorageBase<Edge>* storage =
  1182         new _writer_bits::MapStorage<Edge, Map, Converter>(map, converter);
  1183       _edge_maps.push_back(std::make_pair(caption, storage));
  1184       return *this;
  1185     }
  1186 
  1187     /// \brief Arc map writing rule
  1188     ///
  1189     /// Add an arc map writing rule to the writer.
  1190     template <typename Map>
  1191     GraphWriter& arcMap(const std::string& caption, const Map& map) {
  1192       checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
  1193       _writer_bits::MapStorageBase<Edge>* forward_storage =
  1194         new _writer_bits::GraphArcMapStorage<GR, true, Map>(_graph, map);
  1195       _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
  1196       _writer_bits::MapStorageBase<Edge>* backward_storage =
  1197         new _writer_bits::GraphArcMapStorage<GR, false, Map>(_graph, map);
  1198       _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
  1199       return *this;
  1200     }
  1201 
  1202     /// \brief Arc map writing rule
  1203     ///
  1204     /// Add an arc map writing rule with specialized converter to the
  1205     /// writer.
  1206     template <typename Map, typename Converter>
  1207     GraphWriter& arcMap(const std::string& caption, const Map& map,
  1208                           const Converter& converter = Converter()) {
  1209       checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
  1210       _writer_bits::MapStorageBase<Edge>* forward_storage =
  1211         new _writer_bits::GraphArcMapStorage<GR, true, Map, Converter>
  1212         (_graph, map, converter);
  1213       _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
  1214       _writer_bits::MapStorageBase<Edge>* backward_storage =
  1215         new _writer_bits::GraphArcMapStorage<GR, false, Map, Converter>
  1216         (_graph, map, converter);
  1217       _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
  1218       return *this;
  1219     }
  1220 
  1221     /// \brief Attribute writing rule
  1222     ///
  1223     /// Add an attribute writing rule to the writer.
  1224     template <typename Value>
  1225     GraphWriter& attribute(const std::string& caption, const Value& value) {
  1226       _writer_bits::ValueStorageBase* storage =
  1227         new _writer_bits::ValueStorage<Value>(value);
  1228       _attributes.push_back(std::make_pair(caption, storage));
  1229       return *this;
  1230     }
  1231 
  1232     /// \brief Attribute writing rule
  1233     ///
  1234     /// Add an attribute writing rule with specialized converter to the
  1235     /// writer.
  1236     template <typename Value, typename Converter>
  1237     GraphWriter& attribute(const std::string& caption, const Value& value,
  1238                              const Converter& converter = Converter()) {
  1239       _writer_bits::ValueStorageBase* storage =
  1240         new _writer_bits::ValueStorage<Value, Converter>(value, converter);
  1241       _attributes.push_back(std::make_pair(caption, storage));
  1242       return *this;
  1243     }
  1244 
  1245     /// \brief Node writing rule
  1246     ///
  1247     /// Add a node writing rule to the writer.
  1248     GraphWriter& node(const std::string& caption, const Node& node) {
  1249       typedef _writer_bits::MapLookUpConverter<Node> Converter;
  1250       Converter converter(_node_index);
  1251       _writer_bits::ValueStorageBase* storage =
  1252         new _writer_bits::ValueStorage<Node, Converter>(node, converter);
  1253       _attributes.push_back(std::make_pair(caption, storage));
  1254       return *this;
  1255     }
  1256 
  1257     /// \brief Edge writing rule
  1258     ///
  1259     /// Add an edge writing rule to writer.
  1260     GraphWriter& edge(const std::string& caption, const Edge& edge) {
  1261       typedef _writer_bits::MapLookUpConverter<Edge> Converter;
  1262       Converter converter(_edge_index);
  1263       _writer_bits::ValueStorageBase* storage =
  1264         new _writer_bits::ValueStorage<Edge, Converter>(edge, converter);
  1265       _attributes.push_back(std::make_pair(caption, storage));
  1266       return *this;
  1267     }
  1268 
  1269     /// \brief Arc writing rule
  1270     ///
  1271     /// Add an arc writing rule to writer.
  1272     GraphWriter& arc(const std::string& caption, const Arc& arc) {
  1273       typedef _writer_bits::GraphArcLookUpConverter<GR> Converter;
  1274       Converter converter(_graph, _edge_index);
  1275       _writer_bits::ValueStorageBase* storage =
  1276         new _writer_bits::ValueStorage<Arc, Converter>(arc, converter);
  1277       _attributes.push_back(std::make_pair(caption, storage));
  1278       return *this;
  1279     }
  1280 
  1281     /// \name Section Captions
  1282     /// @{
  1283 
  1284     /// \brief Add an additional caption to the \c \@nodes section
  1285     ///
  1286     /// Add an additional caption to the \c \@nodes section.
  1287     GraphWriter& nodes(const std::string& caption) {
  1288       _nodes_caption = caption;
  1289       return *this;
  1290     }
  1291 
  1292     /// \brief Add an additional caption to the \c \@edges section
  1293     ///
  1294     /// Add an additional caption to the \c \@edges section.
  1295     GraphWriter& edges(const std::string& caption) {
  1296       _edges_caption = caption;
  1297       return *this;
  1298     }
  1299 
  1300     /// \brief Add an additional caption to the \c \@attributes section
  1301     ///
  1302     /// Add an additional caption to the \c \@attributes section.
  1303     GraphWriter& attributes(const std::string& caption) {
  1304       _attributes_caption = caption;
  1305       return *this;
  1306     }
  1307 
  1308     /// \name Skipping Section
  1309     /// @{
  1310 
  1311     /// \brief Skip writing the node set
  1312     ///
  1313     /// The \c \@nodes section will not be written to the stream.
  1314     GraphWriter& skipNodes() {
  1315       LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member");
  1316       _skip_nodes = true;
  1317       return *this;
  1318     }
  1319 
  1320     /// \brief Skip writing edge set
  1321     ///
  1322     /// The \c \@edges section will not be written to the stream.
  1323     GraphWriter& skipEdges() {
  1324       LEMON_ASSERT(!_skip_edges, "Multiple usage of skipEdges() member");
  1325       _skip_edges = true;
  1326       return *this;
  1327     }
  1328 
  1329     /// @}
  1330 
  1331   private:
  1332 
  1333     void writeNodes() {
  1334       _writer_bits::MapStorageBase<Node>* label = 0;
  1335       for (typename NodeMaps::iterator it = _node_maps.begin();
  1336            it != _node_maps.end(); ++it) {
  1337         if (it->first == "label") {
  1338           label = it->second;
  1339           break;
  1340         }
  1341       }
  1342 
  1343       *_os << "@nodes";
  1344       if (!_nodes_caption.empty()) {
  1345         _writer_bits::writeToken(*_os << ' ', _nodes_caption);
  1346       }
  1347       *_os << std::endl;
  1348 
  1349       if (label == 0) {
  1350         *_os << "label" << '\t';
  1351       }
  1352       for (typename NodeMaps::iterator it = _node_maps.begin();
  1353            it != _node_maps.end(); ++it) {
  1354         _writer_bits::writeToken(*_os, it->first) << '\t';
  1355       }
  1356       *_os << std::endl;
  1357 
  1358       std::vector<Node> nodes;
  1359       for (NodeIt n(_graph); n != INVALID; ++n) {
  1360         nodes.push_back(n);
  1361       }
  1362 
  1363       if (label == 0) {
  1364         IdMap<GR, Node> id_map(_graph);
  1365         _writer_bits::MapLess<IdMap<GR, Node> > id_less(id_map);
  1366         std::sort(nodes.begin(), nodes.end(), id_less);
  1367       } else {
  1368         label->sort(nodes);
  1369       }
  1370 
  1371       for (int i = 0; i < static_cast<int>(nodes.size()); ++i) {
  1372         Node n = nodes[i];
  1373         if (label == 0) {
  1374           std::ostringstream os;
  1375           os << _graph.id(n);
  1376           _writer_bits::writeToken(*_os, os.str());
  1377           *_os << '\t';
  1378           _node_index.insert(std::make_pair(n, os.str()));
  1379         }
  1380         for (typename NodeMaps::iterator it = _node_maps.begin();
  1381              it != _node_maps.end(); ++it) {
  1382           std::string value = it->second->get(n);
  1383           _writer_bits::writeToken(*_os, value);
  1384           if (it->first == "label") {
  1385             _node_index.insert(std::make_pair(n, value));
  1386           }
  1387           *_os << '\t';
  1388         }
  1389         *_os << std::endl;
  1390       }
  1391     }
  1392 
  1393     void createNodeIndex() {
  1394       _writer_bits::MapStorageBase<Node>* label = 0;
  1395       for (typename NodeMaps::iterator it = _node_maps.begin();
  1396            it != _node_maps.end(); ++it) {
  1397         if (it->first == "label") {
  1398           label = it->second;
  1399           break;
  1400         }
  1401       }
  1402 
  1403       if (label == 0) {
  1404         for (NodeIt n(_graph); n != INVALID; ++n) {
  1405           std::ostringstream os;
  1406           os << _graph.id(n);
  1407           _node_index.insert(std::make_pair(n, os.str()));
  1408         }
  1409       } else {
  1410         for (NodeIt n(_graph); n != INVALID; ++n) {
  1411           std::string value = label->get(n);
  1412           _node_index.insert(std::make_pair(n, value));
  1413         }
  1414       }
  1415     }
  1416 
  1417     void writeEdges() {
  1418       _writer_bits::MapStorageBase<Edge>* label = 0;
  1419       for (typename EdgeMaps::iterator it = _edge_maps.begin();
  1420            it != _edge_maps.end(); ++it) {
  1421         if (it->first == "label") {
  1422           label = it->second;
  1423           break;
  1424         }
  1425       }
  1426 
  1427       *_os << "@edges";
  1428       if (!_edges_caption.empty()) {
  1429         _writer_bits::writeToken(*_os << ' ', _edges_caption);
  1430       }
  1431       *_os << std::endl;
  1432 
  1433       *_os << '\t' << '\t';
  1434       if (label == 0) {
  1435         *_os << "label" << '\t';
  1436       }
  1437       for (typename EdgeMaps::iterator it = _edge_maps.begin();
  1438            it != _edge_maps.end(); ++it) {
  1439         _writer_bits::writeToken(*_os, it->first) << '\t';
  1440       }
  1441       *_os << std::endl;
  1442 
  1443       std::vector<Edge> edges;
  1444       for (EdgeIt n(_graph); n != INVALID; ++n) {
  1445         edges.push_back(n);
  1446       }
  1447 
  1448       if (label == 0) {
  1449         IdMap<GR, Edge> id_map(_graph);
  1450         _writer_bits::MapLess<IdMap<GR, Edge> > id_less(id_map);
  1451         std::sort(edges.begin(), edges.end(), id_less);
  1452       } else {
  1453         label->sort(edges);
  1454       }
  1455 
  1456       for (int i = 0; i < static_cast<int>(edges.size()); ++i) {
  1457         Edge e = edges[i];
  1458         _writer_bits::writeToken(*_os, _node_index.
  1459                                  find(_graph.u(e))->second);
  1460         *_os << '\t';
  1461         _writer_bits::writeToken(*_os, _node_index.
  1462                                  find(_graph.v(e))->second);
  1463         *_os << '\t';
  1464         if (label == 0) {
  1465           std::ostringstream os;
  1466           os << _graph.id(e);
  1467           _writer_bits::writeToken(*_os, os.str());
  1468           *_os << '\t';
  1469           _edge_index.insert(std::make_pair(e, os.str()));
  1470         }
  1471         for (typename EdgeMaps::iterator it = _edge_maps.begin();
  1472              it != _edge_maps.end(); ++it) {
  1473           std::string value = it->second->get(e);
  1474           _writer_bits::writeToken(*_os, value);
  1475           if (it->first == "label") {
  1476             _edge_index.insert(std::make_pair(e, value));
  1477           }
  1478           *_os << '\t';
  1479         }
  1480         *_os << std::endl;
  1481       }
  1482     }
  1483 
  1484     void createEdgeIndex() {
  1485       _writer_bits::MapStorageBase<Edge>* label = 0;
  1486       for (typename EdgeMaps::iterator it = _edge_maps.begin();
  1487            it != _edge_maps.end(); ++it) {
  1488         if (it->first == "label") {
  1489           label = it->second;
  1490           break;
  1491         }
  1492       }
  1493 
  1494       if (label == 0) {
  1495         for (EdgeIt e(_graph); e != INVALID; ++e) {
  1496           std::ostringstream os;
  1497           os << _graph.id(e);
  1498           _edge_index.insert(std::make_pair(e, os.str()));
  1499         }
  1500       } else {
  1501         for (EdgeIt e(_graph); e != INVALID; ++e) {
  1502           std::string value = label->get(e);
  1503           _edge_index.insert(std::make_pair(e, value));
  1504         }
  1505       }
  1506     }
  1507 
  1508     void writeAttributes() {
  1509       if (_attributes.empty()) return;
  1510       *_os << "@attributes";
  1511       if (!_attributes_caption.empty()) {
  1512         _writer_bits::writeToken(*_os << ' ', _attributes_caption);
  1513       }
  1514       *_os << std::endl;
  1515       for (typename Attributes::iterator it = _attributes.begin();
  1516            it != _attributes.end(); ++it) {
  1517         _writer_bits::writeToken(*_os, it->first) << ' ';
  1518         _writer_bits::writeToken(*_os, it->second->get());
  1519         *_os << std::endl;
  1520       }
  1521     }
  1522 
  1523   public:
  1524 
  1525     /// \name Execution of the Writer
  1526     /// @{
  1527 
  1528     /// \brief Start the batch processing
  1529     ///
  1530     /// This function starts the batch processing.
  1531     void run() {
  1532       if (!_skip_nodes) {
  1533         writeNodes();
  1534       } else {
  1535         createNodeIndex();
  1536       }
  1537       if (!_skip_edges) {
  1538         writeEdges();
  1539       } else {
  1540         createEdgeIndex();
  1541       }
  1542       writeAttributes();
  1543     }
  1544 
  1545     /// \brief Give back the stream of the writer
  1546     ///
  1547     /// Give back the stream of the writer
  1548     std::ostream& ostream() {
  1549       return *_os;
  1550     }
  1551 
  1552     /// @}
  1553   };
  1554 
  1555   /// \ingroup lemon_io
  1556   ///
  1557   /// \brief Return a \ref GraphWriter class
  1558   ///
  1559   /// This function just returns a \ref GraphWriter class.
  1560   ///
  1561   /// With this function a graph can be write to a file or output
  1562   /// stream in \ref lgf-format "LGF" format with several maps and
  1563   /// attributes. For example, with the following code a weighted
  1564   /// matching problem can be written to the standard output, i.e. a
  1565   /// graph with a \e weight map on the edges:
  1566   ///
  1567   ///\code
  1568   ///ListGraph graph;
  1569   ///ListGraph::EdgeMap<int> weight(graph);
  1570   ///  // Setting the weight map
  1571   ///graphWriter(graph, std::cout).
  1572   ///  edgeMap("weight", weight).
  1573   ///  run();
  1574   ///\endcode
  1575   ///
  1576   /// For a complete documentation, please see the \ref GraphWriter
  1577   /// class documentation.
  1578   /// \warning Don't forget to put the \ref GraphWriter::run() "run()"
  1579   /// to the end of the parameter list.
  1580   /// \relates GraphWriter
  1581   /// \sa graphWriter(const TGR& graph, const std::string& fn)
  1582   /// \sa graphWriter(const TGR& graph, const char* fn)
  1583   template <typename TGR>
  1584   GraphWriter<TGR> graphWriter(const TGR& graph, std::ostream& os) {
  1585     GraphWriter<TGR> tmp(graph, os);
  1586     return tmp;
  1587   }
  1588 
  1589   /// \brief Return a \ref GraphWriter class
  1590   ///
  1591   /// This function just returns a \ref GraphWriter class.
  1592   /// \relates GraphWriter
  1593   /// \sa graphWriter(const TGR& graph, std::ostream& os)
  1594   template <typename TGR>
  1595   GraphWriter<TGR> graphWriter(const TGR& graph, const std::string& fn) {
  1596     GraphWriter<TGR> tmp(graph, fn);
  1597     return tmp;
  1598   }
  1599 
  1600   /// \brief Return a \ref GraphWriter class
  1601   ///
  1602   /// This function just returns a \ref GraphWriter class.
  1603   /// \relates GraphWriter
  1604   /// \sa graphWriter(const TGR& graph, std::ostream& os)
  1605   template <typename TGR>
  1606   GraphWriter<TGR> graphWriter(const TGR& graph, const char* fn) {
  1607     GraphWriter<TGR> tmp(graph, fn);
  1608     return tmp;
  1609   }
  1610 
  1611   template <typename BGR>
  1612   class BpGraphWriter;
  1613 
  1614   template <typename TBGR>
  1615   BpGraphWriter<TBGR> bpGraphWriter(const TBGR& graph,
  1616                                     std::ostream& os = std::cout);
  1617   template <typename TBGR>
  1618   BpGraphWriter<TBGR> bpGraphWriter(const TBGR& graph, const std::string& fn);
  1619   template <typename TBGR>
  1620   BpGraphWriter<TBGR> bpGraphWriter(const TBGR& graph, const char* fn);
  1621 
  1622   /// \ingroup lemon_io
  1623   ///
  1624   /// \brief \ref lgf-format "LGF" writer for undirected bipartite graphs
  1625   ///
  1626   /// This utility writes an \ref lgf-format "LGF" file.
  1627   ///
  1628   /// It can be used almost the same way as \c GraphWriter, but it
  1629   /// reads the red and blue nodes from separate sections, and these
  1630   /// sections can contain different set of maps.
  1631   ///
  1632   /// The red and blue node maps are written to the corresponding
  1633   /// sections. The node maps are written to both of these sections
  1634   /// with the same map name.
  1635   template <typename BGR>
  1636   class BpGraphWriter {
  1637   public:
  1638 
  1639     typedef BGR BpGraph;
  1640     TEMPLATE_BPGRAPH_TYPEDEFS(BGR);
  1641 
  1642   private:
  1643 
  1644 
  1645     std::ostream* _os;
  1646     bool local_os;
  1647 
  1648     const BGR& _graph;
  1649 
  1650     std::string _nodes_caption;
  1651     std::string _edges_caption;
  1652     std::string _attributes_caption;
  1653 
  1654     typedef std::map<Node, std::string> NodeIndex;
  1655     NodeIndex _node_index;
  1656     typedef std::map<Edge, std::string> EdgeIndex;
  1657     EdgeIndex _edge_index;
  1658 
  1659     typedef std::vector<std::pair<std::string,
  1660       _writer_bits::MapStorageBase<Node>* > > NodeMaps;
  1661     NodeMaps _red_maps;
  1662     NodeMaps _blue_maps;
  1663 
  1664     typedef std::vector<std::pair<std::string,
  1665       _writer_bits::MapStorageBase<Edge>* > >EdgeMaps;
  1666     EdgeMaps _edge_maps;
  1667 
  1668     typedef std::vector<std::pair<std::string,
  1669       _writer_bits::ValueStorageBase*> > Attributes;
  1670     Attributes _attributes;
  1671 
  1672     bool _skip_nodes;
  1673     bool _skip_edges;
  1674 
  1675   public:
  1676 
  1677     /// \brief Constructor
  1678     ///
  1679     /// Construct a bipartite graph writer, which writes to the given
  1680     /// output stream.
  1681     BpGraphWriter(const BGR& graph, std::ostream& os = std::cout)
  1682       : _os(&os), local_os(false), _graph(graph),
  1683         _skip_nodes(false), _skip_edges(false) {}
  1684 
  1685     /// \brief Constructor
  1686     ///
  1687     /// Construct a bipartite graph writer, which writes to the given
  1688     /// output file.
  1689     BpGraphWriter(const BGR& graph, const std::string& fn)
  1690       : _os(new std::ofstream(fn.c_str())), local_os(true), _graph(graph),
  1691         _skip_nodes(false), _skip_edges(false) {
  1692       if (!(*_os)) {
  1693         delete _os;
  1694         throw IoError("Cannot write file", fn);
  1695       }
  1696     }
  1697 
  1698     /// \brief Constructor
  1699     ///
  1700     /// Construct a bipartite graph writer, which writes to the given
  1701     /// output file.
  1702     BpGraphWriter(const BGR& graph, const char* fn)
  1703       : _os(new std::ofstream(fn)), local_os(true), _graph(graph),
  1704         _skip_nodes(false), _skip_edges(false) {
  1705       if (!(*_os)) {
  1706         delete _os;
  1707         throw IoError("Cannot write file", fn);
  1708       }
  1709     }
  1710 
  1711     /// \brief Destructor
  1712     ~BpGraphWriter() {
  1713       for (typename NodeMaps::iterator it = _red_maps.begin();
  1714            it != _red_maps.end(); ++it) {
  1715         delete it->second;
  1716       }
  1717 
  1718       for (typename NodeMaps::iterator it = _blue_maps.begin();
  1719            it != _blue_maps.end(); ++it) {
  1720         delete it->second;
  1721       }
  1722 
  1723       for (typename EdgeMaps::iterator it = _edge_maps.begin();
  1724            it != _edge_maps.end(); ++it) {
  1725         delete it->second;
  1726       }
  1727 
  1728       for (typename Attributes::iterator it = _attributes.begin();
  1729            it != _attributes.end(); ++it) {
  1730         delete it->second;
  1731       }
  1732 
  1733       if (local_os) {
  1734         delete _os;
  1735       }
  1736     }
  1737 
  1738   private:
  1739 
  1740     template <typename TBGR>
  1741     friend BpGraphWriter<TBGR> bpGraphWriter(const TBGR& graph,
  1742                                              std::ostream& os);
  1743     template <typename TBGR>
  1744     friend BpGraphWriter<TBGR> bpGraphWriter(const TBGR& graph,
  1745                                              const std::string& fn);
  1746     template <typename TBGR>
  1747     friend BpGraphWriter<TBGR> bpGraphWriter(const TBGR& graph, const char *fn);
  1748 
  1749     BpGraphWriter(BpGraphWriter& other)
  1750       : _os(other._os), local_os(other.local_os), _graph(other._graph),
  1751         _skip_nodes(other._skip_nodes), _skip_edges(other._skip_edges) {
  1752 
  1753       other._os = 0;
  1754       other.local_os = false;
  1755 
  1756       _node_index.swap(other._node_index);
  1757       _edge_index.swap(other._edge_index);
  1758 
  1759       _red_maps.swap(other._red_maps);
  1760       _blue_maps.swap(other._blue_maps);
  1761       _edge_maps.swap(other._edge_maps);
  1762       _attributes.swap(other._attributes);
  1763 
  1764       _nodes_caption = other._nodes_caption;
  1765       _edges_caption = other._edges_caption;
  1766       _attributes_caption = other._attributes_caption;
  1767     }
  1768 
  1769     BpGraphWriter& operator=(const BpGraphWriter&);
  1770 
  1771   public:
  1772 
  1773     /// \name Writing Rules
  1774     /// @{
  1775 
  1776     /// \brief Node map writing rule
  1777     ///
  1778     /// Add a node map writing rule to the writer.
  1779     template <typename Map>
  1780     BpGraphWriter& nodeMap(const std::string& caption, const Map& map) {
  1781       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
  1782       _writer_bits::MapStorageBase<Node>* red_storage =
  1783         new _writer_bits::MapStorage<Node, Map>(map);
  1784       _red_maps.push_back(std::make_pair(caption, red_storage));
  1785       _writer_bits::MapStorageBase<Node>* blue_storage =
  1786         new _writer_bits::MapStorage<Node, Map>(map);
  1787       _blue_maps.push_back(std::make_pair(caption, blue_storage));
  1788       return *this;
  1789     }
  1790 
  1791     /// \brief Node map writing rule
  1792     ///
  1793     /// Add a node map writing rule with specialized converter to the
  1794     /// writer.
  1795     template <typename Map, typename Converter>
  1796     BpGraphWriter& nodeMap(const std::string& caption, const Map& map,
  1797                            const Converter& converter = Converter()) {
  1798       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
  1799       _writer_bits::MapStorageBase<Node>* red_storage =
  1800         new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
  1801       _red_maps.push_back(std::make_pair(caption, red_storage));
  1802       _writer_bits::MapStorageBase<Node>* blue_storage =
  1803         new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
  1804       _blue_maps.push_back(std::make_pair(caption, blue_storage));
  1805       return *this;
  1806     }
  1807 
  1808     /// \brief Red node map writing rule
  1809     ///
  1810     /// Add a red node map writing rule to the writer.
  1811     template <typename Map>
  1812     BpGraphWriter& redNodeMap(const std::string& caption, const Map& map) {
  1813       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
  1814       _writer_bits::MapStorageBase<Node>* storage =
  1815         new _writer_bits::MapStorage<Node, Map>(map);
  1816       _red_maps.push_back(std::make_pair(caption, storage));
  1817       return *this;
  1818     }
  1819 
  1820     /// \brief Red node map writing rule
  1821     ///
  1822     /// Add a red node map writing rule with specialized converter to the
  1823     /// writer.
  1824     template <typename Map, typename Converter>
  1825     BpGraphWriter& redNodeMap(const std::string& caption, const Map& map,
  1826                               const Converter& converter = Converter()) {
  1827       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
  1828       _writer_bits::MapStorageBase<Node>* storage =
  1829         new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
  1830       _red_maps.push_back(std::make_pair(caption, storage));
  1831       return *this;
  1832     }
  1833 
  1834     /// \brief Blue node map writing rule
  1835     ///
  1836     /// Add a blue node map writing rule to the writer.
  1837     template <typename Map>
  1838     BpGraphWriter& blueNodeMap(const std::string& caption, const Map& map) {
  1839       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
  1840       _writer_bits::MapStorageBase<Node>* storage =
  1841         new _writer_bits::MapStorage<Node, Map>(map);
  1842       _blue_maps.push_back(std::make_pair(caption, storage));
  1843       return *this;
  1844     }
  1845 
  1846     /// \brief Blue node map writing rule
  1847     ///
  1848     /// Add a blue node map writing rule with specialized converter to the
  1849     /// writer.
  1850     template <typename Map, typename Converter>
  1851     BpGraphWriter& blueNodeMap(const std::string& caption, const Map& map,
  1852                                const Converter& converter = Converter()) {
  1853       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
  1854       _writer_bits::MapStorageBase<Node>* storage =
  1855         new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
  1856       _blue_maps.push_back(std::make_pair(caption, storage));
  1857       return *this;
  1858     }
  1859 
  1860     /// \brief Edge map writing rule
  1861     ///
  1862     /// Add an edge map writing rule to the writer.
  1863     template <typename Map>
  1864     BpGraphWriter& edgeMap(const std::string& caption, const Map& map) {
  1865       checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
  1866       _writer_bits::MapStorageBase<Edge>* storage =
  1867         new _writer_bits::MapStorage<Edge, Map>(map);
  1868       _edge_maps.push_back(std::make_pair(caption, storage));
  1869       return *this;
  1870     }
  1871 
  1872     /// \brief Edge map writing rule
  1873     ///
  1874     /// Add an edge map writing rule with specialized converter to the
  1875     /// writer.
  1876     template <typename Map, typename Converter>
  1877     BpGraphWriter& edgeMap(const std::string& caption, const Map& map,
  1878                           const Converter& converter = Converter()) {
  1879       checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
  1880       _writer_bits::MapStorageBase<Edge>* storage =
  1881         new _writer_bits::MapStorage<Edge, Map, Converter>(map, converter);
  1882       _edge_maps.push_back(std::make_pair(caption, storage));
  1883       return *this;
  1884     }
  1885 
  1886     /// \brief Arc map writing rule
  1887     ///
  1888     /// Add an arc map writing rule to the writer.
  1889     template <typename Map>
  1890     BpGraphWriter& arcMap(const std::string& caption, const Map& map) {
  1891       checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
  1892       _writer_bits::MapStorageBase<Edge>* forward_storage =
  1893         new _writer_bits::GraphArcMapStorage<BGR, true, Map>(_graph, map);
  1894       _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
  1895       _writer_bits::MapStorageBase<Edge>* backward_storage =
  1896         new _writer_bits::GraphArcMapStorage<BGR, false, Map>(_graph, map);
  1897       _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
  1898       return *this;
  1899     }
  1900 
  1901     /// \brief Arc map writing rule
  1902     ///
  1903     /// Add an arc map writing rule with specialized converter to the
  1904     /// writer.
  1905     template <typename Map, typename Converter>
  1906     BpGraphWriter& arcMap(const std::string& caption, const Map& map,
  1907                           const Converter& converter = Converter()) {
  1908       checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
  1909       _writer_bits::MapStorageBase<Edge>* forward_storage =
  1910         new _writer_bits::GraphArcMapStorage<BGR, true, Map, Converter>
  1911         (_graph, map, converter);
  1912       _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
  1913       _writer_bits::MapStorageBase<Edge>* backward_storage =
  1914         new _writer_bits::GraphArcMapStorage<BGR, false, Map, Converter>
  1915         (_graph, map, converter);
  1916       _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
  1917       return *this;
  1918     }
  1919 
  1920     /// \brief Attribute writing rule
  1921     ///
  1922     /// Add an attribute writing rule to the writer.
  1923     template <typename Value>
  1924     BpGraphWriter& attribute(const std::string& caption, const Value& value) {
  1925       _writer_bits::ValueStorageBase* storage =
  1926         new _writer_bits::ValueStorage<Value>(value);
  1927       _attributes.push_back(std::make_pair(caption, storage));
  1928       return *this;
  1929     }
  1930 
  1931     /// \brief Attribute writing rule
  1932     ///
  1933     /// Add an attribute writing rule with specialized converter to the
  1934     /// writer.
  1935     template <typename Value, typename Converter>
  1936     BpGraphWriter& attribute(const std::string& caption, const Value& value,
  1937                              const Converter& converter = Converter()) {
  1938       _writer_bits::ValueStorageBase* storage =
  1939         new _writer_bits::ValueStorage<Value, Converter>(value, converter);
  1940       _attributes.push_back(std::make_pair(caption, storage));
  1941       return *this;
  1942     }
  1943 
  1944     /// \brief Node writing rule
  1945     ///
  1946     /// Add a node writing rule to the writer.
  1947     BpGraphWriter& node(const std::string& caption, const Node& node) {
  1948       typedef _writer_bits::MapLookUpConverter<Node> Converter;
  1949       Converter converter(_node_index);
  1950       _writer_bits::ValueStorageBase* storage =
  1951         new _writer_bits::ValueStorage<Node, Converter>(node, converter);
  1952       _attributes.push_back(std::make_pair(caption, storage));
  1953       return *this;
  1954     }
  1955 
  1956     /// \brief Edge writing rule
  1957     ///
  1958     /// Add an edge writing rule to writer.
  1959     BpGraphWriter& edge(const std::string& caption, const Edge& edge) {
  1960       typedef _writer_bits::MapLookUpConverter<Edge> Converter;
  1961       Converter converter(_edge_index);
  1962       _writer_bits::ValueStorageBase* storage =
  1963         new _writer_bits::ValueStorage<Edge, Converter>(edge, converter);
  1964       _attributes.push_back(std::make_pair(caption, storage));
  1965       return *this;
  1966     }
  1967 
  1968     /// \brief Arc writing rule
  1969     ///
  1970     /// Add an arc writing rule to writer.
  1971     BpGraphWriter& arc(const std::string& caption, const Arc& arc) {
  1972       typedef _writer_bits::GraphArcLookUpConverter<BGR> Converter;
  1973       Converter converter(_graph, _edge_index);
  1974       _writer_bits::ValueStorageBase* storage =
  1975         new _writer_bits::ValueStorage<Arc, Converter>(arc, converter);
  1976       _attributes.push_back(std::make_pair(caption, storage));
  1977       return *this;
  1978     }
  1979 
  1980     /// \name Section Captions
  1981     /// @{
  1982 
  1983     /// \brief Add an additional caption to the \c \@red_nodes and
  1984     /// \c \@blue_nodes section
  1985     ///
  1986     /// Add an additional caption to the \c \@red_nodes and \c
  1987     /// \@blue_nodes section.
  1988     BpGraphWriter& nodes(const std::string& caption) {
  1989       _nodes_caption = caption;
  1990       return *this;
  1991     }
  1992 
  1993     /// \brief Add an additional caption to the \c \@edges section
  1994     ///
  1995     /// Add an additional caption to the \c \@edges section.
  1996     BpGraphWriter& edges(const std::string& caption) {
  1997       _edges_caption = caption;
  1998       return *this;
  1999     }
  2000 
  2001     /// \brief Add an additional caption to the \c \@attributes section
  2002     ///
  2003     /// Add an additional caption to the \c \@attributes section.
  2004     BpGraphWriter& attributes(const std::string& caption) {
  2005       _attributes_caption = caption;
  2006       return *this;
  2007     }
  2008 
  2009     /// \name Skipping Section
  2010     /// @{
  2011 
  2012     /// \brief Skip writing the node set
  2013     ///
  2014     /// The \c \@red_nodes and \c \@blue_nodes section will not be
  2015     /// written to the stream.
  2016     BpGraphWriter& skipNodes() {
  2017       LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member");
  2018       _skip_nodes = true;
  2019       return *this;
  2020     }
  2021 
  2022     /// \brief Skip writing edge set
  2023     ///
  2024     /// The \c \@edges section will not be written to the stream.
  2025     BpGraphWriter& skipEdges() {
  2026       LEMON_ASSERT(!_skip_edges, "Multiple usage of skipEdges() member");
  2027       _skip_edges = true;
  2028       return *this;
  2029     }
  2030 
  2031     /// @}
  2032 
  2033   private:
  2034 
  2035     void writeRedNodes() {
  2036       _writer_bits::MapStorageBase<Node>* label = 0;
  2037       for (typename NodeMaps::iterator it = _red_maps.begin();
  2038            it != _red_maps.end(); ++it) {
  2039         if (it->first == "label") {
  2040           label = it->second;
  2041           break;
  2042         }
  2043       }
  2044 
  2045       *_os << "@red_nodes";
  2046       if (!_nodes_caption.empty()) {
  2047         _writer_bits::writeToken(*_os << ' ', _nodes_caption);
  2048       }
  2049       *_os << std::endl;
  2050 
  2051       if (label == 0) {
  2052         *_os << "label" << '\t';
  2053       }
  2054       for (typename NodeMaps::iterator it = _red_maps.begin();
  2055            it != _red_maps.end(); ++it) {
  2056         _writer_bits::writeToken(*_os, it->first) << '\t';
  2057       }
  2058       *_os << std::endl;
  2059 
  2060       std::vector<Node> nodes;
  2061       for (RedNodeIt n(_graph); n != INVALID; ++n) {
  2062         nodes.push_back(n);
  2063       }
  2064 
  2065       if (label == 0) {
  2066         IdMap<BGR, Node> id_map(_graph);
  2067         _writer_bits::MapLess<IdMap<BGR, Node> > id_less(id_map);
  2068         std::sort(nodes.begin(), nodes.end(), id_less);
  2069       } else {
  2070         label->sort(nodes);
  2071       }
  2072 
  2073       for (int i = 0; i < static_cast<int>(nodes.size()); ++i) {
  2074         Node n = nodes[i];
  2075         if (label == 0) {
  2076           std::ostringstream os;
  2077           os << _graph.id(n);
  2078           _writer_bits::writeToken(*_os, os.str());
  2079           *_os << '\t';
  2080           _node_index.insert(std::make_pair(n, os.str()));
  2081         }
  2082         for (typename NodeMaps::iterator it = _red_maps.begin();
  2083              it != _red_maps.end(); ++it) {
  2084           std::string value = it->second->get(n);
  2085           _writer_bits::writeToken(*_os, value);
  2086           if (it->first == "label") {
  2087             _node_index.insert(std::make_pair(n, value));
  2088           }
  2089           *_os << '\t';
  2090         }
  2091         *_os << std::endl;
  2092       }
  2093     }
  2094 
  2095     void writeBlueNodes() {
  2096       _writer_bits::MapStorageBase<Node>* label = 0;
  2097       for (typename NodeMaps::iterator it = _blue_maps.begin();
  2098            it != _blue_maps.end(); ++it) {
  2099         if (it->first == "label") {
  2100           label = it->second;
  2101           break;
  2102         }
  2103       }
  2104 
  2105       *_os << "@blue_nodes";
  2106       if (!_nodes_caption.empty()) {
  2107         _writer_bits::writeToken(*_os << ' ', _nodes_caption);
  2108       }
  2109       *_os << std::endl;
  2110 
  2111       if (label == 0) {
  2112         *_os << "label" << '\t';
  2113       }
  2114       for (typename NodeMaps::iterator it = _blue_maps.begin();
  2115            it != _blue_maps.end(); ++it) {
  2116         _writer_bits::writeToken(*_os, it->first) << '\t';
  2117       }
  2118       *_os << std::endl;
  2119 
  2120       std::vector<Node> nodes;
  2121       for (BlueNodeIt n(_graph); n != INVALID; ++n) {
  2122         nodes.push_back(n);
  2123       }
  2124 
  2125       if (label == 0) {
  2126         IdMap<BGR, Node> id_map(_graph);
  2127         _writer_bits::MapLess<IdMap<BGR, Node> > id_less(id_map);
  2128         std::sort(nodes.begin(), nodes.end(), id_less);
  2129       } else {
  2130         label->sort(nodes);
  2131       }
  2132 
  2133       for (int i = 0; i < static_cast<int>(nodes.size()); ++i) {
  2134         Node n = nodes[i];
  2135         if (label == 0) {
  2136           std::ostringstream os;
  2137           os << _graph.id(n);
  2138           _writer_bits::writeToken(*_os, os.str());
  2139           *_os << '\t';
  2140           _node_index.insert(std::make_pair(n, os.str()));
  2141         }
  2142         for (typename NodeMaps::iterator it = _blue_maps.begin();
  2143              it != _blue_maps.end(); ++it) {
  2144           std::string value = it->second->get(n);
  2145           _writer_bits::writeToken(*_os, value);
  2146           if (it->first == "label") {
  2147             _node_index.insert(std::make_pair(n, value));
  2148           }
  2149           *_os << '\t';
  2150         }
  2151         *_os << std::endl;
  2152       }
  2153     }
  2154 
  2155     void createRedNodeIndex() {
  2156       _writer_bits::MapStorageBase<Node>* label = 0;
  2157       for (typename NodeMaps::iterator it = _red_maps.begin();
  2158            it != _red_maps.end(); ++it) {
  2159         if (it->first == "label") {
  2160           label = it->second;
  2161           break;
  2162         }
  2163       }
  2164 
  2165       if (label == 0) {
  2166         for (NodeIt n(_graph); n != INVALID; ++n) {
  2167           std::ostringstream os;
  2168           os << _graph.id(n);
  2169           _node_index.insert(std::make_pair(n, os.str()));
  2170         }
  2171       } else {
  2172         for (NodeIt n(_graph); n != INVALID; ++n) {
  2173           std::string value = label->get(n);
  2174           _node_index.insert(std::make_pair(n, value));
  2175         }
  2176       }
  2177     }
  2178 
  2179     void createBlueNodeIndex() {
  2180       _writer_bits::MapStorageBase<Node>* label = 0;
  2181       for (typename NodeMaps::iterator it = _blue_maps.begin();
  2182            it != _blue_maps.end(); ++it) {
  2183         if (it->first == "label") {
  2184           label = it->second;
  2185           break;
  2186         }
  2187       }
  2188 
  2189       if (label == 0) {
  2190         for (NodeIt n(_graph); n != INVALID; ++n) {
  2191           std::ostringstream os;
  2192           os << _graph.id(n);
  2193           _node_index.insert(std::make_pair(n, os.str()));
  2194         }
  2195       } else {
  2196         for (NodeIt n(_graph); n != INVALID; ++n) {
  2197           std::string value = label->get(n);
  2198           _node_index.insert(std::make_pair(n, value));
  2199         }
  2200       }
  2201     }
  2202 
  2203     void writeEdges() {
  2204       _writer_bits::MapStorageBase<Edge>* label = 0;
  2205       for (typename EdgeMaps::iterator it = _edge_maps.begin();
  2206            it != _edge_maps.end(); ++it) {
  2207         if (it->first == "label") {
  2208           label = it->second;
  2209           break;
  2210         }
  2211       }
  2212 
  2213       *_os << "@edges";
  2214       if (!_edges_caption.empty()) {
  2215         _writer_bits::writeToken(*_os << ' ', _edges_caption);
  2216       }
  2217       *_os << std::endl;
  2218 
  2219       *_os << '\t' << '\t';
  2220       if (label == 0) {
  2221         *_os << "label" << '\t';
  2222       }
  2223       for (typename EdgeMaps::iterator it = _edge_maps.begin();
  2224            it != _edge_maps.end(); ++it) {
  2225         _writer_bits::writeToken(*_os, it->first) << '\t';
  2226       }
  2227       *_os << std::endl;
  2228 
  2229       std::vector<Edge> edges;
  2230       for (EdgeIt n(_graph); n != INVALID; ++n) {
  2231         edges.push_back(n);
  2232       }
  2233 
  2234       if (label == 0) {
  2235         IdMap<BGR, Edge> id_map(_graph);
  2236         _writer_bits::MapLess<IdMap<BGR, Edge> > id_less(id_map);
  2237         std::sort(edges.begin(), edges.end(), id_less);
  2238       } else {
  2239         label->sort(edges);
  2240       }
  2241 
  2242       for (int i = 0; i < static_cast<int>(edges.size()); ++i) {
  2243         Edge e = edges[i];
  2244         _writer_bits::writeToken(*_os, _node_index.
  2245                                  find(_graph.redNode(e))->second);
  2246         *_os << '\t';
  2247         _writer_bits::writeToken(*_os, _node_index.
  2248                                  find(_graph.blueNode(e))->second);
  2249         *_os << '\t';
  2250         if (label == 0) {
  2251           std::ostringstream os;
  2252           os << _graph.id(e);
  2253           _writer_bits::writeToken(*_os, os.str());
  2254           *_os << '\t';
  2255           _edge_index.insert(std::make_pair(e, os.str()));
  2256         }
  2257         for (typename EdgeMaps::iterator it = _edge_maps.begin();
  2258              it != _edge_maps.end(); ++it) {
  2259           std::string value = it->second->get(e);
  2260           _writer_bits::writeToken(*_os, value);
  2261           if (it->first == "label") {
  2262             _edge_index.insert(std::make_pair(e, value));
  2263           }
  2264           *_os << '\t';
  2265         }
  2266         *_os << std::endl;
  2267       }
  2268     }
  2269 
  2270     void createEdgeIndex() {
  2271       _writer_bits::MapStorageBase<Edge>* label = 0;
  2272       for (typename EdgeMaps::iterator it = _edge_maps.begin();
  2273            it != _edge_maps.end(); ++it) {
  2274         if (it->first == "label") {
  2275           label = it->second;
  2276           break;
  2277         }
  2278       }
  2279 
  2280       if (label == 0) {
  2281         for (EdgeIt e(_graph); e != INVALID; ++e) {
  2282           std::ostringstream os;
  2283           os << _graph.id(e);
  2284           _edge_index.insert(std::make_pair(e, os.str()));
  2285         }
  2286       } else {
  2287         for (EdgeIt e(_graph); e != INVALID; ++e) {
  2288           std::string value = label->get(e);
  2289           _edge_index.insert(std::make_pair(e, value));
  2290         }
  2291       }
  2292     }
  2293 
  2294     void writeAttributes() {
  2295       if (_attributes.empty()) return;
  2296       *_os << "@attributes";
  2297       if (!_attributes_caption.empty()) {
  2298         _writer_bits::writeToken(*_os << ' ', _attributes_caption);
  2299       }
  2300       *_os << std::endl;
  2301       for (typename Attributes::iterator it = _attributes.begin();
  2302            it != _attributes.end(); ++it) {
  2303         _writer_bits::writeToken(*_os, it->first) << ' ';
  2304         _writer_bits::writeToken(*_os, it->second->get());
  2305         *_os << std::endl;
  2306       }
  2307     }
  2308 
  2309   public:
  2310 
  2311     /// \name Execution of the Writer
  2312     /// @{
  2313 
  2314     /// \brief Start the batch processing
  2315     ///
  2316     /// This function starts the batch processing.
  2317     void run() {
  2318       if (!_skip_nodes) {
  2319         writeRedNodes();
  2320         writeBlueNodes();
  2321       } else {
  2322         createRedNodeIndex();
  2323         createBlueNodeIndex();
  2324       }
  2325       if (!_skip_edges) {
  2326         writeEdges();
  2327       } else {
  2328         createEdgeIndex();
  2329       }
  2330       writeAttributes();
  2331     }
  2332 
  2333     /// \brief Give back the stream of the writer
  2334     ///
  2335     /// Give back the stream of the writer
  2336     std::ostream& ostream() {
  2337       return *_os;
  2338     }
  2339 
  2340     /// @}
  2341   };
  2342 
  2343   /// \ingroup lemon_io
  2344   ///
  2345   /// \brief Return a \ref BpGraphWriter class
  2346   ///
  2347   /// This function just returns a \ref BpGraphWriter class.
  2348   ///
  2349   /// With this function a bipartite graph can be write to a file or output
  2350   /// stream in \ref lgf-format "LGF" format with several maps and
  2351   /// attributes. For example, with the following code a bipartite
  2352   /// weighted matching problem can be written to the standard output,
  2353   /// i.e. a graph with a \e weight map on the edges:
  2354   ///
  2355   ///\code
  2356   ///ListBpGraph graph;
  2357   ///ListBpGraph::EdgeMap<int> weight(graph);
  2358   ///  // Setting the weight map
  2359   ///bpGraphWriter(graph, std::cout).
  2360   ///  edgeMap("weight", weight).
  2361   ///  run();
  2362   ///\endcode
  2363   ///
  2364   /// For a complete documentation, please see the \ref BpGraphWriter
  2365   /// class documentation.
  2366   /// \warning Don't forget to put the \ref BpGraphWriter::run() "run()"
  2367   /// to the end of the parameter list.
  2368   /// \relates BpGraphWriter
  2369   /// \sa bpGraphWriter(const TBGR& graph, const std::string& fn)
  2370   /// \sa bpGraphWriter(const TBGR& graph, const char* fn)
  2371   template <typename TBGR>
  2372   BpGraphWriter<TBGR> bpGraphWriter(const TBGR& graph, std::ostream& os) {
  2373     BpGraphWriter<TBGR> tmp(graph, os);
  2374     return tmp;
  2375   }
  2376 
  2377   /// \brief Return a \ref BpGraphWriter class
  2378   ///
  2379   /// This function just returns a \ref BpGraphWriter class.
  2380   /// \relates BpGraphWriter
  2381   /// \sa graphWriter(const TBGR& graph, std::ostream& os)
  2382   template <typename TBGR>
  2383   BpGraphWriter<TBGR> bpGraphWriter(const TBGR& graph, const std::string& fn) {
  2384     BpGraphWriter<TBGR> tmp(graph, fn);
  2385     return tmp;
  2386   }
  2387 
  2388   /// \brief Return a \ref BpGraphWriter class
  2389   ///
  2390   /// This function just returns a \ref BpGraphWriter class.
  2391   /// \relates BpGraphWriter
  2392   /// \sa graphWriter(const TBGR& graph, std::ostream& os)
  2393   template <typename TBGR>
  2394   BpGraphWriter<TBGR> bpGraphWriter(const TBGR& graph, const char* fn) {
  2395     BpGraphWriter<TBGR> tmp(graph, fn);
  2396     return tmp;
  2397   }
  2398 
  2399   class SectionWriter;
  2400 
  2401   SectionWriter sectionWriter(std::istream& is);
  2402   SectionWriter sectionWriter(const std::string& fn);
  2403   SectionWriter sectionWriter(const char* fn);
  2404 
  2405   /// \ingroup lemon_io
  2406   ///
  2407   /// \brief Section writer class
  2408   ///
  2409   /// In the \ref lgf-format "LGF" file extra sections can be placed,
  2410   /// which contain any data in arbitrary format. Such sections can be
  2411   /// written with this class. A writing rule can be added to the
  2412   /// class with two different functions. With the \c sectionLines()
  2413   /// function a generator can write the section line-by-line, while
  2414   /// with the \c sectionStream() member the section can be written to
  2415   /// an output stream.
  2416   class SectionWriter {
  2417   private:
  2418 
  2419     std::ostream* _os;
  2420     bool local_os;
  2421 
  2422     typedef std::vector<std::pair<std::string, _writer_bits::Section*> >
  2423     Sections;
  2424 
  2425     Sections _sections;
  2426 
  2427   public:
  2428 
  2429     /// \brief Constructor
  2430     ///
  2431     /// Construct a section writer, which writes to the given output
  2432     /// stream.
  2433     SectionWriter(std::ostream& os)
  2434       : _os(&os), local_os(false) {}
  2435 
  2436     /// \brief Constructor
  2437     ///
  2438     /// Construct a section writer, which writes into the given file.
  2439     SectionWriter(const std::string& fn)
  2440       : _os(new std::ofstream(fn.c_str())), local_os(true) {
  2441       if (!(*_os)) {
  2442         delete _os;
  2443         throw IoError("Cannot write file", fn);
  2444       }
  2445     }
  2446 
  2447     /// \brief Constructor
  2448     ///
  2449     /// Construct a section writer, which writes into the given file.
  2450     SectionWriter(const char* fn)
  2451       : _os(new std::ofstream(fn)), local_os(true) {
  2452       if (!(*_os)) {
  2453         delete _os;
  2454         throw IoError("Cannot write file", fn);
  2455       }
  2456     }
  2457 
  2458     /// \brief Destructor
  2459     ~SectionWriter() {
  2460       for (Sections::iterator it = _sections.begin();
  2461            it != _sections.end(); ++it) {
  2462         delete it->second;
  2463       }
  2464 
  2465       if (local_os) {
  2466         delete _os;
  2467       }
  2468 
  2469     }
  2470 
  2471   private:
  2472 
  2473     friend SectionWriter sectionWriter(std::ostream& os);
  2474     friend SectionWriter sectionWriter(const std::string& fn);
  2475     friend SectionWriter sectionWriter(const char* fn);
  2476 
  2477     SectionWriter(SectionWriter& other)
  2478       : _os(other._os), local_os(other.local_os) {
  2479 
  2480       other._os = 0;
  2481       other.local_os = false;
  2482 
  2483       _sections.swap(other._sections);
  2484     }
  2485 
  2486     SectionWriter& operator=(const SectionWriter&);
  2487 
  2488   public:
  2489 
  2490     /// \name Section Writers
  2491     /// @{
  2492 
  2493     /// \brief Add a section writer with line oriented writing
  2494     ///
  2495     /// The first parameter is the type descriptor of the section, the
  2496     /// second is a generator with std::string values. At the writing
  2497     /// process, the returned \c std::string will be written into the
  2498     /// output file until it is an empty string.
  2499     ///
  2500     /// For example, an integer vector is written into a section.
  2501     ///\code
  2502     ///  @numbers
  2503     ///  12 45 23 78
  2504     ///  4 28 38 28
  2505     ///  23 6 16
  2506     ///\endcode
  2507     ///
  2508     /// The generator is implemented as a struct.
  2509     ///\code
  2510     ///  struct NumberSection {
  2511     ///    std::vector<int>::const_iterator _it, _end;
  2512     ///    NumberSection(const std::vector<int>& data)
  2513     ///      : _it(data.begin()), _end(data.end()) {}
  2514     ///    std::string operator()() {
  2515     ///      int rem_in_line = 4;
  2516     ///      std::ostringstream ls;
  2517     ///      while (rem_in_line > 0 && _it != _end) {
  2518     ///        ls << *(_it++) << ' ';
  2519     ///        --rem_in_line;
  2520     ///      }
  2521     ///      return ls.str();
  2522     ///    }
  2523     ///  };
  2524     ///
  2525     ///  // ...
  2526     ///
  2527     ///  writer.sectionLines("numbers", NumberSection(vec));
  2528     ///\endcode
  2529     template <typename Functor>
  2530     SectionWriter& sectionLines(const std::string& type, Functor functor) {
  2531       LEMON_ASSERT(!type.empty(), "Type is empty.");
  2532       _sections.push_back(std::make_pair(type,
  2533         new _writer_bits::LineSection<Functor>(functor)));
  2534       return *this;
  2535     }
  2536 
  2537 
  2538     /// \brief Add a section writer with stream oriented writing
  2539     ///
  2540     /// The first parameter is the type of the section, the second is
  2541     /// a functor, which takes a \c std::ostream& parameter. The
  2542     /// functor writes the section to the output stream.
  2543     /// \warning The last line must be closed with end-line character.
  2544     template <typename Functor>
  2545     SectionWriter& sectionStream(const std::string& type, Functor functor) {
  2546       LEMON_ASSERT(!type.empty(), "Type is empty.");
  2547       _sections.push_back(std::make_pair(type,
  2548          new _writer_bits::StreamSection<Functor>(functor)));
  2549       return *this;
  2550     }
  2551 
  2552     /// @}
  2553 
  2554   public:
  2555 
  2556 
  2557     /// \name Execution of the Writer
  2558     /// @{
  2559 
  2560     /// \brief Start the batch processing
  2561     ///
  2562     /// This function starts the batch processing.
  2563     void run() {
  2564 
  2565       LEMON_ASSERT(_os != 0, "This writer is assigned to an other writer");
  2566 
  2567       for (Sections::iterator it = _sections.begin();
  2568            it != _sections.end(); ++it) {
  2569         (*_os) << '@' << it->first << std::endl;
  2570         it->second->process(*_os);
  2571       }
  2572     }
  2573 
  2574     /// \brief Give back the stream of the writer
  2575     ///
  2576     /// Returns the stream of the writer
  2577     std::ostream& ostream() {
  2578       return *_os;
  2579     }
  2580 
  2581     /// @}
  2582 
  2583   };
  2584 
  2585   /// \ingroup lemon_io
  2586   ///
  2587   /// \brief Return a \ref SectionWriter class
  2588   ///
  2589   /// This function just returns a \ref SectionWriter class.
  2590   ///
  2591   /// Please see SectionWriter documentation about the custom section
  2592   /// output.
  2593   ///
  2594   /// \relates SectionWriter
  2595   /// \sa sectionWriter(const std::string& fn)
  2596   /// \sa sectionWriter(const char *fn)
  2597   inline SectionWriter sectionWriter(std::ostream& os) {
  2598     SectionWriter tmp(os);
  2599     return tmp;
  2600   }
  2601 
  2602   /// \brief Return a \ref SectionWriter class
  2603   ///
  2604   /// This function just returns a \ref SectionWriter class.
  2605   /// \relates SectionWriter
  2606   /// \sa sectionWriter(std::ostream& os)
  2607   inline SectionWriter sectionWriter(const std::string& fn) {
  2608     SectionWriter tmp(fn);
  2609     return tmp;
  2610   }
  2611 
  2612   /// \brief Return a \ref SectionWriter class
  2613   ///
  2614   /// This function just returns a \ref SectionWriter class.
  2615   /// \relates SectionWriter
  2616   /// \sa sectionWriter(std::ostream& os)
  2617   inline SectionWriter sectionWriter(const char* fn) {
  2618     SectionWriter tmp(fn);
  2619     return tmp;
  2620   }
  2621 }
  2622 
  2623 #endif