src/work/deba/graph_reader.h
author deba
Tue, 14 Dec 2004 19:26:50 +0000
changeset 1036 2f514b5c7122
parent 1032 9e903d3a1ef6
child 1037 3eaff8d04171
permissions -rw-r--r--
reader under construction
deba@1032
     1
/* -*- C++ -*-
deba@1032
     2
 * src/lemon/graph_reader.h - Part of LEMON, a generic C++ optimization library
deba@1032
     3
 *
deba@1032
     4
 * Copyright (C) 2004 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
deba@1032
     5
 * (Egervary Combinatorial Optimization Research Group, EGRES).
deba@1032
     6
 *
deba@1032
     7
 * Permission to use, modify and distribute this software is granted
deba@1032
     8
 * provided that this copyright notice appears in all copies. For
deba@1032
     9
 * precise terms see the accompanying LICENSE file.
deba@1032
    10
 *
deba@1032
    11
 * This software is provided "AS IS" with no warranty of any kind,
deba@1032
    12
 * express or implied, and with no claim as to its suitability for any
deba@1032
    13
 * purpose.
deba@1032
    14
 *
deba@1032
    15
 */
deba@1032
    16
deba@1032
    17
///\ingroup gio
deba@1032
    18
///\file
deba@1036
    19
///\brief Graph reader.
deba@1032
    20
deba@1032
    21
#include <iostream>
deba@1032
    22
#include <sstream>
deba@1032
    23
deba@1032
    24
#include <map>
deba@1032
    25
#include <vector>
deba@1032
    26
deba@1032
    27
#include <lemon/error.h>
deba@1032
    28
deba@1032
    29
/// \todo fix exceptions
deba@1032
    30
deba@1032
    31
deba@1032
    32
