gravatar
deba@inf.elte.hu
deba@inf.elte.hu
More docs for undirected LGF IO
0 3 0
default
3 files changed with 22 insertions and 2 deletions:
↑ Collapse diff ↑
Ignore white space 768 line context
1 1
/* -*- C++ -*-
2 2
 *
3 3
 * This file is a part of LEMON, a generic C++ optimization library
4 4
 *
5 5
 * Copyright (C) 2003-2008
6 6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7 7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
8 8
 *
9 9
 * Permission to use, modify and distribute this software is granted
10 10
 * provided that this copyright notice appears in all copies. For
11 11
 * precise terms see the accompanying LICENSE file.
12 12
 *
13 13
 * This software is provided "AS IS" with no warranty of any kind,
14 14
 * express or implied, and with no claim as to its suitability for any
15 15
 * purpose.
16 16
 *
17 17
 */
18 18

	
19 19
namespace lemon {
20 20
/*!
21 21

	
22 22

	
23 23

	
24 24
\page lgf-format Lemon Graph Format (LGF)
25 25

	
26 26
The \e LGF is a <em>column oriented</em>
27 27
file format for storing graphs and associated data like
28 28
node and edge maps.
29 29

	
30 30
Each line with \c '#' first non-whitespace
31 31
character is considered as a comment line.
32 32

	
33 33
Otherwise the file consists of sections starting with
34 34
a header line. The header lines starts with an \c '@' character followed by the
35 35
type of section. The standard section types are \c \@nodes, \c
36 36
\@arcs and \c \@edges
37 37
and \@attributes. Each header line may also have an optional
38 38
\e name, which can be use to distinguish the sections of the same
39 39
type.
40 40

	
41 41
The standard sections are column oriented, each line consists of
42 42
<em>token</em>s separated by whitespaces. A token can be \e plain or
43 43
\e quoted. A plain token is just a sequence of non-whitespace characters,
44 44
while a quoted token is a
45 45
character sequence surrounded by double quotes, and it can also
46 46
contain whitespaces and escape sequences. 
47 47

	
48 48
The \c \@nodes section describes a set of nodes and associated
49 49
maps. The first is a header line, its columns are the names of the
50 50
maps appearing in the following lines.
51 51
One of the maps must be called \c
52 52
"label", which plays special role in the file.
53 53
The following
54 54
non-empty lines until the next section describes nodes of the
55 55
graph. Each line contains the values of the node maps
56 56
associated to the current node.
57 57

	
58 58
\code
59 59
 @nodes
60 60
 label   coordinates size    title
61 61
 1       (10,20)     10      "First node"
62 62
 2       (80,80)     8       "Second node"
63 63
 3       (40,10)     10      "Third node"
64 64
\endcode
65 65

	
66 66
The \c \@arcs section is very similar to the \c \@nodes section,
67 67
it again starts with a header line describing the names of the maps,
68 68
but the \c "label" map is not obligatory here. The following lines
69 69
describe the arcs. The first two tokens of each line are
70 70
the source and the target node of the arc, respectively, then come the map
71 71
values. The source and target tokens must be node labels.
72 72

	
73 73
\code
74 74
 @arcs
75 75
 	      capacity
76 76
 1   2   16
77 77
 1   3   12
78 78
 2   3   18
79 79
\endcode
80 80

	
81
The \c \@edges is just a synonym of \c \@arcs.
81
The \c \@edges is just a synonym of \c \@arcs. The @arcs section can
82
also store the edge set of an undirected graph. In such case there is
83
a conventional method for store arc maps in the file, if two columns
84
has the same caption with \c '+' and \c '-' prefix, then these columns
85
can be regarded as the values of an arc map.
82 86

	
83 87
The \c \@attributes section contains key-value pairs, each line
84
consists of two tokens, an attribute name, and then an attribute value.
88
consists of two tokens, an attribute name, and then an attribute
89
value. The value of the attribute could be also a label value of a
90
node or an edge, or even an edge label prefixed with \c '+' or \c '-',
91
which regards to the forward or backward directed arc of the
92
corresponding edge.
85 93

	
86 94
\code
87 95
 @attributes
88 96
 source 1
89 97
 target 3
90 98
 caption "LEMON test digraph"
91 99
\endcode
92 100

	
93 101
The \e LGF can contain extra sections, but there is no restriction on
94 102
the format of such sections.
95 103

	
96 104
*/
97 105
}
98 106

	
99 107
//  LocalWords:  whitespace whitespaces
Ignore white space 768 line context
... ...
@@ -845,768 +845,774 @@
845 845
	if (readSuccess() && line) line.putback(c);
846 846
	if (!_node_maps.empty()) 
847 847
	  throw DataFormatError("Cannot find map names");
848 848
	return;
849 849
      }
850 850
      line.putback(c);
851 851

	
852 852
      {
853 853
	std::map<std::string, int> maps;
854 854
	
855 855
	std::string map;
856 856
	int index = 0;
857 857
	while (_reader_bits::readToken(line, map)) {
858 858
	  if (maps.find(map) != maps.end()) {
859 859
	    std::ostringstream msg;
860 860
	    msg << "Multiple occurence of node map: " << map;
861 861
	    throw DataFormatError(msg.str().c_str());
862 862
	  }
863 863
	  maps.insert(std::make_pair(map, index));
864 864
	  ++index;
865 865
	}
866 866
	
867 867
	for (int i = 0; i < static_cast<int>(_node_maps.size()); ++i) {
868 868
	  std::map<std::string, int>::iterator jt = 
869 869
	    maps.find(_node_maps[i].first);
870 870
	  if (jt == maps.end()) {
871 871
	    std::ostringstream msg;
872 872
	    msg << "Map not found in file: " << _node_maps[i].first;
873 873
	    throw DataFormatError(msg.str().c_str());
874 874
	  }
875 875
	  map_index[i] = jt->second;
876 876
	}
877 877

	
878 878
	{
879 879
	  std::map<std::string, int>::iterator jt = maps.find("label");
880 880
	  if (jt != maps.end()) {
881 881
	    label_index = jt->second;
882 882
	  } else {
883 883
	    label_index = -1;
884 884
	  }
885 885
	}
886 886
	map_num = maps.size();
887 887
      }
888 888

	
889 889
      while (readLine() && line >> c && c != '@') {
890 890
	line.putback(c);
891 891

	
892 892
	std::vector<std::string> tokens(map_num);
893 893
	for (int i = 0; i < map_num; ++i) {
894 894
	  if (!_reader_bits::readToken(line, tokens[i])) {
895 895
	    std::ostringstream msg;
896 896
	    msg << "Column not found (" << i + 1 << ")";
897 897
	    throw DataFormatError(msg.str().c_str());
898 898
	  }
899 899
	}
900 900
	if (line >> std::ws >> c)
901 901
	  throw DataFormatError("Extra character on the end of line");
902 902
	
903 903
	Node n;
904 904
	if (!_use_nodes) {
905 905
	  n = _digraph.addNode();
906 906
	  if (label_index != -1)
907 907
	    _node_index.insert(std::make_pair(tokens[label_index], n));
908 908
	} else {
909 909
	  if (label_index == -1) 
910 910
	    throw DataFormatError("Label map not found in file");
911 911
	  typename std::map<std::string, Node>::iterator it =
912 912
	    _node_index.find(tokens[label_index]);
913 913
	  if (it == _node_index.end()) {
914 914
	    std::ostringstream msg;
915 915
	    msg << "Node with label not found: " << tokens[label_index];
916 916
	    throw DataFormatError(msg.str().c_str());	    
917 917
	  }
918 918
	  n = it->second;
919 919
	}
920 920

	
921 921
	for (int i = 0; i < static_cast<int>(_node_maps.size()); ++i) {
922 922
	  _node_maps[i].second->set(n, tokens[map_index[i]]);
923 923
	}
924 924

	
925 925
      }
926 926
      if (readSuccess()) {
927 927
	line.putback(c);
928 928
      }
929 929
    }
