lemon/hypercube_graph.h
author Alpar Juttner <alpar@cs.elte.hu>
Sat, 10 Aug 2013 14:27:19 +0200
branch1.2
changeset 1278 92d53f86d1a9
parent 834 c2230649a493
parent 833 e20173729589
permissions -rw-r--r--
LEMON 1.2.4 released (e13061207f85 tagged as r1.2.4)
kpeter@376
     1
/* -*- mode: C++; indent-tabs-mode: nil; -*-
kpeter@376
     2
 *
kpeter@376
     3
 * This file is a part of LEMON, a generic C++ optimization library.
kpeter@376
     4
 *
alpar@463
     5
 * Copyright (C) 2003-2009
kpeter@376
     6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
kpeter@376
     7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
kpeter@376
     8
 *
kpeter@376
     9
 * Permission to use, modify and distribute this software is granted
kpeter@376
    10
 * provided that this copyright notice appears in all copies. For
kpeter@376
    11
 * precise terms see the accompanying LICENSE file.
kpeter@376
    12
 *
kpeter@376
    13
 * This software is provided "AS IS" with no warranty of any kind,
kpeter@376
    14
 * express or implied, and with no claim as to its suitability for any
kpeter@376
    15
 * purpose.
kpeter@376
    16
 *
kpeter@376
    17
 */
kpeter@376
    18
kpeter@376
    19
#ifndef HYPERCUBE_GRAPH_H
kpeter@376
    20
#define HYPERCUBE_GRAPH_H
kpeter@376
    21
kpeter@376
    22
#include <vector>
kpeter@376
    23
#include <lemon/core.h>
kpeter@377
    24
#include <lemon/assert.h>
kpeter@376
    25
#include <lemon/bits/graph_extender.h>
kpeter@376
    26
kpeter@376
    27
///\ingroup graphs
kpeter@376
    28
///\file
kpeter@377
    29
///\brief HypercubeGraph class.
kpeter@376
    30
kpeter@376
    31