namespace lemon {
deba@1036
    33
deba@1036
    34
  // Exceptions
deba@1036
    35
deba@1036
    36
  class IOException {
deba@1036
    37
  public:
deba@1036
    38
    virtual string what() const = 0;
deba@1036
    39
  };
deba@1036
    40
deba@1036
    41
  class DataFormatException : public IOException {
deba@1036
    42
    std::string message;
deba@1036
    43
  public:
deba@1036
    44
    DataFormatException(const std::string& _message) 
deba@1036
    45
      : message(_message) {}
deba@1036
    46
    std::string what() const {
deba@1036
    47
      return "DataFormatException: " + message; 
deba@1036
    48
    }
deba@1036
    49
  };
deba@1036
    50
deba@1036
    51
  class StreamException : public IOException {
deba@1036
    52
  public:
deba@1036
    53
    virtual int line() = 0;
deba@1036
    54
  private:
deba@1036
    55
    IOException* exception;
deba@1036
    56
    int line_num;
deba@1036
    57
  };  
deba@1036
    58
deba@1036
    59
deba@1036
    60
  // Readers and ReaderTraits
deba@1032
    61
  
deba@1032
    62
  struct DefaultReaderTraits {
deba@1032
    63
deba@1032
    64
    template <typename _Value>
deba@1032
    65
    struct Reader {
deba@1032
    66
      typedef _Value Value;
deba@1032
    67
      void read(std::istream& is, Value& value) {
deba@1036
    68
	if (!(is >> value)) 
deba@1036
    69
	  throw DataFormatException("Default Reader format exception");
deba@1032
    70
      }
deba@1032
    71
    };
deba@1032
    72
deba@1036
    73
    typedef Reader<std::string> DefaultReader;
deba@1032
    74
deba@1032
    75
  };
deba@1032
    76
deba@1036
    77
  class QuotedStringReader {
deba@1036
    78
  public:
deba@1036
    79
    typedef std::string Value;
deba@1036
    80
deba@1036
    81
    QuotedStringReader(bool _escaped = true) : escaped(_escaped) {}
deba@1036
    82
deba@1036
    83
    void read(std::istream& is, std::string& value) {
deba@1036
    84
      char c;
deba@1036
    85
      value.clear();
deba@1036
    86
      is >> ws;
deba@1036
    87
      if (!is.get(c) || c != '\"') throw DataFormatException("Quoted string format exception");
deba@1036
    88
      while (is.get(c) && c != '\"') {
deba@1036
    89
	if (escaped && c == '\\') {
deba@1036
    90
	  value += readEscape(is);
deba@1036
    91
	} else {
deba@1036
    92
	  value += c;
deba@1036
    93
	}
deba@1036
    94
      }
deba@1036
    95
      if (!is) throw DataFormatException("Quoted string format exception");
deba@1036
    96
    }
deba@1036
    97
deba@1036
    98
  private:
deba@1036
    99
    
deba@1036
   100
    static char readEscape(std::istream& is) {
deba@1036
   101
      char c;
deba@1036
   102
      switch (is.get(c), c) {
deba@1036
   103
      case '\\':
deba@1036
   104
	return '\\';
deba@1036
   105
      case '\"':
deba@1036
   106
	return '\"';
deba@1036
   107
      case '\'':
deba@1036
   108
	return '\'';
deba@1036
   109
      case '\?':
deba@1036
   110
	return '\?';
deba@1036
   111
      case 'a':
deba@1036
   112
	return '\a';
deba@1036
   113
      case 'b':
deba@1036
   114
	return '\b';
deba@1036
   115
      case 'f':
deba@1036
   116
	return '\f';
deba@1036
   117
      case 'n':
deba@1036
   118
	return '\n';
deba@1036
   119
      case 'r':
deba@1036
   120
	return '\r';
deba@1036
   121
      case 't':
deba@1036
   122
	return '\t';
deba@1036
   123
      case 'v':
deba@1036
   124
	return '\v';
deba@1036
   125
      case 'x':
deba@1036
   126
	{
deba@1036
   127
	  int code;
deba@1036
   128
	  if (!is.get(c) || !isHex(c)) throw DataFormatException("Escape format exception");
deba@1036
   129
	  else if (code = valueHex(c), !is.get(c) || !isHex(c)) is.putback(c);
deba@1036
   130
	  else code = code * 16 + valueHex(c);
deba@1036
   131
	  return code;
deba@1036
   132
	}
deba@1036
   133
      default:
deba@1036
   134
	{
deba@1036
   135
	  int code;
deba@1036
   136
	  if (!isOct(c)) throw DataFormatException("Escape format exception");
deba@1036
   137
	  else if (code = valueOct(c), !is.get(c) || !isOct(c)) is.putback(c);
deba@1036
   138
	  else if (code = code * 8 + valueOct(c), !is.get(c) || !isOct(c)) is.putback(c);
deba@1036
   139
	  else code = code * 8 + valueOct(c);
deba@1036
   140
	  return code;
deba@1036
   141
	}	      
deba@1036
   142
      } 
deba@1036
   143
    }
deba@1036
   144
deba@1036
   145
    static bool isOct(char c) {
deba@1036
   146
      return '0' <= c && c <='7'; 
deba@1036
   147
    }
deba@1036
   148
    
deba@1036
   149
    static int valueOct(char c) {
deba@1036
   150
      return c - '0';
deba@1036
   151
    }
deba@1036
   152
deba@1036
   153
   static bool isHex(char c) {
deba@1036
   154
      return ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); 
deba@1036
   155
    }
deba@1036
   156
    
deba@1036
   157
    static int valueHex(char c) {
deba@1036
   158
      if ('0' <= c && c <= '9') return c - '0';
deba@1036
   159
      if ('a' <= c && c <= 'z') return c - 'a' + 10;
deba@1036
   160
      return c - 'A' + 10;
deba@1036
   161
    }
deba@1036
   162
deba@1036
   163
    bool escaped;
deba@1032
   164
  };
deba@1032
   165
deba@1032
   166
deba@1036
   167
deba@1036
   168
deba@1036
   169
deba@1036
   170
  // Graph reader
deba@1032
   171
  
deba@1032
   172
  template <typename _Graph, typename _ReaderTraits = DefaultReaderTraits> 