930 930

	
931 931
    void readArcs() {
932 932

	
933 933
      std::vector<int> map_index(_arc_maps.size());
934 934
      int map_num, label_index;
935 935

	
936 936
      char c;
937 937
      if (!readLine() || !(line >> c) || c == '@') {
938 938
	if (readSuccess() && line) line.putback(c);
939 939
	if (!_arc_maps.empty()) 
940 940
	  throw DataFormatError("Cannot find map names");
941 941
	return;
942 942
      }
943 943
      line.putback(c);
944 944
      
945 945
      {
946 946
	std::map<std::string, int> maps;
947 947
	
948 948
	std::string map;
949 949
	int index = 0;
950 950
	while (_reader_bits::readToken(line, map)) {
951 951
	  if (maps.find(map) != maps.end()) {
952 952
	    std::ostringstream msg;
953 953
	    msg << "Multiple occurence of arc map: " << map;
954 954
	    throw DataFormatError(msg.str().c_str());
955 955
	  }
956 956
	  maps.insert(std::make_pair(map, index));
957 957
	  ++index;
958 958
	}
959 959
	
960 960
	for (int i = 0; i < static_cast<int>(_arc_maps.size()); ++i) {
961 961
	  std::map<std::string, int>::iterator jt = 
962 962
	    maps.find(_arc_maps[i].first);
963 963
	  if (jt == maps.end()) {
964 964
	    std::ostringstream msg;
965 965
	    msg << "Map not found in file: " << _arc_maps[i].first;
966 966
	    throw DataFormatError(msg.str().c_str());
967 967
	  }
968 968
	  map_index[i] = jt->second;
969 969
	}
970 970

	
971 971
	{
972 972
	  std::map<std::string, int>::iterator jt = maps.find("label");
973 973
	  if (jt != maps.end()) {
974 974
	    label_index = jt->second;
975 975
	  } else {
976 976
	    label_index = -1;
977 977
	  }
978 978
	}
979 979
	map_num = maps.size();
980 980
      }
981 981

	
982 982
      while (readLine() && line >> c && c != '@') {
983 983
	line.putback(c);
984 984

	
985 985
	std::string source_token;
986 986
	std::string target_token;
987 987

	
988 988
	if (!_reader_bits::readToken(line, source_token))
989 989
	  throw DataFormatError("Source not found");
990 990

	
991 991
	if (!_reader_bits::readToken(line, target_token))
992 992
	  throw DataFormatError("Target not found");
993 993
	
994 994
	std::vector<std::string> tokens(map_num);
995 995
	for (int i = 0; i < map_num; ++i) {
996 996
	  if (!_reader_bits::readToken(line, tokens[i])) {
997 997
	    std::ostringstream msg;
998 998
	    msg << "Column not found (" << i + 1 << ")";
999 999
	    throw DataFormatError(msg.str().c_str());
1000 1000
	  }
1001 1001
	}
1002 1002
	if (line >> std::ws >> c)
1003 1003
	  throw DataFormatError("Extra character on the end of line");
1004 1004
	
1005 1005
	Arc a;
1006 1006
	if (!_use_arcs) {
1007 1007

	
1008 1008
          typename NodeIndex::iterator it;
1009 1009
 
1010 1010
          it = _node_index.find(source_token);
1011 1011
          if (it == _node_index.end()) {
1012 1012
            std::ostringstream msg;
1013 1013
            msg << "Item not found: " << source_token;
1014 1014
            throw DataFormatError(msg.str().c_str());
1015 1015
          }
1016 1016
          Node source = it->second;
1017 1017

	
1018 1018
          it = _node_index.find(target_token);
1019 1019
          if (it == _node_index.end()) {       
1020 1020
            std::ostringstream msg;            
1021 1021
            msg << "Item not found: " << target_token;
1022 1022
            throw DataFormatError(msg.str().c_str());
1023 1023
          }                                          
1024 1024
          Node target = it->second;                            
1025 1025

	
1026 1026
	  a = _digraph.addArc(source, target);
1027 1027
	  if (label_index != -1) 
1028 1028
	    _arc_index.insert(std::make_pair(tokens[label_index], a));
1029 1029
	} else {
1030 1030
	  if (label_index == -1) 
1031 1031
	    throw DataFormatError("Label map not found in file");
1032 1032
	  typename std::map<std::string, Arc>::iterator it =
1033 1033
	    _arc_index.find(tokens[label_index]);
1034 1034
	  if (it == _arc_index.end()) {
1035 1035
	    std::ostringstream msg;
1036 1036
	    msg << "Arc with label not found: " << tokens[label_index];
1037 1037
	    throw DataFormatError(msg.str().c_str());	    
1038 1038
	  }
1039 1039
	  a = it->second;
1040 1040
	}
1041 1041

	
1042 1042
	for (int i = 0; i < static_cast<int>(_arc_maps.size()); ++i) {
1043 1043
	  _arc_maps[i].second->set(a, tokens[map_index[i]]);
1044 1044
	}
1045 1045

	
1046 1046
      }
1047 1047
      if (readSuccess()) {
1048 1048
	line.putback(c);
1049 1049
      }
1050 1050
    }
1051 1051

	
1052 1052
    void readAttributes() {
1053 1053

	
1054 1054
      std::set<std::string> read_attr;
1055 1055

	
1056 1056
      char c;
1057 1057
      while (readLine() && line >> c && c != '@') {
1058 1058
	line.putback(c);
1059 1059
	
1060 1060
	std::string attr, token;
1061 1061
	if (!_reader_bits::readToken(line, attr))
1062 1062
	  throw DataFormatError("Attribute name not found");
1063 1063
	if (!_reader_bits::readToken(line, token))
1064 1064
	  throw DataFormatError("Attribute value not found");
1065 1065
	if (line >> c)
1066 1066
	  throw DataFormatError("Extra character on the end of line");	  
1067 1067

	
1068 1068
	{
1069 1069
	  std::set<std::string>::iterator it = read_attr.find(attr);
1070 1070
	  if (it != read_attr.end()) {
1071 1071
	    std::ostringstream msg;
1072 1072
	    msg << "Multiple occurence of attribute " << attr;
1073 1073
	    throw DataFormatError(msg.str().c_str());
1074 1074
	  }
1075 1075
	  read_attr.insert(attr);
1076 1076
	}
1077 1077
	
1078 1078
	{
1079 1079
	  typename Attributes::iterator it = _attributes.lower_bound(attr);
1080 1080
	  while (it != _attributes.end() && it->first == attr) {
1081 1081
	    it->second->set(token);
1082 1082
	    ++it;
1083 1083
	  }
1084 1084
	}
1085 1085

	
1086 1086
      }
1087 1087
      if (readSuccess()) {
1088 1088
	line.putback(c);
1089 1089
      }
1090 1090
      for (typename Attributes::iterator it = _attributes.begin();
1091 1091
	   it != _attributes.end(); ++it) {
1092 1092
	if (read_attr.find(it->first) == read_attr.end()) {
1093 1093
	  std::ostringstream msg;
1094 1094
	  msg << "Attribute not found in file: " << it->first;
1095 1095
	  throw DataFormatError(msg.str().c_str());
1096 1096
	}	
1097 1097
      }
1098 1098
    }
1099 1099

	
1100 1100
  public:
1101 1101

	
1102 1102
    /// \name Execution of the reader    
1103 1103
    /// @{
1104 1104

	
1105 1105
    /// \brief Start the batch processing
1106 1106
    ///
1107 1107
    /// This function starts the batch processing
1108 1108
    void run() {
1109 1109
      LEMON_ASSERT(_is != 0, "This reader assigned to an other reader");
1110 1110
      if (!*_is) {
1111 1111
	throw DataFormatError("Cannot find file");
1112 1112
      }
1113 1113
      
1114 1114
      bool nodes_done = _skip_nodes;
1115 1115
      bool arcs_done = _skip_arcs;
1116 1116
      bool attributes_done = false;
1117 1117

	
1118 1118
      line_num = 0;      
1119 1119
      readLine();
1120 1120
      skipSection();
1121 1121

	
1122 1122
      while (readSuccess()) {
1123 1123
	try {
1124 1124
	  char c;
1125 1125
	  std::string section, caption;
1126 1126
	  line >> c;
1127 1127
	  _reader_bits::readToken(line, section);
1128 1128
	  _reader_bits::readToken(line, caption);
1129 1129

	
1130 1130
	  if (line >> c) 
1131 1131
	    throw DataFormatError("Extra character on the end of line");
1132 1132

	
1133 1133
	  if (section == "nodes" && !nodes_done) {
1134 1134
	    if (_nodes_caption.empty() || _nodes_caption == caption) {
1135 1135
	      readNodes();
1136 1136
	      nodes_done = true;
1137 1137
	    }
1138 1138
	  } else if ((section == "arcs" || section == "edges") && 
1139 1139
		     !arcs_done) {
1140 1140
	    if (_arcs_caption.empty() || _arcs_caption == caption) {
1141 1141
	      readArcs();
1142 1142
	      arcs_done = true;
1143 1143
	    }
1144 1144
	  } else if (section == "attributes" && !attributes_done) {
1145 1145
	    if (_attributes_caption.empty() || _attributes_caption == caption) {
1146 1146
	      readAttributes();
1147 1147
	      attributes_done = true;
1148 1148
	    }
1149 1149
	  } else {
1150 1150
	    readLine();
1151 1151
	    skipSection();
1152 1152
	  }
1153 1153
	} catch (DataFormatError& error) {
1154 1154
	  error.line(line_num);
1155 1155
	  throw;
1156 1156
	}	
1157 1157
      }
1158 1158

	
1159 1159
      if (!nodes_done) {
1160 1160
	throw DataFormatError("Section @nodes not found");
1161 1161
      }
1162 1162

	
1163 1163
      if (!arcs_done) {
1164 1164
	throw DataFormatError("Section @arcs not found");
1165 1165
      }
1166 1166

	
1167 1167
      if (!attributes_done && !_attributes.empty()) {
1168 1168
	throw DataFormatError("Section @attributes not found");
1169 1169
      }
1170 1170

	
1171 1171
    }
1172 1172

	
1173 1173
    /// @}
1174 1174
    
1175 1175
  };
1176 1176

	
1177 1177
  /// \brief Return a \ref DigraphReader class
1178 1178
  /// 
1179 1179
  /// This function just returns a \ref DigraphReader class.
1180 1180
  /// \relates DigraphReader
1181 1181
  template <typename Digraph>
1182 1182
  DigraphReader<Digraph> digraphReader(std::istream& is, Digraph& digraph) {
1183 1183
    DigraphReader<Digraph> tmp(is, digraph);
1184 1184
    return tmp;
1185 1185
  }