namespace lemon {
kpeter@376
    32
kpeter@377
    33
  class HypercubeGraphBase {
kpeter@376
    34
kpeter@376
    35
  public:
kpeter@376
    36
kpeter@377
    37
    typedef HypercubeGraphBase Graph;
kpeter@376
    38
kpeter@376
    39
    class Node;
kpeter@377
    40
    class Edge;
kpeter@376
    41
    class Arc;
kpeter@376
    42
kpeter@376
    43
  public:
kpeter@376
    44
kpeter@377
    45
    HypercubeGraphBase() {}
kpeter@376
    46
kpeter@376
    47
  protected:
kpeter@376
    48
kpeter@376
    49
    void construct(int dim) {
kpeter@377
    50
      LEMON_ASSERT(dim >= 1, "The number of dimensions must be at least 1.");
kpeter@376
    51
      _dim = dim;
kpeter@377
    52
      _node_num = 1 << dim;
alpar@385
    53
      _edge_num = dim * (1 << (dim-1));
kpeter@376
    54
    }
kpeter@376
    55
kpeter@376
    56
  public:
kpeter@376
    57
kpeter@376
    58
    typedef True NodeNumTag;
kpeter@377
    59
    typedef True EdgeNumTag;
kpeter@376
    60
    typedef True ArcNumTag;
kpeter@376
    61
kpeter@377
    62
    int nodeNum() const { return _node_num; }
kpeter@377
    63
    int edgeNum() const { return _edge_num; }
kpeter@377
    64
    int arcNum() const { return 2 * _edge_num; }
kpeter@376
    65
kpeter@377
    66
    int maxNodeId() const { return _node_num - 1; }
kpeter@377
    67
    int maxEdgeId() const { return _edge_num - 1; }
kpeter@377
    68
    int maxArcId() const { return 2 * _edge_num - 1; }
kpeter@376
    69
kpeter@377
    70
    static Node nodeFromId(int id) { return Node(id); }
kpeter@377
    71
    static Edge edgeFromId(int id) { return Edge(id); }
kpeter@377
    72
    static Arc arcFromId(int id) { return Arc(id); }
kpeter@377
    73
kpeter@377
    74
    static int id(Node node) { return node._id; }
kpeter@377
    75
    static int id(Edge edge) { return edge._id; }
kpeter@377
    76
    static int id(Arc arc) { return arc._id; }
kpeter@377
    77
kpeter@377
    78
    Node u(Edge edge) const {
alpar@385
    79
      int base = edge._id & ((1 << (_dim-1)) - 1);
alpar@385
    80
      int k = edge._id >> (_dim-1);
alpar@385
    81
      return ((base >> k) << (k+1)) | (base & ((1 << k) - 1));
kpeter@376
    82
    }
kpeter@376
    83
kpeter@377
    84
    Node v(Edge edge) const {
alpar@385
    85
      int base = edge._id & ((1 << (_dim-1)) - 1);
alpar@385
    86
      int k = edge._id >> (_dim-1);
alpar@385
    87
      return ((base >> k) << (k+1)) | (base & ((1 << k) - 1)) | (1 << k);
kpeter@376
    88
    }
kpeter@376
    89
kpeter@377
    90
    Node source(Arc arc) const {
kpeter@377
    91
      return (arc._id & 1) == 1 ? u(arc) : v(arc);
kpeter@377
    92
    }
kpeter@376
    93
kpeter@377
    94
    Node target(Arc arc) const {
kpeter@377
    95
      return (arc._id & 1) == 1 ? v(arc) : u(arc);
kpeter@377
    96
    }
kpeter@376
    97
kpeter@377
    98
    typedef True FindEdgeTag;
kpeter@377
    99
    typedef True FindArcTag;
kpeter@377
   100
kpeter@377
   101
    Edge findEdge(Node u, Node v, Edge prev = INVALID) const {
kpeter@377
   102
      if (prev != INVALID) return INVALID;
kpeter@377
   103
      int d = u._id ^ v._id;
kpeter@377
   104
      int k = 0;
kpeter@377
   105
      if (d == 0) return INVALID;
kpeter@377
   106
      for ( ; (d & 1) == 0; d >>= 1) ++k;
kpeter@377
   107
      if (d >> 1 != 0) return INVALID;
alpar@385
   108
      return (k << (_dim-1)) | ((u._id >> (k+1)) << k) |
alpar@385
   109
        (u._id & ((1 << k) - 1));
kpeter@377
   110
    }
kpeter@377
   111
kpeter@377
   112
    Arc findArc(Node u, Node v, Arc prev = INVALID) const {
kpeter@377
   113
      Edge edge = findEdge(u, v, prev);
kpeter@377
   114
      if (edge == INVALID) return INVALID;
alpar@385
   115
      int k = edge._id >> (_dim-1);
kpeter@377
   116
      return ((u._id >> k) & 1) == 1 ? edge._id << 1 : (edge._id << 1) | 1;
kpeter@377
   117
    }
kpeter@376
   118
kpeter@376
   119
    class Node {
kpeter@377
   120
      friend class HypercubeGraphBase;
kpeter@377
   121
kpeter@376
   122
    protected:
kpeter@377
   123
      int _id;
kpeter@377
   124
      Node(int id) : _id(id) {}
kpeter@376
   125
    public:
kpeter@376
   126
      Node() {}
kpeter@377
   127
      Node (Invalid) : _id(-1) {}
kpeter@377
   128
      bool operator==(const Node node) const {return _id == node._id;}
kpeter@377
   129
      bool operator!=(const Node node) const {return _id != node._id;}
kpeter@377
   130
      bool operator<(const Node node) const {return _id < node._id;}
kpeter@377
   131
    };
kpeter@377
   132
kpeter@377
   133
    class Edge {
kpeter@377
   134
      friend class HypercubeGraphBase;
kpeter@377
   135
      friend class Arc;
kpeter@377
   136
kpeter@377
   137
    protected:
kpeter@377
   138
      int _id;
kpeter@377
   139
kpeter@377
   140
      Edge(int id) : _id(id) {}
kpeter@377
   141
kpeter@377
   142
    public:
kpeter@377
   143
      Edge() {}
kpeter@377
   144
      Edge (Invalid) : _id(-1) {}
kpeter@377
   145
      bool operator==(const Edge edge) const {return _id == edge._id;}
kpeter@377
   146
      bool operator!=(const Edge edge) const {return _id != edge._id;}
kpeter@377
   147
      bool operator<(const Edge edge) const {return _id < edge._id;}
kpeter@376
   148
    };
kpeter@376
   149
kpeter@376
   150
    class Arc {
kpeter@377
   151
      friend class HypercubeGraphBase;
kpeter@377
   152
kpeter@376
   153
    protected:
kpeter@377
   154
      int _id;
kpeter@377
   155
kpeter@377
   156
      Arc(int id) : _id(id) {}
kpeter@377
   157
kpeter@376
   158
    public:
kpeter@377
   159
      Arc() {}
kpeter@377
   160
      Arc (Invalid) : _id(-1) {}
kpeter@377
   161
      operator Edge() const { return _id != -1 ? Edge(_id >> 1) : INVALID; }
kpeter@377
   162
      bool operator==(const Arc arc) const {return _id == arc._id;}
kpeter@377
   163
      bool operator!=(const Arc arc) const {return _id != arc._id;}
kpeter@377
   164
      bool operator<(const Arc arc) const {return _id < arc._id;}
kpeter@376
   165
    };
kpeter@376
   166
kpeter@376
   167
    void first(Node& node) const {
kpeter@377
   168
      node._id = _node_num - 1;
kpeter@376
   169
    }
kpeter@376
   170
kpeter@376
   171
    static void next(Node& node) {
kpeter@377
   172
      --node._id;
kpeter@377
   173
    }
kpeter@377
   174
kpeter@377
   175
    void first(Edge& edge) const {
kpeter@377
   176
      edge._id = _edge_num - 1;
kpeter@377
   177
    }
kpeter@377
   178
kpeter@377
   179
    static void next(Edge& edge) {
kpeter@377
   180
      --edge._id;
kpeter@376
   181
    }
kpeter@376
   182
kpeter@376
   183
    void first(Arc& arc) const {
kpeter@377
   184
      arc._id = 2 * _edge_num - 1;
kpeter@376
   185
    }
kpeter@376
   186
kpeter@376
   187
    static void next(Arc& arc) {
kpeter@377
   188
      --arc._id;
kpeter@377
   189
    }
kpeter@377
   190
kpeter@377
   191
    void firstInc(Edge& edge, bool& dir, const Node& node) const {
kpeter@377
   192
      edge._id = node._id >> 1;
kpeter@377
   193
      dir = (node._id & 1) == 0;
kpeter@377
   194
    }
kpeter@377
   195
kpeter@377
   196
    void nextInc(Edge& edge, bool& dir) const {
kpeter@377
   197
      Node n = dir ? u(edge) : v(edge);
alpar@385
   198
      int k = (edge._id >> (_dim-1)) + 1;
kpeter@377
   199
      if (k < _dim) {
alpar@385
   200
        edge._id = (k << (_dim-1)) |
alpar@385
   201
          ((n._id >> (k+1)) << k) | (n._id & ((1 << k) - 1));
kpeter@377
   202
        dir = ((n._id >> k) & 1) == 0;
kpeter@377
   203
      } else {
kpeter@377
   204
        edge._id = -1;
kpeter@377
   205
        dir = true;
kpeter@377
   206
      }
kpeter@376
   207
    }
kpeter@376
   208
kpeter@376
   209
    void firstOut(Arc& arc, const Node& node) const {
kpeter@377
   210
      arc._id = ((node._id >> 1) << 1) | (~node._id & 1);
kpeter@376
   211
    }
kpeter@376
   212
kpeter@376
   213
    void nextOut(Arc& arc) const {
kpeter@377
   214
      Node n = (arc._id & 1) == 1 ? u(arc) : v(arc);
kpeter@377
   215
      int k = (arc._id >> _dim) + 1;
kpeter@377
   216
      if (k < _dim) {
alpar@385
   217
        arc._id = (k << (_dim-1)) |
alpar@385
   218
          ((n._id >> (k+1)) << k) | (n._id & ((1 << k) - 1));
kpeter@377
   219
        arc._id = (arc._id << 1) | (~(n._id >> k) & 1);
kpeter@377
   220
      } else {
kpeter@377
   221
        arc._id = -1;
kpeter@377
   222
      }
kpeter@376
   223
    }
kpeter@376
   224
kpeter@376
   225
    void firstIn(Arc& arc, const Node& node) const {
kpeter@377
   226
      arc._id = ((node._id >> 1) << 1) | (node._id & 1);
kpeter@376
   227
    }
kpeter@376
   228
kpeter@376
   229
    void nextIn(Arc& arc) const {
kpeter@377
   230
      Node n = (arc._id & 1) == 1 ? v(arc) : u(arc);
kpeter@377
   231
      int k = (arc._id >> _dim) + 1;
kpeter@377
   232
      if (k < _dim) {
alpar@385
   233
        arc._id = (k << (_dim-1)) |
alpar@385
   234
          ((n._id >> (k+1)) << k) | (n._id & ((1 << k) - 1));
kpeter@377
   235
        arc._id = (arc._id << 1) | ((n._id >> k) & 1);
kpeter@376
   236
      } else {
kpeter@377
   237
        arc._id = -1;
kpeter@376
   238
      }
kpeter@376
   239
    }
kpeter@376
   240
kpeter@377
   241
    static bool direction(Arc arc) {
kpeter@377
   242
      return (arc._id & 1) == 1;
kpeter@377
   243
    }
kpeter@377
   244
kpeter@377
   245
    static Arc direct(Edge edge, bool dir) {
kpeter@377
   246
      return Arc((edge._id << 1) | (dir ? 1 : 0));
kpeter@377
   247
    }
kpeter@377
   248
kpeter@376
   249
    int dimension() const {
kpeter@376
   250
      return _dim;
kpeter@376
   251
    }
kpeter@376
   252
kpeter@376
   253
    bool projection(Node node, int n) const {
kpeter@377
   254
      return static_cast<bool>(node._id & (1 << n));
kpeter@377
   255
    }
kpeter@377
   256
kpeter@377
   257
    int dimension(Edge edge) const {
alpar@385
   258
      return edge._id >> (_dim-1);
kpeter@376
   259
    }
kpeter@376
   260
kpeter@376
   261
    int dimension(Arc arc) const {
kpeter@377
   262
      return arc._id >> _dim;
kpeter@376
   263
    }
kpeter@376
   264
kpeter@825
   265
    static int index(Node node) {
kpeter@377
   266
      return node._id;
kpeter@376
   267
    }
kpeter@376
   268
kpeter@376
   269
    Node operator()(int ix) const {
kpeter@376
   270
      return Node(ix);
kpeter@376
   271
    }
kpeter@376
   272
kpeter@376
   273
  private:
kpeter@377
   274
    int _dim;
kpeter@377
   275
    int _node_num, _edge_num;
kpeter@376
   276
  };
kpeter@376
   277
kpeter@376
   278
kpeter@377
   279
  typedef GraphExtender<HypercubeGraphBase> ExtendedHypercubeGraphBase;
kpeter@376
   280
kpeter@377
   281
  /// \ingroup graphs
kpeter@376
   282
  ///
kpeter@377
   283
  /// \brief Hypercube graph class
kpeter@376
   284
  ///
kpeter@782
   285
  /// HypercubeGraph implements a special graph type. The nodes of the
kpeter@782
   286
  /// graph are indexed with integers having at most \c dim binary digits.
kpeter@377
   287
  /// Two nodes are connected in the graph if and only if their indices
kpeter@377
   288
  /// differ only on one position in the binary form.
kpeter@782
   289
  /// This class is completely static and it needs constant memory space.
kpeter@833
   290
  /// Thus you can neither add nor delete nodes or edges, however,
kpeter@784
   291
  /// the structure can be resized using resize().
kpeter@782
   292
  ///
kpeter@782
   293
  /// This type fully conforms to the \ref concepts::Graph "Graph concept".
kpeter@782
   294
  /// Most of its member functions and nested classes are documented
kpeter@782
   295
  /// only in the concept class.
kpeter@376
   296
  ///
kpeter@834
   297
  /// This class provides constant time counting for nodes, edges and arcs.
kpeter@834
   298
  ///
kpeter@377
   299
  /// \note The type of the indices is chosen to \c int for efficiency
kpeter@377
   300
  /// reasons. Thus the maximum dimension of this implementation is 26
kpeter@377
   301
  /// (assuming that the size of \c int is 32 bit).
kpeter@377
   302
  class HypercubeGraph : public ExtendedHypercubeGraphBase {
kpeter@664
   303
    typedef ExtendedHypercubeGraphBase Parent;
kpeter@664
   304
kpeter@376
   305
  public:
kpeter@376
   306
kpeter@377
   307
    /// \brief Constructs a hypercube graph with \c dim dimensions.
kpeter@376
   308
    ///
kpeter@377
   309
    /// Constructs a hypercube graph with \c dim dimensions.
kpeter@377
   310
    HypercubeGraph(int dim) { construct(dim); }
kpeter@376
   311
kpeter@784
   312
    /// \brief Resizes the graph
kpeter@784
   313
    ///
kpeter@784
   314
    /// This function resizes the graph. It fully destroys and
kpeter@784
   315
    /// rebuilds the structure, therefore the maps of the graph will be
kpeter@784
   316
    /// reallocated automatically and the previous values will be lost.
kpeter@784
   317
    void resize(int dim) {
kpeter@784
   318
      Parent::notifier(Arc()).clear();
kpeter@784
   319
      Parent::notifier(Edge()).clear();
kpeter@784
   320
      Parent::notifier(Node()).clear();
kpeter@784
   321
      construct(dim);
kpeter@784
   322
      Parent::notifier(Node()).build();
kpeter@784
   323
      Parent::notifier(Edge()).build();
kpeter@784
   324
      Parent::notifier(Arc()).build();
kpeter@784
   325
    }
kpeter@784
   326
kpeter@377
   327
    /// \brief The number of dimensions.
kpeter@376
   328
    ///
kpeter@377
   329
    /// Gives back the number of dimensions.
kpeter@376
   330
    int dimension() const {
kpeter@376
   331
      return Parent::dimension();
kpeter@376
   332
    }
kpeter@376
   333
kpeter@377
   334
    /// \brief Returns \c true if the n'th bit of the node is one.
kpeter@376
   335
    ///
kpeter@377
   336
    /// Returns \c true if the n'th bit of the node is one.
kpeter@376
   337
    bool projection(Node node, int n) const {
kpeter@376
   338
      return Parent::projection(node, n);
kpeter@376
   339
    }
kpeter@376
   340
kpeter@377
   341
    /// \brief The dimension id of an edge.
kpeter@376
   342
    ///
kpeter@377
   343
    /// Gives back the dimension id of the given edge.
kpeter@782
   344
    /// It is in the range <tt>[0..dim-1]</tt>.
kpeter@377
   345
    int dimension(Edge edge) const {
kpeter@377
   346
      return Parent::dimension(edge);
kpeter@377
   347
    }
kpeter@377
   348
kpeter@377
   349
    /// \brief The dimension id of an arc.
kpeter@377
   350
    ///
kpeter@377
   351
    /// Gives back the dimension id of the given arc.
kpeter@782
   352
    /// It is in the range <tt>[0..dim-1]</tt>.
kpeter@376
   353
    int dimension(Arc arc) const {
kpeter@376
   354
      return Parent::dimension(arc);
kpeter@376
   355
    }
kpeter@376
   356
kpeter@377
   357
    /// \brief The index of a node.
kpeter@376
   358
    ///
kpeter@377
   359
    /// Gives back the index of the given node.
kpeter@377
   360
    /// The lower bits of the integer describes the node.
kpeter@825
   361
    static int index(Node node) {
kpeter@376
   362
      return Parent::index(node);
kpeter@376
   363
    }
kpeter@376
   364
kpeter@377
   365
    /// \brief Gives back a node by its index.
kpeter@376
   366
    ///
kpeter@377
   367
    /// Gives back a node by its index.
kpeter@376
   368
    Node operator()(int ix) const {
kpeter@376
   369
      return Parent::operator()(ix);
kpeter@376
   370
    }
kpeter@376
   371
kpeter@376
   372
    /// \brief Number of nodes.
kpeter@376
   373
    int nodeNum() const { return Parent::nodeNum(); }
kpeter@377
   374
    /// \brief Number of edges.
kpeter@377
   375
    int edgeNum() const { return Parent::edgeNum(); }
kpeter@376
   376
    /// \brief Number of arcs.
kpeter@376
   377
    int arcNum() const { return Parent::arcNum(); }
kpeter@376
   378
kpeter@376
   379
    /// \brief Linear combination map.
kpeter@376
   380
    ///
kpeter@377
   381
    /// This map makes possible to give back a linear combination
kpeter@377
   382
    /// for each node. It works like the \c std::accumulate function,
kpeter@377
   383
    /// so it accumulates the \c bf binary function with the \c fv first
kpeter@377
   384
    /// value. The map accumulates only on that positions (dimensions)
kpeter@377
   385
    /// where the index of the node is one. The values that have to be
kpeter@377
   386
    /// accumulated should be given by the \c begin and \c end iterators
kpeter@377
   387
    /// and the length of this range should be equal to the dimension
kpeter@377
   388
    /// number of the graph.
kpeter@376
   389
    ///
kpeter@376
   390
    ///\code
kpeter@376
   391
    /// const int DIM = 3;
kpeter@377
   392
    /// HypercubeGraph graph(DIM);
kpeter@376
   393
    /// dim2::Point<double> base[DIM];
kpeter@376
   394
    /// for (int k = 0; k < DIM; ++k) {
kpeter@376
   395
    ///   base[k].x = rnd();
kpeter@376
   396
    ///   base[k].y = rnd();
kpeter@376
   397
    /// }
kpeter@377
   398
    /// HypercubeGraph::HyperMap<dim2::Point<double> >
kpeter@377
   399
    ///   pos(graph, base, base + DIM, dim2::Point<double>(0.0, 0.0));
kpeter@376
   400
    ///\endcode
kpeter@376
   401
    ///
kpeter@377
   402
    /// \see HypercubeGraph
kpeter@376
   403
    template <typename T, typename BF = std::plus<T> >
kpeter@376
   404
    class HyperMap {
kpeter@376
   405
    public:
kpeter@376
   406
kpeter@377
   407
      /// \brief The key type of the map
kpeter@376
   408
      typedef Node Key;
kpeter@377
   409
      /// \brief The value type of the map
kpeter@376
   410
      typedef T Value;
kpeter@376
   411
kpeter@376
   412
      /// \brief Constructor for HyperMap.
kpeter@376
   413
      ///
kpeter@377
   414
      /// Construct a HyperMap for the given graph. The values that have
kpeter@377
   415
      /// to be accumulated should be given by the \c begin and \c end
kpeter@377
   416
      /// iterators and the length of this range should be equal to the
kpeter@377
   417
      /// dimension number of the graph.
kpeter@376
   418
      ///
kpeter@377
   419
      /// This map accumulates the \c bf binary function with the \c fv
kpeter@377
   420
      /// first value on that positions (dimensions) where the index of
kpeter@377
   421
      /// the node is one.
kpeter@376
   422
      template <typename It>
kpeter@377
   423
      HyperMap(const Graph& graph, It begin, It end,
kpeter@377
   424
               T fv = 0, const BF& bf = BF())
kpeter@377
   425
        : _graph(graph), _values(begin, end), _first_value(fv), _bin_func(bf)
kpeter@376
   426
      {
kpeter@377
   427
        LEMON_ASSERT(_values.size() == graph.dimension(),
kpeter@377
   428
                     "Wrong size of range");
kpeter@376
   429
      }
kpeter@376
   430
kpeter@377
   431
      /// \brief The partial accumulated value.
kpeter@376
   432
      ///
kpeter@376
   433
      /// Gives back the partial accumulated value.
kpeter@377
   434
      Value operator[](const Key& k) const {
kpeter@376
   435
        Value val = _first_value;
kpeter@376
   436
        int id = _graph.index(k);
kpeter@376
   437
        int n = 0;
kpeter@376
   438
        while (id != 0) {
kpeter@376
   439
          if (id & 1) {
kpeter@376
   440
            val = _bin_func(val, _values[n]);
kpeter@376
   441
          }
kpeter@376
   442
          id >>= 1;
kpeter@376
   443
          ++n;
kpeter@376
   444
        }
kpeter@376
   445
        return val;
kpeter@376
   446
      }
kpeter@376
   447
kpeter@376
   448
    private:
kpeter@377
   449
      const Graph& _graph;
kpeter@376
   450
      std::vector<T> _values;
kpeter@376
   451
      T _first_value;
kpeter@376
   452
      BF _bin_func;
kpeter@376
   453
    };
kpeter@376
   454
kpeter@376
   455
  };
kpeter@376
   456
kpeter@376
   457
}
kpeter@376
   458
kpeter@376
   459
#endif