deba@1032
   173
  class GraphReader {
deba@1032
   174
deba@1032
   175
  public:
deba@1032
   176
    
deba@1032
   177
    typedef _Graph Graph;
deba@1032
   178
    typedef typename Graph::Node Node;
deba@1032
   179
    typedef typename Graph::Edge Edge;
deba@1032
   180
deba@1032
   181
    typedef _ReaderTraits ReaderTraits;
deba@1036
   182
    typedef typename ReaderTraits::DefaultReader DefaultReader;
deba@1032
   183
deba@1036
   184
    GraphReader(istream& _is, Graph& _graph, const DefaultReader& _reader = DefaultReader()) 
deba@1036
   185
      : is(_is), graph(_graph), nodeSkipper(_reader), edgeSkipper(_reader) {}
deba@1036
   186
deba@1036
   187
deba@1036
   188
    ~GraphReader() {
deba@1036
   189
deba@1036
   190
      for (typename NodeReaders::iterator it = node_readers.begin(); it != node_readers.end(); ++it) {
deba@1036
   191
	delete it->second;
deba@1036
   192
      }
deba@1036
   193
deba@1036
   194
      for (typename EdgeReaders::iterator it = edge_readers.begin(); it != edge_readers.end(); ++it) {
deba@1036
   195
	delete it->second;
deba@1036
   196
      }
deba@1036
   197
deba@1036
   198
    }
deba@1032
   199
deba@1032
   200
    template <typename Map>
deba@1036
   201
    GraphReader& readNodeMap(std::string name, Map& map) {
deba@1036
   202
      return readNodeMap<typename ReaderTraits::template Reader<typename Map::Value>, Map>(name, map);
deba@1032
   203
    }
deba@1032
   204
deba@1036
   205
    template <typename Reader, typename Map>
deba@1032
   206
    GraphReader& readNodeMap(std::string name, Map& map, const Reader& reader = Reader()) {
deba@1036
   207
      if (node_readers.find(name) != node_readers.end()) {
deba@1036
   208
	Exception e;
deba@1036
   209
	e << "Multiple read rule for node map: " << name;
deba@1036
   210
	throw e;
deba@1036
   211
      }
deba@1032
   212
      node_readers.insert(make_pair(name, new MapReader<Node, Map, Reader>(map, reader)));
deba@1032
   213
      return *this;
deba@1032
   214
    }
deba@1032
   215
deba@1036
   216
    template <typename Reader>
deba@1036
   217
    GraphReader& skipNodeMap(std::string name, const Reader& reader = Reader()) {
deba@1036
   218
      if (node_readers.find(name) != node_readers.end()) {
deba@1036
   219
	Exception e;
deba@1036
   220
	e << "Multiple read rule for node map: " << name;
deba@1036
   221
	throw e;
deba@1036
   222
      }
deba@1036
   223
      node_readers.insert(make_pair(name, new SkipReader<Node, Reader>(reader)));
deba@1036
   224
      return *this;
deba@1032
   225
    }
deba@1032
   226
deba@1036
   227
    template <typename Map>
deba@1036
   228
    GraphReader& readEdgeMap(std::string name, Map& map) { 
deba@1036
   229
      return readEdgeMap<typename ReaderTraits::template Reader<typename Map::Value>, Map>(name, map);
deba@1036
   230
    }
deba@1036
   231
deba@1036
   232
deba@1036
   233
    template <typename Reader, typename Map>
deba@1032
   234
    GraphReader& readEdgeMap(std::string name, Map& map, const Reader& reader = Reader()) {
deba@1036
   235
      if (edge_readers.find(name) != edge_readers.end()) {
deba@1036
   236
	Exception e;
deba@1036
   237
	e << "Multiple read rule for edge map: " << name;
deba@1036
   238
	throw e;
deba@1036
   239
      }
deba@1032
   240
      edge_readers.insert(make_pair(name, new MapReader<Edge, Map, Reader>(map, reader)));
deba@1032
   241
      return *this;
deba@1032
   242
    }
deba@1032
   243
deba@1036
   244
    template <typename Reader>
deba@1036
   245
    GraphReader& skipEdgeMap(std::string name, const Reader& reader = Reader()) {
deba@1036
   246
      if (edge_readers.find(name) != edge_readers.end()) {
deba@1036
   247
	Exception e;
deba@1036
   248
	e << "Multiple read rule for edge map: " << name;
deba@1036
   249
	throw e;
deba@1036
   250
      }
deba@1036
   251
      edge_readers.insert(make_pair(name, new SkipReader<Edge, Reader>(reader)));
deba@1036
   252
      return *this;
deba@1036
   253
    }
deba@1036
   254
deba@1032
   255
    void read() {
deba@1032
   256
      int line_num = 0;
deba@1036
   257
      InverterBase<Node>* nodeInverter = 0;
deba@1036
   258
      InverterBase<Edge>* edgeInverter = 0;
deba@1036
   259
      // \todo delete the inverters
deba@1036
   260
      //      try {
deba@1036
   261
	{
deba@1036
   262
	  std::string line = readNotEmptyLine(is, line_num);
deba@1036
   263
	}
deba@1036
   264
	readNodeSet(line_num, nodeInverter);
deba@1036
   265
	readEdgeSet(line_num, edgeInverter, nodeInverter);
deba@1036
   266
	//      } catch (...){
deba@1036
   267
	if (nodeInverter != 0) delete nodeInverter;
deba@1036
   268
	if (edgeInverter != 0) delete edgeInverter;
deba@1036
   269
	//      }
deba@1032
   270
    }
deba@1032
   271
deba@1032
   272
  private:
deba@1032
   273
deba@1036
   274
    template <typename Item> class InverterBase;
deba@1036
   275
    //    template <typename Item> class InverterBase;
deba@1036
   276
deba@1036
   277
    void readNodeSet(int& line_num, InverterBase<Node>* & nodeInverter) {
deba@1032
   278
      int n = 0;
deba@1036
   279
      std::vector<ReaderBase<Node>*> index;
deba@1032
   280
      {
deba@1032
   281
	std::string line = readNotEmptyLine(is, line_num);    
deba@1032
   282
	std::string id;
deba@1032
   283
	std::istringstream ls(line);	
deba@1032
   284
	while (ls >> id) {
deba@1036
   285
	  if (id[0] == '#') break;
deba@1036
   286
	  typename NodeReaders::iterator it = node_readers.find(id);
deba@1032
   287
	  if (it != node_readers.end()) {
deba@1032
   288
	    index.push_back(it->second);
deba@1032
   289
	  } else {
deba@1036
   290
	    index.push_back(&nodeSkipper);
deba@1032
   291
	  }
deba@1032
   292
	  ++n;
deba@1032
   293
	}
deba@1032
   294
      }
deba@1036
   295
deba@1036
   296
      nodeInverter = index[0]->getInverter();
deba@1032
   297
      std::string line;
deba@1032
   298
      while (line = readNotEmptyLine(is, line_num), line[0] != '@') {
deba@1032
   299
	Node node = graph.addNode();
deba@1032
   300
	std::istringstream ls(line);
deba@1036
   301
	nodeInverter->read(ls, node);
deba@1036
   302
	for (int i = 1; i < n; ++i) {
deba@1036
   303
	  index[i]->read(ls, node);
deba@1032
   304
	}
deba@1032
   305
      }
deba@1032
   306
    }
deba@1032
   307
deba@1036
   308
    void readEdgeSet(int& line_num, InverterBase<Edge>* & edgeInverter, InverterBase<Node>* & nodeInverter) {
deba@1036
   309
      int n = 0;
deba@1036
   310
      std::vector<ReaderBase<Edge>*> index;
deba@1036
   311
      {
deba@1036
   312
	std::string line = readNotEmptyLine(is, line_num);    
deba@1036
   313
	std::string id;
deba@1036
   314
	std::istringstream ls(line);	
deba@1036
   315
	while (ls >> id) {
deba@1036
   316
	  if (id[0] == '#') break;
deba@1036
   317
	  typename EdgeReaders::iterator it = edge_readers.find(id);
deba@1036
   318
	  if (it != edge_readers.end()) {
deba@1036
   319
	    index.push_back(it->second);
deba@1036
   320
	  } else {
deba@1036
   321
	    index.push_back(&edgeSkipper);
deba@1036
   322
	  }
deba@1036
   323
	  ++n;
deba@1036
   324
	}
deba@1036
   325
      }
deba@1036
   326
      edgeInverter = index[0]->getInverter();
deba@1036
   327
      std::string line;
deba@1036
   328
      while (line = readNotEmptyLine(is, line_num), line[0] != '@') {	
deba@1036
   329
	std::istringstream ls(line);
deba@1036
   330
	Node source = nodeInverter->read(ls);
deba@1036
   331
	Node target = nodeInverter->read(ls);
deba@1036
   332
	Edge edge = graph.addEdge(source, target);
deba@1036
   333
	edgeInverter->read(ls, edge);
deba@1036
   334
	for (int i = 1; i < n; ++i) {
deba@1036
   335
	  index[i]->read(ls, edge);
deba@1036
   336
	}
deba@1036
   337
      }      
deba@1032
   338
    }
deba@1032
   339
deba@1032
   340
    std::string readNotEmptyLine(std::istream& is, int& line_num) {
deba@1032
   341
      std::string line;
deba@1032
   342
      while (++line_num, getline(is, line)) {	
deba@1032
   343
	int vi = line.find_first_not_of(" \t");
deba@1032
   344
	if (vi != string::npos && line[vi] != '#') {
deba@1032
   345
	  return line.substr(vi);
deba@1032
   346
	}
deba@1032
   347
      }
deba@1032
   348
      throw Exception();
deba@1032
   349
    }
deba@1032
   350
    
deba@1036
   351
    template <typename _Item>
deba@1036
   352
    class InverterBase {
deba@1036
   353
    public:
deba@1036
   354
      typedef _Item Item;
deba@1036
   355
      virtual void read(istream&, const Item&) = 0;
deba@1036
   356
      virtual Item read(istream&) = 0;
deba@1036
   357
    };
deba@1032
   358
deba@1036
   359
    template <typename _Item, typename _Map, typename _Reader>
deba@1036
   360
    class MapReaderInverter : public InverterBase<_Item> {
deba@1036
   361
    public:
deba@1036
   362
      typedef _Item Item;
deba@1036
   363
      typedef _Reader Reader;
deba@1036
   364
      typedef typename Reader::Value Value;
deba@1036
   365
      typedef _Map Map;
deba@1036
   366
      typedef std::map<Value, Item> Inverse;
deba@1036
   367
deba@1036
   368
      Map& map;
deba@1036
   369
      Reader reader;
deba@1036
   370
      Inverse inverse;
deba@1036
   371
deba@1036
   372
      MapReaderInverter(Map& _map, const Reader& _reader) 
deba@1036
   373
	: map(_map), reader(_reader) {}
deba@1036
   374
deba@1036
   375
      virtual void read(istream& is, const Item& item) {
deba@1036
   376
	Value value;
deba@1036
   377
	reader.read(is, value);
deba@1036
   378
	map.set(item, value);
deba@1036
   379
	typename Inverse::iterator it = inverse.find(value);
deba@1036
   380
	if (it == inverse.end()) {
deba@1036
   381
	  inverse.insert(make_pair(value, item));
deba@1036
   382
	} else {
deba@1036
   383
	  throw DataFormatException("Multiple ID occurence");
deba@1036
   384
	}
deba@1036
   385
      }
deba@1036
   386
deba@1036
   387
      virtual Item read(istream& is) {
deba@1036
   388
	Value value;
deba@1036
   389
	reader.read(is, value);	
deba@1036
   390
	typename Inverse::const_iterator it = inverse.find(value);
deba@1036
   391
	if (it != inverse.end()) {
deba@1036
   392
	  return it->second;
deba@1036
   393
	} else {
deba@1036
   394
	  throw DataFormatException("Invalid ID");
deba@1036
   395
	}
deba@1036
   396
      }      
deba@1036
   397
    };
deba@1036
   398
deba@1036
   399
    template <typename _Item, typename _Reader>
deba@1036
   400
    class SkipReaderInverter : public InverterBase<_Item> {
deba@1036
   401
    public:
deba@1036
   402
      typedef _Item Item;
deba@1036
   403
      typedef _Reader Reader;
deba@1036
   404
      typedef typename Reader::Value Value;
deba@1036
   405
      typedef std::map<Value, Item> Inverse;
deba@1036
   406
deba@1036
   407
      Reader reader;
deba@1036
   408
deba@1036
   409
      SkipReaderInverter(const Reader& _reader) 
deba@1036
   410
	: reader(_reader) {}
deba@1036
   411
deba@1036
   412
      virtual void read(istream& is, const Item& item) {
deba@1036
   413
	Value value;
deba@1036
   414
	reader.read(is, value);
deba@1036
   415
	typename Inverse::iterator it = inverse.find(value);
deba@1036
   416
	if (it == inverse.end()) {
deba@1036
   417
	  inverse.insert(make_pair(value, item));
deba@1036
   418
	} else {
deba@1036
   419
	  throw DataFormatException("Multiple ID occurence");
deba@1036
   420
	}
deba@1036
   421
      }
deba@1036
   422
deba@1036
   423
      virtual Item read(istream& is) {
deba@1036
   424
	Value value;
deba@1036
   425
	reader.read(is, value);	
deba@1036
   426
	typename Inverse::const_iterator it = inverse.find(value);
deba@1036
   427
	if (it != inverse.end()) {
deba@1036
   428
	  return it->second;
deba@1036
   429
	} else {
deba@1036
   430
	  throw DataFormatException("Invalid ID");
deba@1036
   431
	}
deba@1036
   432
      }      
deba@1036
   433
    private:
deba@1036
   434
      Inverse inverse;
deba@1036
   435
    };
deba@1036
   436
deba@1032
   437
deba@1032
   438
    template <typename _Item>    
deba@1036
   439
    class ReaderBase {
deba@1032
   440
    public:
deba@1032
   441
      typedef _Item Item;
deba@1032
   442
      virtual void read(istream& is, const Item& item) = 0;
deba@1036
   443
      virtual InverterBase<_Item>* getInverter() = 0;
deba@1032
   444
    };
deba@1036
   445
deba@1032
   446
    template <typename _Item, typename _Map, typename _Reader>
deba@1036
   447
    class MapReader : public ReaderBase<_Item> {
deba@1032
   448
    public:
deba@1032
   449
      typedef _Map Map;
deba@1032
   450
      typedef _Reader Reader;
deba@1036
   451
      typedef typename Reader::Value Value;
deba@1032
   452
      typedef _Item Item;
deba@1032
   453
      
deba@1032
   454
      Map& map;
deba@1032
   455
      Reader reader;
deba@1032
   456
deba@1032
   457
      MapReader(Map& _map, const Reader& _reader) 
deba@1032
   458
	: map(_map), reader(_reader) {}
deba@1032
   459
deba@1032
   460
deba@1032
   461
      virtual void read(istream& is, const Item& item) {
deba@1036
   462
	Value value;
deba@1032
   463
	reader.read(is, value);
deba@1032
   464
	map.set(item, value);
deba@1032
   465
      }
deba@1036
   466
deba@1036
   467
      virtual InverterBase<_Item>* getInverter() {
deba@1036
   468
	return new MapReaderInverter<Item, Map, Reader>(map, reader);
deba@1036
   469
      }
deba@1032
   470
    };
deba@1032
   471
deba@1036
   472
deba@1036
   473
    template <typename _Item, typename _Reader>
deba@1036
   474
    class SkipReader : public ReaderBase<_Item> {
deba@1036
   475
    public:
deba@1036
   476
      typedef _Reader Reader;
deba@1036
   477
      typedef typename Reader::Value Value;
deba@1036
   478
      typedef _Item Item;
deba@1036
   479
deba@1036
   480
      Reader reader;
deba@1036
   481
      SkipReader(const Reader& _reader) : reader(_reader) {}
deba@1036
   482
deba@1036
   483
      virtual void read(istream& is, const Item& item) {
deba@1036
   484
	Value value;
deba@1036
   485
	reader.read(is, value);
deba@1036
   486
      }      
deba@1036
   487
deba@1036
   488
      virtual InverterBase<Item>* getInverter() {
deba@1036
   489
	return new SkipReaderInverter<Item, Reader>(reader);
deba@1036
   490
      }
deba@1036
   491
    };
deba@1036
   492
deba@1036
   493
deba@1036
   494
    typedef std::map<std::string, ReaderBase<Node>* > NodeReaders;
deba@1032
   495
    NodeReaders node_readers;
deba@1032
   496
deba@1036
   497
    typedef std::map<std::string, ReaderBase<Edge>* > EdgeReaders;
deba@1032
   498
    EdgeReaders edge_readers;
deba@1032
   499
deba@1036
   500
    std::istream& is;
deba@1036
   501
    Graph& graph;
deba@1036
   502
deba@1036
   503
    SkipReader<Node, DefaultReader> nodeSkipper;
deba@1036
   504
    SkipReader<Edge, DefaultReader> edgeSkipper;
deba@1036
   505
deba@1032
   506
  };
deba@1032
   507
deba@1032
   508
}