1186 1186

	
1187 1187
  /// \brief Return a \ref DigraphReader class
1188 1188
  /// 
1189 1189
  /// This function just returns a \ref DigraphReader class.
1190 1190
  /// \relates DigraphReader
1191 1191
  template <typename Digraph>
1192 1192
  DigraphReader<Digraph> digraphReader(const std::string& fn, 
1193 1193
				       Digraph& digraph) {
1194 1194
    DigraphReader<Digraph> tmp(fn, digraph);
1195 1195
    return tmp;
1196 1196
  }
1197 1197

	
1198 1198
  /// \brief Return a \ref DigraphReader class
1199 1199
  /// 
1200 1200
  /// This function just returns a \ref DigraphReader class.
1201 1201
  /// \relates DigraphReader
1202 1202
  template <typename Digraph>
1203 1203
  DigraphReader<Digraph> digraphReader(const char* fn, Digraph& digraph) {
1204 1204
    DigraphReader<Digraph> tmp(fn, digraph);
1205 1205
    return tmp;
1206 1206
  }
1207 1207

	
1208 1208
  template <typename Graph>
1209 1209
  class GraphReader;
1210 1210

	
1211 1211
  template <typename Graph>
1212 1212
  GraphReader<Graph> graphReader(std::istream& is, Graph& graph);    
1213 1213

	
1214 1214
  template <typename Graph>
1215 1215
  GraphReader<Graph> graphReader(const std::string& fn, Graph& graph);   
1216 1216

	
1217 1217
  template <typename Graph>
1218 1218
  GraphReader<Graph> graphReader(const char *fn, Graph& graph);    
1219 1219

	
1220 1220
  /// \ingroup lemon_io
1221 1221
  ///  
1222 1222
  /// \brief \ref lgf-format "LGF" reader for undirected graphs
1223 1223
  ///
1224 1224
  /// This utility reads an \ref lgf-format "LGF" file.
1225 1225
  ///
1226 1226
  /// It can be used almost the same way as \c DigraphReader.
1227 1227
  /// The only difference is that this class can handle edges and
1228 1228
  /// edge maps as well as arcs and arc maps.
1229
  ///
1230
  /// The columns in the \c \@edges (or \c \@arcs) section are the
1231
  /// edge maps. However, if there are two maps with the same name
1232
  /// prefixed with \c '+' and \c '-', then these can be read into an
1233
  /// arc map.  Similarly, an attribute can be read into an arc, if
1234
  /// it's value is an edge label prefixed with \c '+' or \c '-'.
1229 1235
  template <typename _Graph>
