doc/graph_io.dox
author deba
Mon, 12 Sep 2005 11:24:54 +0000
changeset 1681 84e43c7ca1e3
parent 1540 7d028a73d7f2
child 1788 614ce2dd3cba
permissions -rw-r--r--
SubGraphAdaptors with edge checking functionality.

Improved grid_graph_demo
alpar@1118
     1
namespace lemon {
deba@1114
     2
/*!
deba@1114
     3
deba@1114
     4
deba@1114
     5
\page graph-io-page Graph Input-Output
deba@1114
     6
athos@1540
     7
The standard graph IO enables one to store graphs and additional maps
athos@1540
     8
(i.e. functions on the nodes or edges) in a flexible and efficient way. 
athos@1540
     9
Before you read this page you should be familiar with LEMON 
athos@1540
    10
\ref graphs "graphs" and \ref maps-page "maps".
deba@1114
    11
deba@1114
    12
\section format The general file format
deba@1114
    13
deba@1532
    14
The file contains sections in the following order:
deba@1114
    15
deba@1114
    16
\li nodeset
deba@1114
    17
\li edgeset
deba@1114
    18
\li nodes
deba@1114
    19
\li edges
deba@1532
    20
\li attributes
deba@1114
    21
athos@1540
    22
Some of these sections can be omitted, but you will basicly need the nodeset
athos@1540
    23
section (unless your graph has no nodes at all) and the edgeset section
athos@1540
    24
(unless your graph has no edges at all). 
athos@1540
    25
athos@1540
    26
The nodeset section describes the nodes of your graph: it identifies the nodes
athos@1540
    27
and gives the maps defined on them, if any. It starts with the
athos@1540
    28
following line:
athos@1522
    29
athos@1522
    30
<tt>\@nodeset</tt>
athos@1522
    31
athos@1522
    32
The next line contains the names of the nodemaps, separated by whitespaces.  Each
athos@1522
    33
following line describes a node in the graph: it contains the values of the
athos@1522
    34
maps in the right order. The map named "id" should contain unique values
athos@1540
    35
because it is regarded as an ID-map. These ids need not be numbers but they
athos@1540
    36
must identify the nodes uniquely for later reference. For example:
deba@1114
    37
deba@1114
    38
\code
deba@1114
    39
@nodeset
deba@1114
    40
id  x-coord  y-coord  color
deba@1114
    41
3   1.0      4.0      blue
deba@1114
    42
5   2.3      5.7      red
deba@1114
    43
12  7.8      2.3      green
deba@1114
    44
\endcode
deba@1114
    45
deba@1114
    46
The edgeset section is very similar to the nodeset section, it has
athos@1522
    47
the same coloumn oriented structure. It starts with the line 
athos@1522
    48
athos@1522
    49
<tt>\@edgeset</tt>
athos@1522
    50
athos@1540
    51
The next line contains the whitespace separated list of names of the edge
athos@1540
    52
maps.  Each of the next lines describes one edge. The first two elements in
athos@1540
    53
the line are the IDs of the source and target (or tail and head) nodes of the
athos@1540
    54
edge as they occur in the ID node map of the nodeset section. You can also
athos@1540
    55
have an optional ID map on the edges for later reference (which has to be
athos@1540
    56
unique in this case).
deba@1114
    57
deba@1114
    58
\code
deba@1114
    59
@edgeset
deba@1114
    60
             id    weight   label
deba@1114
    61
3   5        a     4.3      a-edge
deba@1114
    62
5   12       c     2.6      c-edge
deba@1114
    63
3   12       g     3.4      g-edge
deba@1114
    64
\endcode
deba@1114
    65
athos@1540
    66
The \e nodes section contains <em>labeled (distinguished) nodes</em> 
athos@1540
    67
(i.e. nodes having a special
alpar@1118
    68
label on them). The section starts with
athos@1522
    69
athos@1522
    70
<tt> \@nodes </tt>
athos@1522
    71
athos@1522
    72
Each of the next lines contains a label for a node in the graph 
athos@1540
    73
and then the ID as described in the \e nodeset section.
deba@1114
    74
deba@1114
    75
\code
deba@1114
    76
@nodes 
deba@1114
    77
source 3
deba@1114
    78
target 12
deba@1114
    79
\endcode
deba@1114
    80
athos@1540
    81
The last section describes the <em>labeled (distinguished) edges</em>
deba@1333
    82
(i.e. edges having a special label on them). It starts with \c \@edges
deba@1114
    83
and then each line contains the name of the edge and the ID.
deba@1114
    84
deba@1114
    85
\code
athos@1540
    86
@edges 
deba@1114
    87
observed c
deba@1114
    88
\endcode
deba@1114
    89
deba@1114
    90
deba@1114
    91
The file may contain empty lines and comment lines. The comment lines
deba@1114
    92
start with an \c # character.
deba@1114
    93
deba@1532
    94
The attributes section can handle some information about the graph. It
athos@1540
    95
contains key-value pairs in each line (a key and the mapped value to key). The
athos@1540
    96
key should be a string without whitespaces, the value can be of various types.
deba@1532
    97
deba@1532
    98
\code
deba@1532
    99
@attributes
deba@1532
   100
title "Four colored plan graph"
deba@1532
   101
author "Balazs DEZSO"
deba@1532
   102
copyright "Lemon Library"
deba@1532
   103
version 12
deba@1532
   104
\endcode
deba@1532
   105
athos@1522
   106
<tt> \@end </tt>
athos@1522
   107
athos@1522
   108
line.
athos@1522
   109
deba@1114
   110
deba@1114
   111
\section use Using graph input-output
athos@1540
   112
athos@1540
   113
The easiest way of using graph input and output is using the versions of the
athos@1540
   114
  public \ref readGraph() and \ref writeGraph() functions; if you don't need
athos@1540
   115
  very sophisticated behaviour then you might be satisfied with
athos@1540
   116
  those. Otherwise go on reading this page.
athos@1540
   117
athos@1540
   118
The graph input and output is based on <em> reading and writing
athos@1540
   119
commands</em>. The user gives reading and writing commands to the reader or
athos@1540
   120
writer class, then he calls the \c run() method that executes all the given
athos@1540
   121
commands.
deba@1114
   122
deba@1114
   123
\subsection write Writing a graph
deba@1114
   124
alpar@1631
   125
The \ref lemon::GraphWriter "GraphWriter" template class
alpar@1631
   126
provides the graph output. To write a graph
athos@1526
   127
you should first give writing commands to the writer. You can declare
athos@1540
   128
writing command as \c NodeMap or \c EdgeMap writing and labeled Node and
deba@1114
   129
Edge writing.
deba@1114
   130
deba@1114
   131
\code
deba@1333
   132
GraphWriter<ListGraph> writer(std::cout, graph);
deba@1114
   133
\endcode
deba@1114
   134
alpar@1631
   135
The \ref lemon::GraphWriter::writeNodeMap() "writeNodeMap()"
alpar@1631
   136
function declares a \c NodeMap writing command in the
alpar@1631
   137
\ref lemon::GraphWriter "GraphWriter".
alpar@1631
   138
You should give a name to the map and the map
athos@1522
   139
object as parameters. The NodeMap writing command with name "id" should write a 
athos@1540
   140
unique map because it will be regarded as an ID map.
deba@1114
   141
deba@1114
   142
\see IdMap, DescriptorMap  
deba@1114
   143
deba@1114
   144
\code
deba@1114
   145
IdMap<ListGraph, Node> nodeIdMap;
deba@1394
   146
writer.writeNodeMap("id", nodeIdMap);
deba@1114
   147
deba@1394
   148
writer.writeNodeMap("x-coord", xCoordMap);
deba@1394
   149
writer.writeNodeMap("y-coord", yCoordMap);
deba@1394
   150
writer.writeNodeMap("color", colorMap);
deba@1114
   151
\endcode
deba@1114
   152
alpar@1631
   153
With the \ref lemon::GraphWriter::writeEdgeMap() "writeEdgeMap()"
alpar@1631
   154
member function you can give an edge map
deba@1333
   155
writing command similar to the NodeMaps.
deba@1114
   156
deba@1114
   157
\see IdMap, DescriptorMap  
athos@1522
   158
deba@1114
   159
\code
deba@1114
   160
DescriptorMap<ListGraph, Edge, ListGraph::EdgeMap<int> > edgeDescMap(graph);
deba@1394
   161
writer.writeEdgeMap("descriptor", edgeDescMap);
deba@1114
   162
deba@1394
   163
writer.writeEdgeMap("weight", weightMap);
deba@1394
   164
writer.writeEdgeMap("label", labelMap);
deba@1114
   165
\endcode
deba@1114
   166
alpar@1631
   167
With \ref lemon::GraphWriter::writeNode() "writeNode()"
alpar@1631
   168
and \ref lemon::GraphWriter::writeEdge() "writeEdge()"
alpar@1631
   169
functions you can designate Nodes and
athos@1522
   170
Edges in the graph. For example, you can write out the source and target node
athos@1522
   171
of a maximum flow instance.
deba@1114
   172
deba@1114
   173
\code
deba@1394
   174
writer.writeNode("source", sourceNode);
deba@1394
   175
writer.writeNode("target", targetNode);
deba@1114
   176
deba@1394
   177
writer.writeEdge("observed", edge);
deba@1114
   178
\endcode
deba@1114
   179
alpar@1631
   180
With \ref lemon::GraphWriter::writeAttribute() "writeAttribute()"
alpar@1631
   181
function you can write an attribute to the file.
deba@1532
   182
deba@1532
   183
\code
deba@1532
   184
writer.writeAttribute("author", "Balazs DEZSO");
deba@1532
   185
writer.writeAttribute("version", 12);
deba@1532
   186
\endcode
deba@1532
   187
alpar@1631
   188
After you give all write commands you must call the
alpar@1631
   189
\ref lemon::GraphWriter::run() "run()" member
athos@1522
   190
function, which executes all the writing commands.
deba@1114
   191
deba@1114
   192
\code
deba@1114
   193
writer.run();
deba@1114
   194
\endcode
deba@1114
   195
deba@1114
   196
\subsection reading Reading a graph
deba@1114
   197
athos@1540
   198
The file to be read may contain several maps and labeled nodes or edges.
deba@1114
   199
If you read a graph you need not read all the maps and items just those
alpar@1631
   200
that you need. The interface of the \ref lemon::GraphReader "GraphReader"
alpar@1631
   201
is very similar to
alpar@1631
   202
the \ref lemon::GraphWriter "GraphWriter"
alpar@1631
   203
but the reading method does not depend on the order of the
deba@1114
   204
given commands.
deba@1114
   205
athos@1522
   206
The reader object assumes that each not readed value does not contain 
alpar@1118
   207
whitespaces, therefore it has some extra possibilities to control how
alpar@1118
   208
it should skip the values when the string representation contains spaces.
deba@1114
   209
deba@1114
   210
\code
deba@1333
   211
GraphReader<ListGraph> reader(std::cin, graph);
deba@1114
   212
\endcode
deba@1114
   213
alpar@1631
   214
The \ref lemon::GraphReader::readNodeMap() "readNodeMap()"
alpar@1631
   215
function reads a map from the \c nodeset section.
athos@1522
   216
If there is a map that you do not want to read from the file and there are
athos@1522
   217
whitespaces in the string represenation of the values then you should
alpar@1631
   218
call the \ref lemon::GraphReader::skipNodeMap() "skipNodeMap()"
alpar@1631
   219
template member function with proper parameters.
deba@1114
   220
deba@1114
   221
\see QuotedStringReader
athos@1522
   222
deba@1114
   223
\code
deba@1394
   224
reader.readNodeMap("x-coord", xCoordMap);
deba@1394
   225
reader.readNodeMap("y-coord", yCoordMap);
deba@1114
   226
deba@1394
   227
reader.readNodeMap<QuotedStringReader>("label", labelMap);
deba@1114
   228
reader.skipNodeMap<QuotedStringReader>("description");
deba@1114
   229
deba@1394
   230
reader.readNodeMap("color", colorMap);
deba@1114
   231
\endcode
deba@1114
   232
alpar@1631
   233
With the \ref lemon::GraphReader::readEdgeMap() "readEdgeMap()"
alpar@1631
   234
member function you can give an edge map
deba@1114
   235
reading command similar to the NodeMaps. 
deba@1114
   236
deba@1114
   237
\code
deba@1394
   238
reader.readEdgeMap("weight", weightMap);
deba@1394
   239
reader.readEdgeMap("label", labelMap);
deba@1114
   240
\endcode
deba@1114
   241
alpar@1631
   242
With \ref lemon::GraphReader::readNode() "readNode()"
alpar@1631
   243
and \ref lemon::GraphReader::readEdge() "readEdge()"
alpar@1631
   244
functions you can read labeled Nodes and
deba@1114
   245
Edges.
deba@1114
   246
deba@1114
   247
\code
deba@1394
   248
reader.readNode("source", sourceNode);
deba@1394
   249
reader.readNode("target", targetNode);
deba@1114
   250
deba@1394
   251
reader.readEdge("observed", edge);
deba@1114
   252
\endcode
deba@1114
   253
alpar@1631
   254
With \ref lemon::GraphReader::readAttribute() "readAttribute()"
alpar@1631
   255
function you can read an attribute from the file.
deba@1532
   256
deba@1532
   257
\code
deba@1532
   258
std::string author;
deba@1532
   259
writer.readAttribute("author", author);
deba@1532
   260
int version;
deba@1532
   261
writer.writeAttribute("version", version);
deba@1532
   262
\endcode
deba@1532
   263
alpar@1631
   264
After you give all read commands you must call the
alpar@1631
   265
\ref lemon::GraphReader::run() "run()" member
athos@1522
   266
function, which executes all the commands.
deba@1114
   267
deba@1114
   268
\code
deba@1114
   269
reader.run();
deba@1114
   270
\endcode
deba@1114
   271
athos@1540
   272
\anchor rwbackground
athos@1527
   273
\section types Background of Reading and Writing
athos@1540
   274
athos@1540
   275
athos@1527
   276
To read a map (on the nodes or edges)
alpar@1631
   277
the \ref lemon::GraphReader "GraphReader"
alpar@1631
   278
should know how to read a Value from the given map.
deba@1114
   279
By the default implementation the input operator reads a value from
deba@1114
   280
the stream and the type of the readed value is the value type of the given map.
deba@1114
   281
When the reader should skip a value in the stream, because you do not
athos@1527
   282
want to store it in a map, the reader skips a character sequence without 
athos@1540
   283
whitespaces. 
deba@1114
   284
deba@1114
   285
If you want to change the functionality of the reader, you can use
deba@1114
   286
template parameters to specialize it. When you give a reading
deba@1114
   287
command for a map you can give a Reader type as template parameter.
deba@1333
   288
With this template parameter you can control how the Reader reads
deba@1114
   289
a value from the stream.
deba@1114
   290
deba@1114
   291
The reader has the next structure: 
deba@1114
   292
\code
deba@1114
   293
struct TypeReader {
deba@1114
   294
  typedef TypeName Value;
deba@1114
   295
deba@1114
   296
  void read(std::istream& is, Value& value);
deba@1114
   297
};
deba@1114
   298
\endcode
deba@1114
   299
athos@1527
   300
For example, the \c "strings" nodemap contains strings and you do not need
athos@1540
   301
the value of the string just the length. Then you can implement an own Reader
deba@1114
   302
struct.
deba@1114
   303
deba@1114
   304
\code
deba@1114
   305
struct LengthReader {
deba@1114
   306
  typedef int Value;
deba@1114
   307
deba@1114
   308
  void read(std::istream& is, Value& value) {
deba@1114
   309
    std::string tmp;
deba@1114
   310
    is >> tmp;
deba@1114
   311
    value = tmp.length();
deba@1114
   312
  }
deba@1114
   313
};
deba@1114
   314
...
deba@1394
   315
reader.readNodeMap<LengthReader>("strings", lengthMap);
deba@1114
   316
\endcode  
deba@1114
   317
deba@1114
   318
The global functionality of the reader class can be changed by giving a
athos@1526
   319
special template parameter to the GraphReader class. By default, the
alpar@1118
   320
template parameter is \c DefaultReaderTraits. A reader traits class 
athos@1540
   321
should provide an inner template class Reader for each type, and a 
deba@1114
   322
DefaultReader for skipping a value.
deba@1114
   323
athos@1540
   324
The specialization of  writing is very similar to that of reading.
deba@1114
   325
athos@1540
   326
\section undir Undirected graphs
deba@1532
   327
athos@1540
   328
In a file describing an undirected graph (undir graph, for short) you find an
athos@1540
   329
\c undiredgeset section instead of the \c edgeset section. The first line of
athos@1540
   330
the section describes the names of the maps on the undirected egdes and all
athos@1540
   331
next lines describe one undirected edge with the the incident nodes and the
athos@1540
   332
values of the map.
deba@1532
   333
athos@1540
   334
The format handles directed edge maps as a syntactical sugar???, if there
athos@1540
   335
are two maps with names being the same with a \c '+' and a \c '-' prefix
athos@1540
   336
then this will be read as a directed map.
deba@1532
   337
deba@1532
   338
\code
deba@1532
   339
@undiredgeset
deba@1532
   340
             id    capacity +flow -flow
deba@1532
   341
32   2       1     4.3      2.0	  0.0
deba@1532
   342
21   21      5     2.6      0.0   2.6
deba@1532
   343
21   12      8     3.4      0.0   0.0
deba@1532
   344
\endcode
deba@1532
   345
athos@1540
   346
The \c edges section is changed to \c undiredges section. This section
deba@1532
   347
describes labeled edges and undirected edges. The directed edge label
athos@1540
   348
should start with a \c '+' or a \c '-' prefix to decide the direction
deba@1532
   349
of the edge. 
deba@1532
   350
deba@1532
   351
\code
deba@1532
   352
@undiredges
deba@1532
   353
undiredge 1
deba@1532
   354
+edge 5
deba@1532
   355
-back 5
deba@1532
   356
\endcode
deba@1532
   357
alpar@1631
   358
There are similar classes to the \ref lemon::GraphReader "GraphReader" and
alpar@1631
   359
\ref lemon::GraphWriter "GraphWriter" which
alpar@1631
   360
handle the undirected graphs. These classes are
alpar@1631
   361
the \ref lemon::UndirGraphReader "UndirGraphReader"
alpar@1631
   362
and \ref lemon::UndirGraphWriter "UndirGraphWriter".
deba@1532
   363
alpar@1631
   364
The \ref lemon::UndirGraphReader::readUndirMap() "readUndirMap()"
alpar@1631
   365
function reads an undirected map and the
alpar@1631
   366
\ref lemon::UndirGraphReader::readUndirEdge() "readUndirEdge()"
alpar@1631
   367
reads an undirected edge from the file, 
deba@1532
   368
deba@1532
   369
\code
deba@1532
   370
reader.readUndirEdgeMap("capacity", capacityMap);
deba@1532
   371
reader.readEdgeMap("flow", flowMap);
deba@1532
   372
...
deba@1532
   373
reader.readUndirEdge("undir_edge", undir_edge);
deba@1532
   374
reader.readEdge("edge", edge);
deba@1532
   375
\endcode
deba@1532
   376
deba@1532
   377
\section advanced Advanced features
deba@1532
   378
athos@1540
   379
The graph reader and writer classes give an easy way to read and write
athos@1540
   380
graphs. But sometimes we want more advanced features. In this case we can
athos@1540
   381
use the more general <tt>lemon reader and writer</tt> interface.
deba@1532
   382
athos@1540
   383
The LEMON file format is a section oriented file format. It contains one or
athos@1540
   384
more sections, each starting with a line identifying its type 
athos@1540
   385
(the word starting with the \c \@  character).
deba@1532
   386
The content of the section this way cannot contain line with \c \@ first
deba@1532
   387
character. The file may contains comment lines with \c # first character.
deba@1532
   388
alpar@1631
   389
The \ref lemon::LemonReader "LemonReader"
alpar@1631
   390
and \ref lemon::LemonWriter "LemonWriter"
alpar@1631
   391
gives a framework to read and
deba@1532
   392
write sections. There are various section reader and section writer
alpar@1631
   393
classes which can be attached to a \ref lemon::LemonReader "LemonReader"
alpar@1631
   394
or a \ref lemon::LemonWriter "LemonWriter".
deba@1532
   395
deba@1532
   396
There are default section readers and writers for reading and writing
athos@1540
   397
item sets, and labeled items in the graph. These read and write
deba@1532
   398
the format described above. Other type of data can be handled with own
deba@1532
   399
section reader and writer classes which are inherited from the
alpar@1631
   400
\c LemonReader::SectionReader or the
alpar@1631
   401
\ref lemon::LemonWriter::SectionWriter "LemonWriter::SectionWriter"
alpar@1631
   402
classes.
deba@1532
   403
deba@1532
   404
The next example defines a special section reader which reads the
deba@1532
   405
\c \@description sections into a string:
deba@1532
   406
deba@1532
   407
\code 
deba@1532
   408
class DescriptionReader : LemonReader::SectionReader {
deba@1532
   409
protected:
deba@1532
   410
  virtual bool header(const std::string& line) {
deba@1532
   411
    std::istringstream ls(line);
deba@1532
   412
    std::string head;
deba@1532
   413
    ls >> head;
deba@1532
   414
    return head == "@description";
deba@1532
   415
  }
deba@1532
   416
deba@1532
   417
  virtual void read(std::istream& is) {
deba@1532
   418
    std::string line;
deba@1532
   419
    while (getline(is, line)) {
deba@1532
   420
      desc += line;
deba@1532
   421
    }
deba@1532
   422
  }
deba@1532
   423
public:
deba@1532
   424
deba@1532
   425
  typedef LemonReader::SectionReader Parent;
deba@1532
   426
  
deba@1532
   427
  DescriptionReader(LemonReader& reader) : Parent(reader) {}
deba@1532
   428
deba@1532
   429
  const std::string& description() const {
deba@1532
   430
    return description;
deba@1532
   431
  }
deba@1532
   432
deba@1532
   433
private:
deba@1532
   434
  std::string desc;
deba@1532
   435
};
deba@1532
   436
\endcode
deba@1532
   437
deba@1532
   438
The other advanced stuff of the generalized file format is that 
deba@1532
   439
multiple edgesets can be stored to the same nodeset. It can be used 
athos@1540
   440
for example as a network traffic matrix.
deba@1532
   441
athos@1540
   442
In our example there is a network with symmetric links and there are assymetric
deba@1532
   443
traffic request on the network. This construction can be stored in an
alpar@1631
   444
undirected graph and in a directed \c NewEdgeSetAdaptor class. The example
alpar@1631
   445
shows the input with the \ref lemon::LemonReader "LemonReader" class:
deba@1532
   446
deba@1532
   447
\code
deba@1532
   448
UndirListGraph network;
deba@1532
   449
UndirListGraph::UndirEdgeSet<double> capacity;
deba@1532
   450
NewEdgeSetAdaptor<UndirListGraph> traffic(network);
deba@1532
   451
NewEdgeSetAdaptor<UndirListGraph>::EdgeSet<double> request(network);
deba@1532
   452
deba@1532
   453
LemonReader reader(std::cin);
deba@1532
   454
NodeSetReader nodesetReader(reader, network);
deba@1532
   455
UndirEdgeSetReader undirEdgesetReader(reader, network, nodesetReader);
deba@1532
   456
undirEdgesetReader.readEdgeMap("capacity", capacity);
deba@1532
   457
EdgeSetReader edgesetReader(reader, traffic, nodesetReader);
deba@1532
   458
edgesetReader.readEdgeMap("request", request);
deba@1532
   459
deba@1532
   460
reader.run();
deba@1532
   461
\endcode
deba@1532
   462
alpar@1631
   463
Because both the \ref lemon::GraphReader "GraphReader"
alpar@1631
   464
and the \ref lemon::UndirGraphReader "UndirGraphReader" can be converted
alpar@1631
   465
to \ref lemon::LemonReader "LemonReader"
alpar@1631
   466
and it can resolve the ID's of the items, the previous
alpar@1631
   467
result can be achived with the \ref lemon::UndirGraphReader "UndirGraphReader"
alpar@1631
   468
class, too.
deba@1532
   469
deba@1532
   470
deba@1532
   471
\code
deba@1532
   472
UndirListGraph network;
deba@1532
   473
UndirListGraph::UndirEdgeSet<double> capacity;
deba@1532
   474
NewEdgeSetAdaptor<UndirListGraph> traffic(network);
deba@1532
   475
NewEdgeSetAdaptor<UndirListGraph>::EdgeSet<double> request(network);
deba@1532
   476
deba@1532
   477
UndirGraphReader reader(std::cin, network);
deba@1532
   478
reader.readEdgeMap("capacity", capacity);
deba@1532
   479
EdgeSetReader edgesetReader(reader, traffic, reader);
deba@1532
   480
edgesetReader.readEdgeMap("request", request);
deba@1532
   481
deba@1532
   482
reader.run();
deba@1532
   483
\endcode
deba@1532
   484
deba@1333
   485
\author Balazs Dezso
deba@1114
   486
*/
alpar@1631
   487
}