1230 1236
  class GraphReader {
1231 1237
  public:
1232 1238

	
1233 1239
    typedef _Graph Graph;
1234 1240
    TEMPLATE_GRAPH_TYPEDEFS(Graph);
1235 1241
    
1236 1242
  private:
1237 1243

	
1238 1244
    std::istream* _is;
1239 1245
    bool local_is;
1240 1246

	
1241 1247
    Graph& _graph;
1242 1248

	
1243 1249
    std::string _nodes_caption;
1244 1250
    std::string _edges_caption;
1245 1251
    std::string _attributes_caption;
1246 1252

	
1247 1253
    typedef std::map<std::string, Node> NodeIndex;
1248 1254
    NodeIndex _node_index;
1249 1255
    typedef std::map<std::string, Edge> EdgeIndex;
1250 1256
    EdgeIndex _edge_index;
1251 1257
    
1252 1258
    typedef std::vector<std::pair<std::string, 
1253 1259
      _reader_bits::MapStorageBase<Node>*> > NodeMaps;    
1254 1260
    NodeMaps _node_maps; 
1255 1261

	
1256 1262
    typedef std::vector<std::pair<std::string,
1257 1263
      _reader_bits::MapStorageBase<Edge>*> > EdgeMaps;
1258 1264
    EdgeMaps _edge_maps;
1259 1265

	
1260 1266
    typedef std::multimap<std::string, _reader_bits::ValueStorageBase*> 
1261 1267
      Attributes;
1262 1268
    Attributes _attributes;
1263 1269

	
1264 1270
    bool _use_nodes;
1265 1271
    bool _use_edges;
1266 1272

	
1267 1273
    bool _skip_nodes;
1268 1274
    bool _skip_edges;
1269 1275

	
1270 1276
    int line_num;
1271 1277
    std::istringstream line;
1272 1278

	
1273 1279
  public:
1274 1280

	
1275 1281
    /// \brief Constructor
1276 1282
    ///
1277 1283
    /// Construct an undirected graph reader, which reads from the given
1278 1284
    /// input stream.
1279 1285
    GraphReader(std::istream& is, Graph& graph) 
1280 1286
      : _is(&is), local_is(false), _graph(graph),
1281 1287
	_use_nodes(false), _use_edges(false),
1282 1288
	_skip_nodes(false), _skip_edges(false) {}
1283 1289

	
1284 1290
    /// \brief Constructor
1285 1291
    ///
1286 1292
    /// Construct an undirected graph reader, which reads from the given
1287 1293
    /// file.
1288 1294
    GraphReader(const std::string& fn, Graph& graph) 
1289 1295
      : _is(new std::ifstream(fn.c_str())), local_is(true), _graph(graph),
1290 1296
    	_use_nodes(false), _use_edges(false),
1291 1297
	_skip_nodes(false), _skip_edges(false) {}
1292 1298
    
1293 1299
    /// \brief Constructor
1294 1300
    ///
1295 1301
    /// Construct an undirected graph reader, which reads from the given
1296 1302
    /// file.
1297 1303
    GraphReader(const char* fn, Graph& graph) 
1298 1304
      : _is(new std::ifstream(fn)), local_is(true), _graph(graph),
1299 1305
    	_use_nodes(false), _use_edges(false),
1300 1306
	_skip_nodes(false), _skip_edges(false) {}
1301 1307

	
1302 1308
    /// \brief Destructor
1303 1309
    ~GraphReader() {
1304 1310
      for (typename NodeMaps::iterator it = _node_maps.begin(); 
1305 1311
	   it != _node_maps.end(); ++it) {
1306 1312
	delete it->second;
1307 1313
      }
1308 1314

	
1309 1315
      for (typename EdgeMaps::iterator it = _edge_maps.begin(); 
1310 1316
	   it != _edge_maps.end(); ++it) {
1311 1317
	delete it->second;
1312 1318
      }
1313 1319

	
1314 1320
      for (typename Attributes::iterator it = _attributes.begin(); 
1315 1321
	   it != _attributes.end(); ++it) {
1316 1322
	delete it->second;
1317 1323
      }
1318 1324

	
1319 1325
      if (local_is) {
1320 1326
	delete _is;
1321 1327
      }
1322 1328

	
1323 1329
    }
1324 1330

	
1325 1331
  private:
1326 1332
    friend GraphReader<Graph> graphReader<>(std::istream& is, Graph& graph);    
1327 1333
    friend GraphReader<Graph> graphReader<>(const std::string& fn, 
1328 1334
					    Graph& graph);   
1329 1335
    friend GraphReader<Graph> graphReader<>(const char *fn, Graph& graph);    
1330 1336

	
1331 1337
    GraphReader(GraphReader& other) 
1332 1338
      : _is(other._is), local_is(other.local_is), _graph(other._graph),
1333 1339
	_use_nodes(other._use_nodes), _use_edges(other._use_edges),
1334 1340
	_skip_nodes(other._skip_nodes), _skip_edges(other._skip_edges) {
1335 1341

	
1336 1342
      other._is = 0;
1337 1343
      other.local_is = false;
1338 1344
      
1339 1345
      _node_index.swap(other._node_index);
1340 1346
      _edge_index.swap(other._edge_index);
1341 1347

	
1342 1348
      _node_maps.swap(other._node_maps);
1343 1349
      _edge_maps.swap(other._edge_maps);
1344 1350
      _attributes.swap(other._attributes);
1345 1351

	
1346 1352
      _nodes_caption = other._nodes_caption;
1347 1353
      _edges_caption = other._edges_caption;
1348 1354
      _attributes_caption = other._attributes_caption;
1349 1355

	
1350 1356
    }
1351 1357

	
1352 1358
    GraphReader& operator=(const GraphReader&);
1353 1359

	
1354 1360
  public:
1355 1361

	
1356 1362
    /// \name Reading rules
1357 1363
    /// @{
1358 1364
    
1359 1365
    /// \brief Node map reading rule
1360 1366
    ///
1361 1367
    /// Add a node map reading rule to the reader.
1362 1368
    template <typename Map>
1363 1369
    GraphReader& nodeMap(const std::string& caption, Map& map) {
1364 1370
      checkConcept<concepts::WriteMap<Node, typename Map::Value>, Map>();
1365 1371
      _reader_bits::MapStorageBase<Node>* storage = 
1366 1372
	new _reader_bits::MapStorage<Node, Map>(map);
1367 1373
      _node_maps.push_back(std::make_pair(caption, storage));
1368 1374
      return *this;
1369 1375
    }
1370 1376

	
1371 1377
    /// \brief Node map reading rule
1372 1378
    ///
1373 1379
    /// Add a node map reading rule with specialized converter to the
1374 1380
    /// reader.
1375 1381
    template <typename Map, typename Converter>
1376 1382
    GraphReader& nodeMap(const std::string& caption, Map& map, 
1377 1383
			   const Converter& converter = Converter()) {
1378 1384
      checkConcept<concepts::WriteMap<Node, typename Map::Value>, Map>();
1379 1385
      _reader_bits::MapStorageBase<Node>* storage = 
1380 1386
	new _reader_bits::MapStorage<Node, Map, Converter>(map, converter);
1381 1387
      _node_maps.push_back(std::make_pair(caption, storage));
1382 1388
      return *this;
1383 1389
    }
1384 1390

	
1385 1391
    /// \brief Edge map reading rule
1386 1392
    ///
1387 1393
    /// Add an edge map reading rule to the reader.
1388 1394
    template <typename Map>
1389 1395
    GraphReader& edgeMap(const std::string& caption, Map& map) {
1390 1396
      checkConcept<concepts::WriteMap<Edge, typename Map::Value>, Map>();
1391 1397
      _reader_bits::MapStorageBase<Edge>* storage = 
1392 1398
	new _reader_bits::MapStorage<Edge, Map>(map);
1393 1399
      _edge_maps.push_back(std::make_pair(caption, storage));
1394 1400
      return *this;
1395 1401
    }
1396 1402

	
1397 1403
    /// \brief Edge map reading rule
1398 1404
    ///
1399 1405
    /// Add an edge map reading rule with specialized converter to the
1400 1406
    /// reader.
1401 1407
    template <typename Map, typename Converter>
1402 1408
    GraphReader& edgeMap(const std::string& caption, Map& map, 
1403 1409
			  const Converter& converter = Converter()) {
1404 1410
      checkConcept<concepts::WriteMap<Edge, typename Map::Value>, Map>();
1405 1411
      _reader_bits::MapStorageBase<Edge>* storage = 
1406 1412
	new _reader_bits::MapStorage<Edge, Map, Converter>(map, converter);
1407 1413
      _edge_maps.push_back(std::make_pair(caption, storage));
1408 1414
      return *this;
1409 1415
    }
1410 1416

	
1411 1417
    /// \brief Arc map reading rule
1412 1418
    ///
1413 1419
    /// Add an arc map reading rule to the reader.
1414 1420
    template <typename Map>
1415 1421
    GraphReader& arcMap(const std::string& caption, Map& map) {
1416 1422
      checkConcept<concepts::WriteMap<Arc, typename Map::Value>, Map>();
1417 1423
      _reader_bits::MapStorageBase<Edge>* forward_storage = 
1418 1424
	new _reader_bits::GraphArcMapStorage<Graph, true, Map>(_graph, map);
1419 1425
      _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
1420 1426
      _reader_bits::MapStorageBase<Edge>* backward_storage = 
1421 1427
	new _reader_bits::GraphArcMapStorage<Graph, false, Map>(_graph, map);
1422 1428
      _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
1423 1429
      return *this;
1424 1430
    }
1425 1431

	
1426 1432
    /// \brief Arc map reading rule
1427 1433
    ///
1428 1434
    /// Add an arc map reading rule with specialized converter to the
1429 1435
    /// reader.
1430 1436
    template <typename Map, typename Converter>
1431 1437
    GraphReader& arcMap(const std::string& caption, Map& map, 
1432 1438
			  const Converter& converter = Converter()) {
1433 1439
      checkConcept<concepts::WriteMap<Arc, typename Map::Value>, Map>();
1434 1440
      _reader_bits::MapStorageBase<Edge>* forward_storage = 
1435 1441
	new _reader_bits::GraphArcMapStorage<Graph, true, Map, Converter>
1436 1442
	(_graph, map, converter);
1437 1443
      _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
1438 1444
      _reader_bits::MapStorageBase<Edge>* backward_storage = 
1439 1445
	new _reader_bits::GraphArcMapStorage<Graph, false, Map, Converter>
1440 1446
	(_graph, map, converter);
1441 1447
      _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
1442 1448
      return *this;
1443 1449
    }
1444 1450

	
1445 1451
    /// \brief Attribute reading rule
1446 1452
    ///
1447 1453
    /// Add an attribute reading rule to the reader.
1448 1454
    template <typename Value>
1449 1455
    GraphReader& attribute(const std::string& caption, Value& value) {
1450 1456
      _reader_bits::ValueStorageBase* storage = 
1451 1457
	new _reader_bits::ValueStorage<Value>(value);
1452 1458
      _attributes.insert(std::make_pair(caption, storage));
1453 1459
      return *this;
1454 1460
    }
1455 1461

	
1456 1462
    /// \brief Attribute reading rule
1457 1463
    ///
1458 1464
    /// Add an attribute reading rule with specialized converter to the
1459 1465
    /// reader.
1460 1466
    template <typename Value, typename Converter>
1461 1467
    GraphReader& attribute(const std::string& caption, Value& value, 
1462 1468
			     const Converter& converter = Converter()) {
1463 1469
      _reader_bits::ValueStorageBase* storage = 
1464 1470
	new _reader_bits::ValueStorage<Value, Converter>(value, converter);
1465 1471
      _attributes.insert(std::make_pair(caption, storage));
1466 1472
      return *this;
1467 1473
    }
1468 1474

	
1469 1475
    /// \brief Node reading rule
1470 1476
    ///
1471 1477
    /// Add a node reading rule to reader.
1472 1478
    GraphReader& node(const std::string& caption, Node& node) {
1473 1479
      typedef _reader_bits::MapLookUpConverter<Node> Converter;
1474 1480
      Converter converter(_node_index);
1475 1481
      _reader_bits::ValueStorageBase* storage = 
1476 1482
	new _reader_bits::ValueStorage<Node, Converter>(node, converter);
1477 1483
      _attributes.insert(std::make_pair(caption, storage));
1478 1484
      return *this;
1479 1485
    }
1480 1486

	
1481 1487
    /// \brief Edge reading rule
1482 1488
    ///
1483 1489
    /// Add an edge reading rule to reader.
1484 1490
    GraphReader& edge(const std::string& caption, Edge& edge) {
1485 1491
      typedef _reader_bits::MapLookUpConverter<Edge> Converter;
1486 1492
      Converter converter(_edge_index);
1487 1493
      _reader_bits::ValueStorageBase* storage = 
1488 1494
	new _reader_bits::ValueStorage<Edge, Converter>(edge, converter);
1489 1495
      _attributes.insert(std::make_pair(caption, storage));
1490 1496
      return *this;
1491 1497
    }
1492 1498

	
1493 1499
    /// \brief Arc reading rule
1494 1500
    ///
1495 1501
    /// Add an arc reading rule to reader.
1496 1502
    GraphReader& arc(const std::string& caption, Arc& arc) {
1497 1503
      typedef _reader_bits::GraphArcLookUpConverter<Graph> Converter;
1498 1504
      Converter converter(_graph, _edge_index);
1499 1505
      _reader_bits::ValueStorageBase* storage = 
1500 1506
	new _reader_bits::ValueStorage<Arc, Converter>(arc, converter);
1501 1507
      _attributes.insert(std::make_pair(caption, storage));
1502 1508
      return *this;
1503 1509
    }
1504 1510

	
1505 1511
    /// @}
1506 1512

	
1507 1513
    /// \name Select section by name
1508 1514
    /// @{
1509 1515

	
1510 1516
    /// \brief Set \c \@nodes section to be read
1511 1517
    ///
1512 1518
    /// Set \c \@nodes section to be read.
1513 1519
    GraphReader& nodes(const std::string& caption) {
1514 1520
      _nodes_caption = caption;
1515 1521
      return *this;
1516 1522
    }
1517 1523

	
1518 1524
    /// \brief Set \c \@edges section to be read
1519 1525
    ///
1520 1526
    /// Set \c \@edges section to be read.
1521 1527
    GraphReader& edges(const std::string& caption) {
1522 1528
      _edges_caption = caption;
1523 1529
      return *this;
1524 1530
    }
1525 1531

	
1526 1532
    /// \brief Set \c \@attributes section to be read
1527 1533
    ///
1528 1534
    /// Set \c \@attributes section to be read.
1529 1535
    GraphReader& attributes(const std::string& caption) {
1530 1536
      _attributes_caption = caption;
1531 1537
      return *this;
1532 1538
    }
1533 1539

	
1534 1540
    /// @}
1535 1541

	
1536 1542
    /// \name Using previously constructed node or edge set
1537 1543
    /// @{
1538 1544

	
1539 1545
    /// \brief Use previously constructed node set
1540 1546
    ///
1541 1547
    /// Use previously constructed node set, and specify the node
1542 1548
    /// label map.
1543 1549
    template <typename Map>
1544 1550
    GraphReader& useNodes(const Map& map) {
1545 1551
      checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
1546 1552
      LEMON_ASSERT(!_use_nodes, "Multiple usage of useNodes() member"); 
1547 1553
      _use_nodes = true;
1548 1554
      _writer_bits::DefaultConverter<typename Map::Value> converter;
1549 1555
      for (NodeIt n(_graph); n != INVALID; ++n) {
1550 1556
	_node_index.insert(std::make_pair(converter(map[n]), n));
1551 1557
      }
1552 1558
      return *this;
1553 1559
    }
1554 1560

	
1555 1561
    /// \brief Use previously constructed node set
1556 1562
    ///
1557 1563
    /// Use previously constructed node set, and specify the node
1558 1564
    /// label map and a functor which converts the label map values to
1559 1565
    /// \c std::string.
1560 1566
    template <typename Map, typename Converter>
1561 1567
    GraphReader& useNodes(const Map& map, 
1562 1568
			    const Converter& converter = Converter()) {
1563 1569
      checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
1564 1570
      LEMON_ASSERT(!_use_nodes, "Multiple usage of useNodes() member"); 
1565 1571
      _use_nodes = true;
1566 1572
      for (NodeIt n(_graph); n != INVALID; ++n) {
1567 1573
	_node_index.insert(std::make_pair(converter(map[n]), n));
1568 1574
      }
1569 1575
      return *this;
1570 1576
    }
1571 1577

	
1572 1578
    /// \brief Use previously constructed edge set
1573 1579
    ///
1574 1580
    /// Use previously constructed edge set, and specify the edge
1575 1581
    /// label map.
1576 1582
    template <typename Map>
1577 1583
    GraphReader& useEdges(const Map& map) {
1578 1584
      checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
1579 1585
      LEMON_ASSERT(!_use_edges, "Multiple usage of useEdges() member");
1580 1586
      _use_edges = true;
1581 1587
      _writer_bits::DefaultConverter<typename Map::Value> converter;
1582 1588
      for (EdgeIt a(_graph); a != INVALID; ++a) {
1583 1589
	_edge_index.insert(std::make_pair(converter(map[a]), a));
1584 1590
      }
1585 1591
      return *this;
1586 1592
    }
1587 1593

	
1588 1594
    /// \brief Use previously constructed edge set
1589 1595
    ///
1590 1596
    /// Use previously constructed edge set, and specify the edge
1591 1597
    /// label map and a functor which converts the label map values to
1592 1598
    /// \c std::string.
1593 1599
    template <typename Map, typename Converter>
1594 1600
    GraphReader& useEdges(const Map& map, 
1595 1601
			    const Converter& converter = Converter()) {
1596 1602
      checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
1597 1603
      LEMON_ASSERT(!_use_edges, "Multiple usage of useEdges() member"); 
1598 1604
      _use_edges = true;
1599 1605
      for (EdgeIt a(_graph); a != INVALID; ++a) {
1600 1606
	_edge_index.insert(std::make_pair(converter(map[a]), a));
1601 1607
      }
1602 1608
      return *this;
1603 1609
    }
1604 1610

	
1605 1611
    /// \brief Skip the reading of node section
1606 1612
    ///
1607 1613
    /// Omit the reading of the node section. This implies that each node
1608 1614
    /// map reading rule will be abandoned, and the nodes of the graph
1609 1615
    /// will not be constructed, which usually cause that the edge set
1610 1616
    /// could not be read due to lack of node name
1611 1617
    /// could not be read due to lack of node name resolving.
1612 1618
    /// Therefore \c skipEdges() function should also be used, or
Ignore white space 768 line context
... ...
@@ -533,768 +533,774 @@
533 533
			  const Converter& converter = Converter()) {
534 534
      checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
535 535
      _writer_bits::MapStorageBase<Arc>* storage = 
536 536
	new _writer_bits::MapStorage<Arc, Map, Converter>(map, converter);
537 537
      _arc_maps.push_back(std::make_pair(caption, storage));
538 538
      return *this;
539 539
    }
540 540

	
541 541
    /// \brief Attribute writing rule
542 542
    ///
543 543
    /// Add an attribute writing rule to the writer.
544 544
    template <typename Value>
545 545
    DigraphWriter& attribute(const std::string& caption, const Value& value) {
546 546
      _writer_bits::ValueStorageBase* storage = 
547 547
	new _writer_bits::ValueStorage<Value>(value);
548 548
      _attributes.push_back(std::make_pair(caption, storage));
549 549
      return *this;
550 550
    }
551 551

	
552 552
    /// \brief Attribute writing rule
553 553
    ///
554 554
    /// Add an attribute writing rule with specialized converter to the
555 555
    /// writer.
556 556
    template <typename Value, typename Converter>
557 557
    DigraphWriter& attribute(const std::string& caption, const Value& value, 
558 558
			     const Converter& converter = Converter()) {
559 559
      _writer_bits::ValueStorageBase* storage = 
560 560
	new _writer_bits::ValueStorage<Value, Converter>(value, converter);
561 561
      _attributes.push_back(std::make_pair(caption, storage));
562 562
      return *this;
563 563
    }
564 564

	
565 565
    /// \brief Node writing rule
566 566
    ///
567 567
    /// Add a node writing rule to the writer.
568 568
    DigraphWriter& node(const std::string& caption, const Node& node) {
569 569
      typedef _writer_bits::MapLookUpConverter<Node> Converter;
570 570
      Converter converter(_node_index);
571 571
      _writer_bits::ValueStorageBase* storage = 
572 572
	new _writer_bits::ValueStorage<Node, Converter>(node, converter);
573 573
      _attributes.push_back(std::make_pair(caption, storage));
574 574
      return *this;
575 575
    }
576 576

	
577 577
    /// \brief Arc writing rule
578 578
    ///
579 579
    /// Add an arc writing rule to writer.
580 580
    DigraphWriter& arc(const std::string& caption, const Arc& arc) {
581 581
      typedef _writer_bits::MapLookUpConverter<Arc> Converter;
582 582
      Converter converter(_arc_index);
583 583
      _writer_bits::ValueStorageBase* storage = 
584 584
	new _writer_bits::ValueStorage<Arc, Converter>(arc, converter);
585 585
      _attributes.push_back(std::make_pair(caption, storage));
586 586
      return *this;
587 587
    }
588 588

	
589 589
    /// \name Section captions
590 590
    /// @{
591 591

	
592 592
    /// \brief Add an additional caption to the \c \@nodes section
593 593
    ///
594 594
    /// Add an additional caption to the \c \@nodes section.
595 595
    DigraphWriter& nodes(const std::string& caption) {
596 596
      _nodes_caption = caption;
597 597
      return *this;
598 598
    }
599 599

	
600 600
    /// \brief Add an additional caption to the \c \@arcs section
601 601
    ///
602 602
    /// Add an additional caption to the \c \@arcs section.
603 603
    DigraphWriter& arcs(const std::string& caption) {
604 604
      _arcs_caption = caption;
605 605
      return *this;
606 606
    }
607 607

	
608 608
    /// \brief Add an additional caption to the \c \@attributes section
609 609
    ///
610 610
    /// Add an additional caption to the \c \@attributes section.
611 611
    DigraphWriter& attributes(const std::string& caption) {
612 612
      _attributes_caption = caption;
613 613
      return *this;
614 614
    }
615 615

	
616 616
    /// \name Skipping section
617 617
    /// @{
618 618

	
619 619
    /// \brief Skip writing the node set
620 620
    ///
621 621
    /// The \c \@nodes section will not be written to the stream.
622 622
    DigraphWriter& skipNodes() {
623 623
      LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member");
624 624
      _skip_nodes = true;
625 625
      return *this;
626 626
    }
627 627

	
628 628
    /// \brief Skip writing arc set
629 629
    ///
630 630
    /// The \c \@arcs section will not be written to the stream.
631 631
    DigraphWriter& skipArcs() {
632 632
      LEMON_ASSERT(!_skip_arcs, "Multiple usage of skipArcs() member");
633 633
      _skip_arcs = true;
634 634
      return *this;
635 635
    }
636 636

	
637 637
    /// @}
638 638

	
639 639
  private:
640 640

	
641 641
    void writeNodes() {
642 642
      _writer_bits::MapStorageBase<Node>* label = 0;
643 643
      for (typename NodeMaps::iterator it = _node_maps.begin();
644 644
	   it != _node_maps.end(); ++it) {
645 645
        if (it->first == "label") {
646 646
	  label = it->second;
647 647
	  break;
648 648
	}
649 649
      }
650 650

	
651 651
      *_os << "@nodes";
652 652
      if (!_nodes_caption.empty()) {
653 653
	_writer_bits::writeToken(*_os << ' ', _nodes_caption);
654 654
      }
655 655
      *_os << std::endl;
656 656

	
657 657
      if (label == 0) {
658 658
	*_os << "label" << '\t';
659 659
      }
660 660
      for (typename NodeMaps::iterator it = _node_maps.begin();
661 661
	   it != _node_maps.end(); ++it) {
662 662
	_writer_bits::writeToken(*_os, it->first) << '\t';
663 663
      }
664 664
      *_os << std::endl;
665 665

	
666 666
      std::vector<Node> nodes;
667 667
      for (NodeIt n(_digraph); n != INVALID; ++n) {
668 668
	nodes.push_back(n);
669 669
      }
670 670
      
671 671
      if (label == 0) {
672 672
	IdMap<Digraph, Node> id_map(_digraph);
673 673
	_writer_bits::MapLess<IdMap<Digraph, Node> > id_less(id_map);
674 674
	std::sort(nodes.begin(), nodes.end(), id_less);
675 675
      } else {
676 676
	label->sort(nodes);
677 677
      }
678 678

	
679 679
      for (int i = 0; i < static_cast<int>(nodes.size()); ++i) {
680 680
	Node n = nodes[i];
681 681
	if (label == 0) {
682 682
	  std::ostringstream os;
683 683
	  os << _digraph.id(n);
684 684
	  _writer_bits::writeToken(*_os, os.str());
685 685
	  *_os << '\t';
686 686
	  _node_index.insert(std::make_pair(n, os.str()));
687 687
	}
688 688
	for (typename NodeMaps::iterator it = _node_maps.begin();
689 689
	     it != _node_maps.end(); ++it) {
690 690
	  std::string value = it->second->get(n);
691 691
	  _writer_bits::writeToken(*_os, value);
692 692
	  if (it->first == "label") {
693 693
	    _node_index.insert(std::make_pair(n, value));
694 694
	  }
695 695
	  *_os << '\t';
696 696
	}
697 697
	*_os << std::endl;
698 698
      }
699 699
    }
700 700

	
701 701
    void createNodeIndex() {
702 702
      _writer_bits::MapStorageBase<Node>* label = 0;
703 703
      for (typename NodeMaps::iterator it = _node_maps.begin();
704 704
	   it != _node_maps.end(); ++it) {
705 705
        if (it->first == "label") {
706 706
	  label = it->second;
707 707
	  break;
708 708
	}
709 709
      }
710 710

	
711 711
      if (label == 0) {
712 712
	for (NodeIt n(_digraph); n != INVALID; ++n) {
713 713
	  std::ostringstream os;
714 714
	  os << _digraph.id(n);
715 715
	  _node_index.insert(std::make_pair(n, os.str()));	  
716 716
	}	
717 717
      } else {
718 718
	for (NodeIt n(_digraph); n != INVALID; ++n) {
719 719
	  std::string value = label->get(n);	  
720 720
	  _node_index.insert(std::make_pair(n, value));
721 721
	}
722 722
      }
723 723
    }
724 724

	
725 725
    void writeArcs() {
726 726
      _writer_bits::MapStorageBase<Arc>* label = 0;
727 727
      for (typename ArcMaps::iterator it = _arc_maps.begin();
728 728
	   it != _arc_maps.end(); ++it) {
729 729
        if (it->first == "label") {
730 730
	  label = it->second;
731 731
	  break;
732 732
	}
733 733
      }
734 734

	
735 735
      *_os << "@arcs";
736 736
      if (!_arcs_caption.empty()) {
737 737
	_writer_bits::writeToken(*_os << ' ', _arcs_caption);
738 738
      }
739 739
      *_os << std::endl;
740 740

	
741 741
      *_os << '\t' << '\t';
742 742
      if (label == 0) {
743 743
	*_os << "label" << '\t';
744 744
      }
745 745
      for (typename ArcMaps::iterator it = _arc_maps.begin();
746 746
	   it != _arc_maps.end(); ++it) {
747 747
	_writer_bits::writeToken(*_os, it->first) << '\t';
748 748
      }
749 749
      *_os << std::endl;
750 750

	
751 751
      std::vector<Arc> arcs;
752 752
      for (ArcIt n(_digraph); n != INVALID; ++n) {
753 753
	arcs.push_back(n);
754 754
      }
755 755
      
756 756
      if (label == 0) {
757 757
	IdMap<Digraph, Arc> id_map(_digraph);
758 758
	_writer_bits::MapLess<IdMap<Digraph, Arc> > id_less(id_map);
759 759
	std::sort(arcs.begin(), arcs.end(), id_less);
760 760
      } else {
761 761
	label->sort(arcs);
762 762
      }
763 763

	
764 764
      for (int i = 0; i < static_cast<int>(arcs.size()); ++i) {
765 765
	Arc a = arcs[i];
766 766
	_writer_bits::writeToken(*_os, _node_index.
767 767
				 find(_digraph.source(a))->second);
768 768
	*_os << '\t';
769 769
	_writer_bits::writeToken(*_os, _node_index.
770 770
				 find(_digraph.target(a))->second);
771 771
	*_os << '\t';
772 772
	if (label == 0) {
773 773
	  std::ostringstream os;
774 774
	  os << _digraph.id(a);
775 775
	  _writer_bits::writeToken(*_os, os.str());
776 776
	  *_os << '\t';
777 777
	  _arc_index.insert(std::make_pair(a, os.str()));
778 778
	}
779 779
	for (typename ArcMaps::iterator it = _arc_maps.begin();
780 780
	     it != _arc_maps.end(); ++it) {
781 781
	  std::string value = it->second->get(a);
782 782
	  _writer_bits::writeToken(*_os, value);
783 783
	  if (it->first == "label") {
784 784
	    _arc_index.insert(std::make_pair(a, value));
785 785
	  }
786 786
	  *_os << '\t';
787 787
	}
788 788
	*_os << std::endl;
789 789
      }
790 790
    }
791 791

	
792 792
    void createArcIndex() {
793 793
      _writer_bits::MapStorageBase<Arc>* label = 0;
794 794
      for (typename ArcMaps::iterator it = _arc_maps.begin();
795 795
	   it != _arc_maps.end(); ++it) {
796 796
        if (it->first == "label") {
797 797
	  label = it->second;
798 798
	  break;
799 799
	}
800 800
      }
801 801

	
802 802
      if (label == 0) {
803 803
	for (ArcIt a(_digraph); a != INVALID; ++a) {
804 804
	  std::ostringstream os;
805 805
	  os << _digraph.id(a);
806 806
	  _arc_index.insert(std::make_pair(a, os.str()));	  
807 807
	}	
808 808
      } else {
809 809
	for (ArcIt a(_digraph); a != INVALID; ++a) {
810 810
	  std::string value = label->get(a);	  
811 811
	  _arc_index.insert(std::make_pair(a, value));
812 812
	}
813 813
      }
814 814
    }
815 815

	
816 816
    void writeAttributes() {
817 817
      if (_attributes.empty()) return;
818 818
      *_os << "@attributes";
819 819
      if (!_attributes_caption.empty()) {
820 820
	_writer_bits::writeToken(*_os << ' ', _attributes_caption);
821 821
      }
822 822
      *_os << std::endl;
823 823
      for (typename Attributes::iterator it = _attributes.begin();
824 824
	   it != _attributes.end(); ++it) {
825 825
	_writer_bits::writeToken(*_os, it->first) << ' ';
826 826
	_writer_bits::writeToken(*_os, it->second->get());
827 827
	*_os << std::endl;
828 828
      }
829 829
    }
830 830
    
831 831
  public:
832 832
    
833 833
    /// \name Execution of the writer    
834 834
    /// @{
835 835

	
836 836
    /// \brief Start the batch processing
837 837
    ///
838 838
    /// This function starts the batch processing.
839 839
    void run() {
840 840
      if (!_skip_nodes) {
841 841
	writeNodes();
842 842
      } else {
843 843
	createNodeIndex();
844 844
      }
845 845
      if (!_skip_arcs) {      
846 846
	writeArcs();
847 847
      } else {
848 848
	createArcIndex();
849 849
      }
850 850
      writeAttributes();
851 851
    }
852 852

	
853 853
    /// \brief Give back the stream of the writer
854 854
    ///
855 855
    /// Give back the stream of the writer.
856 856
    std::ostream& ostream() {
857 857
      return *_os;
858 858
    }
859 859

	
860 860
    /// @}
861 861
  };
862 862

	
863 863
  /// \brief Return a \ref DigraphWriter class
864 864
  /// 
865 865
  /// This function just returns a \ref DigraphWriter class.
866 866
  /// \relates DigraphWriter
867 867
  template <typename Digraph>
868 868
  DigraphWriter<Digraph> digraphWriter(std::ostream& os, 
869 869
				       const Digraph& digraph) {
870 870
    DigraphWriter<Digraph> tmp(os, digraph);
871 871
    return tmp;
872 872
  }
873 873

	
874 874
  /// \brief Return a \ref DigraphWriter class
875 875
  /// 
876 876
  /// This function just returns a \ref DigraphWriter class.
877 877
  /// \relates DigraphWriter
878 878
  template <typename Digraph>
879 879
  DigraphWriter<Digraph> digraphWriter(const std::string& fn, 
880 880
				       const Digraph& digraph) {
881 881
    DigraphWriter<Digraph> tmp(fn, digraph);
882 882
    return tmp;
883 883
  }
884 884

	
885 885
  /// \brief Return a \ref DigraphWriter class
886 886
  /// 
887 887
  /// This function just returns a \ref DigraphWriter class.
888 888
  /// \relates DigraphWriter
889 889
  template <typename Digraph>
890 890
  DigraphWriter<Digraph> digraphWriter(const char* fn, 
891 891
				       const Digraph& digraph) {
892 892
    DigraphWriter<Digraph> tmp(fn, digraph);
893 893
    return tmp;
894 894
  }
895 895

	
896 896
  template <typename Graph>
897 897
  class GraphWriter;
898 898

	
899 899
  template <typename Graph>
900 900
  GraphWriter<Graph> graphWriter(std::ostream& os, const Graph& graph);    
901 901

	
902 902
  template <typename Graph>
903 903
  GraphWriter<Graph> graphWriter(const std::string& fn, const Graph& graph);   
904 904

	
905 905
  template <typename Graph>
906 906
  GraphWriter<Graph> graphWriter(const char *fn, const Graph& graph);    
907 907

	
908 908
  /// \ingroup lemon_io
909 909
  ///  
910 910
  /// \brief \ref lgf-format "LGF" writer for directed graphs
911 911
  ///
912 912
  /// This utility writes an \ref lgf-format "LGF" file.
913 913
  ///
914 914
  /// It can be used almost the same way as \c DigraphWriter.
915 915
  /// The only difference is that this class can handle edges and
916 916
  /// edge maps as well as arcs and arc maps.
917
  ///
918
  /// The arc maps are written into the file as two columns, the
919
  /// caption of the columns are the name of the map prefixed with \c
920
  /// '+' and \c '-'. The arcs are written into the \c \@attributes
921
  /// section as a \c '+' or a \c '-' prefix (depends on the direction
922
  /// of the arc) and the label of corresponding edge.
917 923
  template <typename _Graph>
918 924
  class GraphWriter {
919 925
  public:
920 926

	
921 927
    typedef _Graph Graph;
922 928
    TEMPLATE_GRAPH_TYPEDEFS(Graph);
923 929
    
924 930
  private:
925 931

	
926 932

	
927 933
    std::ostream* _os;
928 934
    bool local_os;
929 935

	
930 936
    Graph& _graph;
931 937

	
932 938
    std::string _nodes_caption;
933 939
    std::string _edges_caption;
934 940
    std::string _attributes_caption;
935 941
    
936 942
    typedef std::map<Node, std::string> NodeIndex;
937 943
    NodeIndex _node_index;
938 944
    typedef std::map<Edge, std::string> EdgeIndex;
939 945
    EdgeIndex _edge_index;
940 946

	
941 947
    typedef std::vector<std::pair<std::string, 
942 948
      _writer_bits::MapStorageBase<Node>* > > NodeMaps;    
943 949
    NodeMaps _node_maps; 
944 950

	
945 951
    typedef std::vector<std::pair<std::string, 
946 952
      _writer_bits::MapStorageBase<Edge>* > >EdgeMaps;
947 953
    EdgeMaps _edge_maps;
948 954

	
949 955
    typedef std::vector<std::pair<std::string, 
950 956
      _writer_bits::ValueStorageBase*> > Attributes;
951 957
    Attributes _attributes;
952 958

	
953 959
    bool _skip_nodes;
954 960
    bool _skip_edges;
955 961

	
956 962
  public:
957 963

	
958 964
    /// \brief Constructor
959 965
    ///
960 966
    /// Construct a directed graph writer, which writes to the given
961 967
    /// output stream.
962 968
    GraphWriter(std::ostream& is, const Graph& graph) 
963 969
      : _os(&is), local_os(false), _graph(graph),
964 970
	_skip_nodes(false), _skip_edges(false) {}
965 971

	
966 972
    /// \brief Constructor
967 973
    ///
968 974
    /// Construct a directed graph writer, which writes to the given
969 975
    /// output file.
970 976
    GraphWriter(const std::string& fn, const Graph& graph) 
971 977
      : _os(new std::ofstream(fn.c_str())), local_os(true), _graph(graph),
972 978
	_skip_nodes(false), _skip_edges(false) {}
973 979

	
974 980
    /// \brief Constructor
975 981
    ///
976 982
    /// Construct a directed graph writer, which writes to the given
977 983
    /// output file.
978 984
    GraphWriter(const char* fn, const Graph& graph) 
979 985
      : _os(new std::ofstream(fn)), local_os(true), _graph(graph),
980 986
	_skip_nodes(false), _skip_edges(false) {}
981 987

	
982 988
    /// \brief Destructor
983 989
    ~GraphWriter() {
984 990
      for (typename NodeMaps::iterator it = _node_maps.begin(); 
985 991
	   it != _node_maps.end(); ++it) {
986 992
	delete it->second;
987 993
      }
988 994

	
989 995
      for (typename EdgeMaps::iterator it = _edge_maps.begin(); 
990 996
	   it != _edge_maps.end(); ++it) {
991 997
	delete it->second;
992 998
      }
993 999

	
994 1000
      for (typename Attributes::iterator it = _attributes.begin(); 
995 1001
	   it != _attributes.end(); ++it) {
996 1002
	delete it->second;
997 1003
      }
998 1004

	
999 1005
      if (local_os) {
1000 1006
	delete _os;
1001 1007
      }
1002 1008
    }
1003 1009
    
1004 1010
  private:
1005 1011

	
1006 1012
    friend GraphWriter<Graph> graphWriter<>(std::ostream& os, 
1007 1013
					    const Graph& graph);    
1008 1014
    friend GraphWriter<Graph> graphWriter<>(const std::string& fn, 
1009 1015
					    const Graph& graph);   
1010 1016
    friend GraphWriter<Graph> graphWriter<>(const char *fn, 
1011 1017
					    const Graph& graph);    
1012 1018

	
1013 1019
    GraphWriter(GraphWriter& other) 
1014 1020
      : _os(other._os), local_os(other.local_os), _graph(other._graph),
1015 1021
	_skip_nodes(other._skip_nodes), _skip_edges(other._skip_edges) {
1016 1022

	
1017 1023
      other._os = 0;
1018 1024
      other.local_os = false;
1019 1025

	
1020 1026
      _node_index.swap(other._node_index);
1021 1027
      _edge_index.swap(other._edge_index);
1022 1028

	
1023 1029
      _node_maps.swap(other._node_maps);
1024 1030
      _edge_maps.swap(other._edge_maps);
1025 1031
      _attributes.swap(other._attributes);
1026 1032

	
1027 1033
      _nodes_caption = other._nodes_caption;
1028 1034
      _edges_caption = other._edges_caption;
1029 1035
      _attributes_caption = other._attributes_caption;
1030 1036
    }
1031 1037

	
1032 1038
    GraphWriter& operator=(const GraphWriter&);
1033 1039

	
1034 1040
  public:
1035 1041

	
1036 1042
    /// \name Writing rules
1037 1043
    /// @{
1038 1044
    
1039 1045
    /// \brief Node map writing rule
1040 1046
    ///
1041 1047
    /// Add a node map writing rule to the writer.
1042 1048
    template <typename Map>
1043 1049
    GraphWriter& nodeMap(const std::string& caption, const Map& map) {
1044 1050
      checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
1045 1051
      _writer_bits::MapStorageBase<Node>* storage = 
1046 1052
	new _writer_bits::MapStorage<Node, Map>(map);
1047 1053
      _node_maps.push_back(std::make_pair(caption, storage));
1048 1054
      return *this;
1049 1055
    }
1050 1056

	
1051 1057
    /// \brief Node map writing rule
1052 1058
    ///
1053 1059
    /// Add a node map writing rule with specialized converter to the
1054 1060
    /// writer.
1055 1061
    template <typename Map, typename Converter>
1056 1062
    GraphWriter& nodeMap(const std::string& caption, const Map& map, 
1057 1063
			   const Converter& converter = Converter()) {
1058 1064
      checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
1059 1065
      _writer_bits::MapStorageBase<Node>* storage = 
1060 1066
	new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
1061 1067
      _node_maps.push_back(std::make_pair(caption, storage));
1062 1068
      return *this;
1063 1069
    }
1064 1070

	
1065 1071
    /// \brief Edge map writing rule
1066 1072
    ///
1067 1073
    /// Add an edge map writing rule to the writer.
1068 1074
    template <typename Map>
1069 1075
    GraphWriter& edgeMap(const std::string& caption, const Map& map) {
1070 1076
      checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
1071 1077
      _writer_bits::MapStorageBase<Edge>* storage = 
1072 1078
	new _writer_bits::MapStorage<Edge, Map>(map);
1073 1079
      _edge_maps.push_back(std::make_pair(caption, storage));
1074 1080
      return *this;
1075 1081
    }
1076 1082

	
1077 1083
    /// \brief Edge map writing rule
1078 1084
    ///
1079 1085
    /// Add an edge map writing rule with specialized converter to the
1080 1086
    /// writer.
1081 1087
    template <typename Map, typename Converter>
1082 1088
    GraphWriter& edgeMap(const std::string& caption, const Map& map, 
1083 1089
			  const Converter& converter = Converter()) {
1084 1090
      checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
1085 1091
      _writer_bits::MapStorageBase<Edge>* storage = 
1086 1092
	new _writer_bits::MapStorage<Edge, Map, Converter>(map, converter);
1087 1093
      _edge_maps.push_back(std::make_pair(caption, storage));
1088 1094
      return *this;
1089 1095
    }
1090 1096

	
1091 1097
    /// \brief Arc map writing rule
1092 1098
    ///
1093 1099
    /// Add an arc map writing rule to the writer.
1094 1100
    template <typename Map>
1095 1101
    GraphWriter& arcMap(const std::string& caption, const Map& map) {
1096 1102
      checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
1097 1103
      _writer_bits::MapStorageBase<Edge>* forward_storage = 
1098 1104
	new _writer_bits::GraphArcMapStorage<Graph, true, Map>(_graph, map);
1099 1105
      _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
1100 1106
      _writer_bits::MapStorageBase<Edge>* backward_storage = 
1101 1107
	new _writer_bits::GraphArcMapStorage<Graph, false, Map>(_graph, map);
1102 1108
      _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
1103 1109
      return *this;
1104 1110
    }
1105 1111

	
1106 1112
    /// \brief Arc map writing rule
1107 1113
    ///
1108 1114
    /// Add an arc map writing rule with specialized converter to the
1109 1115
    /// writer.
1110 1116
    template <typename Map, typename Converter>
1111 1117
    GraphWriter& arcMap(const std::string& caption, const Map& map, 
1112 1118
			  const Converter& converter = Converter()) {
1113 1119
      checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
1114 1120
      _writer_bits::MapStorageBase<Edge>* forward_storage = 
1115 1121
	new _writer_bits::GraphArcMapStorage<Graph, true, Map, Converter>
1116 1122
	(_graph, map, converter);
1117 1123
      _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
1118 1124
      _writer_bits::MapStorageBase<Edge>* backward_storage = 
1119 1125
	new _writer_bits::GraphArcMapStorage<Graph, false, Map, Converter>
1120 1126
	(_graph, map, converter);
1121 1127
      _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
1122 1128
      return *this;
1123 1129
    }
1124 1130

	
1125 1131
    /// \brief Attribute writing rule
1126 1132
    ///
1127 1133
    /// Add an attribute writing rule to the writer.
1128 1134
    template <typename Value>
1129 1135
    GraphWriter& attribute(const std::string& caption, const Value& value) {
1130 1136
      _writer_bits::ValueStorageBase* storage = 
1131 1137
	new _writer_bits::ValueStorage<Value>(value);
1132 1138
      _attributes.push_back(std::make_pair(caption, storage));
1133 1139
      return *this;
1134 1140
    }
1135 1141

	
1136 1142
    /// \brief Attribute writing rule
1137 1143
    ///
1138 1144
    /// Add an attribute writing rule with specialized converter to the
1139 1145
    /// writer.
1140 1146
    template <typename Value, typename Converter>
1141 1147
    GraphWriter& attribute(const std::string& caption, const Value& value, 
1142 1148
			     const Converter& converter = Converter()) {
1143 1149
      _writer_bits::ValueStorageBase* storage = 
1144 1150
	new _writer_bits::ValueStorage<Value, Converter>(value, converter);
1145 1151
      _attributes.push_back(std::make_pair(caption, storage));
1146 1152
      return *this;
1147 1153
    }
1148 1154

	
1149 1155
    /// \brief Node writing rule
1150 1156
    ///
1151 1157
    /// Add a node writing rule to the writer.
1152 1158
    GraphWriter& node(const std::string& caption, const Node& node) {
1153 1159
      typedef _writer_bits::MapLookUpConverter<Node> Converter;
1154 1160
      Converter converter(_node_index);
1155 1161
      _writer_bits::ValueStorageBase* storage = 
1156 1162
	new _writer_bits::ValueStorage<Node, Converter>(node, converter);
1157 1163
      _attributes.push_back(std::make_pair(caption, storage));
1158 1164
      return *this;
1159 1165
    }
1160 1166

	
1161 1167
    /// \brief Edge writing rule
1162 1168
    ///
1163 1169
    /// Add an edge writing rule to writer.
1164 1170
    GraphWriter& edge(const std::string& caption, const Edge& edge) {
1165 1171
      typedef _writer_bits::MapLookUpConverter<Edge> Converter;
1166 1172
      Converter converter(_edge_index);
1167 1173
      _writer_bits::ValueStorageBase* storage = 
1168 1174
	new _writer_bits::ValueStorage<Edge, Converter>(edge, converter);
1169 1175
      _attributes.push_back(std::make_pair(caption, storage));
1170 1176
      return *this;
1171 1177
    }
1172 1178

	
1173 1179
    /// \brief Arc writing rule
1174 1180
    ///
1175 1181
    /// Add an arc writing rule to writer.
1176 1182
    GraphWriter& arc(const std::string& caption, const Arc& arc) {
1177 1183
      typedef _writer_bits::GraphArcLookUpConverter<Graph> Converter;
1178 1184
      Converter converter(_graph, _edge_index);
1179 1185
      _writer_bits::ValueStorageBase* storage = 
1180 1186
	new _writer_bits::ValueStorage<Arc, Converter>(arc, converter);
1181 1187
      _attributes.push_back(std::make_pair(caption, storage));
1182 1188
      return *this;
1183 1189
    }
1184 1190

	
1185 1191
    /// \name Section captions
1186 1192
    /// @{
1187 1193

	
1188 1194
    /// \brief Add an additional caption to the \c \@nodes section
1189 1195
    ///
1190 1196
    /// Add an additional caption to the \c \@nodes section.
1191 1197
    GraphWriter& nodes(const std::string& caption) {
1192 1198
      _nodes_caption = caption;
1193 1199
      return *this;
1194 1200
    }
1195 1201

	
1196 1202
    /// \brief Add an additional caption to the \c \@arcs section
1197 1203
    ///
1198 1204
    /// Add an additional caption to the \c \@arcs section.
1199 1205
    GraphWriter& edges(const std::string& caption) {
1200 1206
      _edges_caption = caption;
1201 1207
      return *this;
1202 1208
    }
1203 1209

	
1204 1210
    /// \brief Add an additional caption to the \c \@attributes section
1205 1211
    ///
1206 1212
    /// Add an additional caption to the \c \@attributes section.
1207 1213
    GraphWriter& attributes(const std::string& caption) {
1208 1214
      _attributes_caption = caption;
1209 1215
      return *this;
1210 1216
    }
1211 1217

	
1212 1218
    /// \name Skipping section
1213 1219
    /// @{
1214 1220

	
1215 1221
    /// \brief Skip writing the node set
1216 1222
    ///
1217 1223
    /// The \c \@nodes section will not be written to the stream.
1218 1224
    GraphWriter& skipNodes() {
1219 1225
      LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member");
1220 1226
      _skip_nodes = true;
1221 1227
      return *this;
1222 1228
    }
1223 1229

	
1224 1230
    /// \brief Skip writing edge set
1225 1231
    ///
1226 1232
    /// The \c \@edges section will not be written to the stream.
1227 1233
    GraphWriter& skipEdges() {
1228 1234
      LEMON_ASSERT(!_skip_edges, "Multiple usage of skipEdges() member");
1229 1235
      _skip_edges = true;
1230 1236
      return *this;
1231 1237
    }
1232 1238

	
1233 1239
    /// @}
1234 1240

	
1235 1241
  private:
1236 1242

	
1237 1243
    void writeNodes() {
1238 1244
      _writer_bits::MapStorageBase<Node>* label = 0;
1239 1245
      for (typename NodeMaps::iterator it = _node_maps.begin();
1240 1246
	   it != _node_maps.end(); ++it) {
1241 1247
        if (it->first == "label") {
1242 1248
	  label = it->second;
1243 1249
	  break;
1244 1250
	}
1245 1251
      }
1246 1252

	
1247 1253
      *_os << "@nodes";
1248 1254
      if (!_nodes_caption.empty()) {
1249 1255
	_writer_bits::writeToken(*_os << ' ', _nodes_caption);
1250 1256
      }
1251 1257
      *_os << std::endl;
1252 1258

	
1253 1259
      if (label == 0) {
1254 1260
	*_os << "label" << '\t';
1255 1261
      }
1256 1262
      for (typename NodeMaps::iterator it = _node_maps.begin();
1257 1263
	   it != _node_maps.end(); ++it) {
1258 1264
	_writer_bits::writeToken(*_os, it->first) << '\t';
1259 1265
      }
1260 1266
      *_os << std::endl;
1261 1267

	
1262 1268
      std::vector<Node> nodes;
1263 1269
      for (NodeIt n(_graph); n != INVALID; ++n) {
1264 1270
	nodes.push_back(n);
1265 1271
      }
1266 1272
      
1267 1273
      if (label == 0) {
1268 1274
	IdMap<Graph, Node> id_map(_graph);
1269 1275
	_writer_bits::MapLess<IdMap<Graph, Node> > id_less(id_map);
1270 1276
	std::sort(nodes.begin(), nodes.end(), id_less);
1271 1277
      } else {
1272 1278
	label->sort(nodes);
1273 1279
      }
1274 1280

	
1275 1281
      for (int i = 0; i < static_cast<int>(nodes.size()); ++i) {
1276 1282
	Node n = nodes[i];
1277 1283
	if (label == 0) {
1278 1284
	  std::ostringstream os;
1279 1285
	  os << _graph.id(n);
1280 1286
	  _writer_bits::writeToken(*_os, os.str());
1281 1287
	  *_os << '\t';
1282 1288
	  _node_index.insert(std::make_pair(n, os.str()));
1283 1289
	}
1284 1290
	for (typename NodeMaps::iterator it = _node_maps.begin();
1285 1291
	     it != _node_maps.end(); ++it) {
1286 1292
	  std::string value = it->second->get(n);
1287 1293
	  _writer_bits::writeToken(*_os, value);
1288 1294
	  if (it->first == "label") {
1289 1295
	    _node_index.insert(std::make_pair(n, value));
1290 1296
	  }
1291 1297
	  *_os << '\t';
1292 1298
	}
1293 1299
	*_os << std::endl;
1294 1300
      }
1295 1301
    }
1296 1302

	
1297 1303
    void createNodeIndex() {
1298 1304
      _writer_bits::MapStorageBase<Node>* label = 0;
1299 1305
      for (typename NodeMaps::iterator it = _node_maps.begin();
1300 1306
	   it != _node_maps.end(); ++it) {
0 comments (0 inline)