test/path_test.cc
author Balazs Dezso <deba@google.com>
Fri, 22 Jan 2021 10:55:32 +0100
changeset 1208 c6aa2cc1af04
parent 1092 dceba191c00d
permissions -rw-r--r--
Factor out recursion from weighted matching algorithms (#638)
alpar@209
     1
/* -*- mode: C++; indent-tabs-mode: nil; -*-
alpar@96
     2
 *
alpar@209
     3
 * This file is a part of LEMON, a generic C++ optimization library.
alpar@96
     4
 *
alpar@1092
     5
 * Copyright (C) 2003-2013
alpar@96
     6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
alpar@96
     7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
alpar@96
     8
 *
alpar@96
     9
 * Permission to use, modify and distribute this software is granted
alpar@96
    10
 * provided that this copyright notice appears in all copies. For
alpar@96
    11
 * precise terms see the accompanying LICENSE file.
alpar@96
    12
 *
alpar@96
    13
 * This software is provided "AS IS" with no warranty of any kind,
alpar@96
    14
 * express or implied, and with no claim as to its suitability for any
alpar@96
    15
 * purpose.
alpar@96
    16
 *
alpar@96
    17
 */
alpar@96
    18
alpar@96
    19
#include <string>
alpar@96
    20
#include <iostream>
alpar@96
    21
alpar@96
    22
#include <lemon/concepts/path.h>
alpar@96
    23
#include <lemon/concepts/digraph.h>
kpeter@1044
    24
#include <lemon/concept_check.h>
alpar@96
    25
alpar@96
    26
#include <lemon/path.h>
alpar@96
    27
#include <lemon/list_graph.h>
alpar@96
    28
alpar@96
    29
#include "test_tools.h"
alpar@96
    30
alpar@96
    31
using namespace std;
alpar@96
    32
using namespace lemon;
alpar@96
    33
kpeter@1044
    34
template <typename GR>
kpeter@1044
    35
void checkConcepts() {
kpeter@1044
    36
  checkConcept<concepts::Path<GR>, concepts::Path<GR> >();
kpeter@1044
    37
  checkConcept<concepts::Path<GR>, Path<GR> >();
kpeter@1044
    38
  checkConcept<concepts::Path<GR>, SimplePath<GR> >();
kpeter@1044
    39
  checkConcept<concepts::Path<GR>, StaticPath<GR> >();
kpeter@1044
    40
  checkConcept<concepts::Path<GR>, ListPath<GR> >();
kpeter@1044
    41
}
kpeter@1044
    42
kpeter@1044
    43
// Conecpt checking for path structures
kpeter@1044
    44
void checkPathConcepts() {
kpeter@1044
    45
  checkConcepts<concepts::Digraph>();
kpeter@1044
    46
  checkConcepts<ListDigraph>();
alpar@96
    47
}
alpar@96
    48
alpar@990
    49
// Check if proper copy consructor is called (use valgrind for testing)
kpeter@1044
    50
template <typename GR, typename P1, typename P2>
kpeter@1044
    51
void checkCopy(typename GR::Arc a) {
kpeter@1044
    52
  P1 p;
kpeter@1044
    53
  p.addBack(a);
kpeter@1044
    54
  P1 q;
kpeter@1044
    55
  q = p;
kpeter@1044
    56
  P1 r(p);
kpeter@1044
    57
  P2 q2;
kpeter@1044
    58
  q2 = p;
kpeter@1044
    59
  P2 r2(p);
kpeter@1044
    60
}
kpeter@1044
    61
kpeter@1044
    62
// Tests for copy constructors and assignment operators of paths
kpeter@1044
    63
void checkPathCopy() {
alpar@990
    64
  ListDigraph g;
kpeter@1044
    65
  ListDigraph::Arc a = g.addArc(g.addNode(), g.addNode());
kpeter@1044
    66
kpeter@1044
    67
  typedef Path<ListDigraph> Path1;
kpeter@1044
    68
  typedef SimplePath<ListDigraph> Path2;
kpeter@1044
    69
  typedef ListPath<ListDigraph> Path3;
kpeter@1044
    70
  typedef StaticPath<ListDigraph> Path4;
kpeter@1044
    71
  checkCopy<ListDigraph, Path1, Path2>(a);
kpeter@1044
    72
  checkCopy<ListDigraph, Path1, Path3>(a);
kpeter@1044
    73
  checkCopy<ListDigraph, Path1, Path4>(a);
kpeter@1044
    74
  checkCopy<ListDigraph, Path2, Path1>(a);
kpeter@1044
    75
  checkCopy<ListDigraph, Path2, Path3>(a);
kpeter@1044
    76
  checkCopy<ListDigraph, Path2, Path4>(a);
kpeter@1044
    77
  checkCopy<ListDigraph, Path3, Path1>(a);
kpeter@1044
    78
  checkCopy<ListDigraph, Path3, Path2>(a);
kpeter@1044
    79
  checkCopy<ListDigraph, Path3, Path4>(a);
alpar@990
    80
}
kpeter@1044
    81
kpeter@1044
    82
// Class for testing path functions
kpeter@1044
    83
class CheckPathFunctions {
kpeter@1044
    84
  typedef ListDigraph GR;
kpeter@1044
    85
  DIGRAPH_TYPEDEFS(GR);
kpeter@1044
    86
  GR gr;
kpeter@1044
    87
  const GR& cgr;
kpeter@1044
    88
  Node n1, n2, n3, n4;
kpeter@1044
    89
  Node tmp_n;
kpeter@1044
    90
  Arc a1, a2, a3, a4;
kpeter@1044
    91
  Arc tmp_a;
kpeter@1044
    92
kpeter@1044
    93
public:
kpeter@1044
    94
kpeter@1044
    95
  CheckPathFunctions() : cgr(gr) {
kpeter@1044
    96
    n1 = gr.addNode();
kpeter@1044
    97
    n2 = gr.addNode();
kpeter@1044
    98
    n3 = gr.addNode();
kpeter@1044
    99
    n4 = gr.addNode();
kpeter@1044
   100
    a1 = gr.addArc(n1, n2);
kpeter@1044
   101
    a2 = gr.addArc(n2, n3);
kpeter@1044
   102
    a3 = gr.addArc(n3, n4);
kpeter@1044
   103
    a4 = gr.addArc(n4, n1);
kpeter@1044
   104
  }
kpeter@1044
   105
kpeter@1044
   106
  void run() {
kpeter@1044
   107
    checkBackAndFrontInsertablePath<Path<GR> >();
kpeter@1044
   108
    checkBackAndFrontInsertablePath<ListPath<GR> >();
kpeter@1044
   109
    checkBackInsertablePath<SimplePath<GR> >();
kpeter@1044
   110
kpeter@1202
   111
    checkSubscriptOperator<Path<GR> >();
kpeter@1202
   112
    checkSubscriptOperator<SimplePath<GR> >();
kpeter@1202
   113
    checkSubscriptOperator<StaticPath<GR> >();
kpeter@1202
   114
    checkSubscriptOperator<ListPath<GR> >();
kpeter@1202
   115
kpeter@1044
   116
    checkListPathSplitAndSplice();
kpeter@1044
   117
  }
kpeter@1044
   118
kpeter@1044
   119
private:
kpeter@1044
   120
kpeter@1044
   121
  template <typename P>
kpeter@1044
   122
  void checkBackInsertablePath() {
kpeter@1044
   123
kpeter@1044
   124
    // Create and check empty path
kpeter@1044
   125
    P p;
kpeter@1044
   126
    const P& cp = p;
kpeter@1044
   127
    check(cp.empty(), "The path is not empty");
kpeter@1044
   128
    check(cp.length() == 0, "The path is not empty");
kpeter@1044
   129
//    check(cp.front() == INVALID, "Wrong front()");
kpeter@1044
   130
//    check(cp.back() == INVALID, "Wrong back()");
kpeter@1044
   131
    typename P::ArcIt ai(cp);
kpeter@1044
   132
    check(ai == INVALID, "Wrong ArcIt");
kpeter@1044
   133
    check(pathSource(cgr, cp) == INVALID, "Wrong pathSource()");
kpeter@1044
   134
    check(pathTarget(cgr, cp) == INVALID, "Wrong pathTarget()");
kpeter@1044
   135
    check(checkPath(cgr, cp), "Wrong checkPath()");
kpeter@1044
   136
    PathNodeIt<P> ni(cgr, cp);
kpeter@1044
   137
    check(ni == INVALID, "Wrong PathNodeIt");
kpeter@1044
   138
kpeter@1044
   139
    // Check single-arc path
kpeter@1044
   140
    p.addBack(a1);
kpeter@1044
   141
    check(!cp.empty(), "Wrong empty()");
kpeter@1044
   142
    check(cp.length() == 1, "Wrong length");
kpeter@1044
   143
    check(cp.front() == a1, "Wrong front()");
kpeter@1044
   144
    check(cp.back() == a1, "Wrong back()");
kpeter@1044
   145
    check(cp.nth(0) == a1, "Wrong nth()");
kpeter@1044
   146
    ai = cp.nthIt(0);
kpeter@1044
   147
    check((tmp_a = ai) == a1, "Wrong nthIt()");
kpeter@1044
   148
    check(++ai == INVALID, "Wrong nthIt()");
kpeter@1044
   149
    typename P::ArcIt ai2(cp);
kpeter@1044
   150
    check((tmp_a = ai2) == a1, "Wrong ArcIt");
kpeter@1044
   151
    check(++ai2 == INVALID, "Wrong ArcIt");
kpeter@1044
   152
    check(pathSource(cgr, cp) == n1, "Wrong pathSource()");
kpeter@1044
   153
    check(pathTarget(cgr, cp) == n2, "Wrong pathTarget()");
kpeter@1044
   154
    check(checkPath(cgr, cp), "Wrong checkPath()");
kpeter@1044
   155
    PathNodeIt<P> ni2(cgr, cp);
kpeter@1044
   156
    check((tmp_n = ni2) == n1, "Wrong PathNodeIt");
kpeter@1044
   157
    check((tmp_n = ++ni2) == n2, "Wrong PathNodeIt");
kpeter@1044
   158
    check(++ni2 == INVALID, "Wrong PathNodeIt");
kpeter@1044
   159
kpeter@1044
   160
    // Check adding more arcs
kpeter@1044
   161
    p.addBack(a2);
kpeter@1044
   162
    p.addBack(a3);
kpeter@1044
   163
    check(!cp.empty(), "Wrong empty()");
kpeter@1044
   164
    check(cp.length() == 3, "Wrong length");
kpeter@1044
   165
    check(cp.front() == a1, "Wrong front()");
kpeter@1044
   166
    check(cp.back() == a3, "Wrong back()");
kpeter@1044
   167
    check(cp.nth(0) == a1, "Wrong nth()");
kpeter@1044
   168
    check(cp.nth(1) == a2, "Wrong nth()");
kpeter@1044
   169
    check(cp.nth(2) == a3, "Wrong nth()");
kpeter@1044
   170
    typename P::ArcIt ai3(cp);
kpeter@1044
   171
    check((tmp_a = ai3) == a1, "Wrong ArcIt");
kpeter@1044
   172
    check((tmp_a = ++ai3) == a2, "Wrong nthIt()");
kpeter@1044
   173
    check((tmp_a = ++ai3) == a3, "Wrong nthIt()");
kpeter@1044
   174
    check(++ai3 == INVALID, "Wrong nthIt()");
kpeter@1044
   175
    ai = cp.nthIt(0);
kpeter@1044
   176
    check((tmp_a = ai) == a1, "Wrong nthIt()");
kpeter@1044
   177
    check((tmp_a = ++ai) == a2, "Wrong nthIt()");
kpeter@1044
   178
    ai = cp.nthIt(2);
kpeter@1044
   179
    check((tmp_a = ai) == a3, "Wrong nthIt()");
kpeter@1044
   180
    check(++ai == INVALID, "Wrong nthIt()");
kpeter@1044
   181
    check(pathSource(cgr, cp) == n1, "Wrong pathSource()");
kpeter@1044
   182
    check(pathTarget(cgr, cp) == n4, "Wrong pathTarget()");
kpeter@1044
   183
    check(checkPath(cgr, cp), "Wrong checkPath()");
kpeter@1044
   184
    PathNodeIt<P> ni3(cgr, cp);
kpeter@1044
   185
    check((tmp_n = ni3) == n1, "Wrong PathNodeIt");
kpeter@1044
   186
    check((tmp_n = ++ni3) == n2, "Wrong PathNodeIt");
kpeter@1044
   187
    check((tmp_n = ++ni3) == n3, "Wrong PathNodeIt");
kpeter@1044
   188
    check((tmp_n = ++ni3) == n4, "Wrong PathNodeIt");
kpeter@1044
   189
    check(++ni3 == INVALID, "Wrong PathNodeIt");
kpeter@1044
   190
kpeter@1044
   191
    // Check arc removal and addition
kpeter@1044
   192
    p.eraseBack();
kpeter@1044
   193
    p.eraseBack();
kpeter@1044
   194
    p.addBack(a2);
kpeter@1044
   195
    check(!cp.empty(), "Wrong empty()");
kpeter@1044
   196
    check(cp.length() == 2, "Wrong length");
kpeter@1044
   197
    check(cp.front() == a1, "Wrong front()");
kpeter@1044
   198
    check(cp.back() == a2, "Wrong back()");
kpeter@1044
   199
    check(pathSource(cgr, cp) == n1, "Wrong pathSource()");
kpeter@1044
   200
    check(pathTarget(cgr, cp) == n3, "Wrong pathTarget()");
kpeter@1044
   201
    check(checkPath(cgr, cp), "Wrong checkPath()");
kpeter@1044
   202
kpeter@1044
   203
    // Check clear()
kpeter@1044
   204
    p.clear();
kpeter@1044
   205
    check(cp.empty(), "The path is not empty");
kpeter@1044
   206
    check(cp.length() == 0, "The path is not empty");
kpeter@1044
   207
kpeter@1044
   208
    // Check inconsistent path
kpeter@1044
   209
    p.addBack(a4);
kpeter@1044
   210
    p.addBack(a2);
kpeter@1044
   211
    p.addBack(a1);
kpeter@1044
   212
    check(!cp.empty(), "Wrong empty()");
kpeter@1044
   213
    check(cp.length() == 3, "Wrong length");
kpeter@1044
   214
    check(cp.front() == a4, "Wrong front()");
kpeter@1044
   215
    check(cp.back() == a1, "Wrong back()");
kpeter@1044
   216
    check(pathSource(cgr, cp) == n4, "Wrong pathSource()");
kpeter@1044
   217
    check(pathTarget(cgr, cp) == n2, "Wrong pathTarget()");
kpeter@1044
   218
    check(!checkPath(cgr, cp), "Wrong checkPath()");
kpeter@1044
   219
  }
kpeter@1044
   220
kpeter@1044
   221
  template <typename P>
kpeter@1044
   222
  void checkBackAndFrontInsertablePath() {
kpeter@1044
   223
kpeter@1044
   224
    // Include back insertable test cases
kpeter@1044
   225
    checkBackInsertablePath<P>();
kpeter@1044
   226
kpeter@1044
   227
    // Check front and back insertion
kpeter@1044
   228
    P p;
kpeter@1044
   229
    const P& cp = p;
kpeter@1044
   230
    p.addFront(a4);
kpeter@1044
   231
    p.addBack(a1);
kpeter@1044
   232
    p.addFront(a3);
kpeter@1044
   233
    check(!cp.empty(), "Wrong empty()");
kpeter@1044
   234
    check(cp.length() == 3, "Wrong length");
kpeter@1044
   235
    check(cp.front() == a3, "Wrong front()");
kpeter@1044
   236
    check(cp.back() == a1, "Wrong back()");
kpeter@1044
   237
    check(cp.nth(0) == a3, "Wrong nth()");
kpeter@1044
   238
    check(cp.nth(1) == a4, "Wrong nth()");
kpeter@1044
   239
    check(cp.nth(2) == a1, "Wrong nth()");
kpeter@1044
   240
    typename P::ArcIt ai(cp);
kpeter@1044
   241
    check((tmp_a = ai) == a3, "Wrong ArcIt");
kpeter@1044
   242
    check((tmp_a = ++ai) == a4, "Wrong nthIt()");
kpeter@1044
   243
    check((tmp_a = ++ai) == a1, "Wrong nthIt()");
kpeter@1044
   244
    check(++ai == INVALID, "Wrong nthIt()");
kpeter@1044
   245
    ai = cp.nthIt(0);
kpeter@1044
   246
    check((tmp_a = ai) == a3, "Wrong nthIt()");
kpeter@1044
   247
    check((tmp_a = ++ai) == a4, "Wrong nthIt()");
kpeter@1044
   248
    ai = cp.nthIt(2);
kpeter@1044
   249
    check((tmp_a = ai) == a1, "Wrong nthIt()");
kpeter@1044
   250
    check(++ai == INVALID, "Wrong nthIt()");
kpeter@1044
   251
    check(pathSource(cgr, cp) == n3, "Wrong pathSource()");
kpeter@1044
   252
    check(pathTarget(cgr, cp) == n2, "Wrong pathTarget()");
kpeter@1044
   253
    check(checkPath(cgr, cp), "Wrong checkPath()");
kpeter@1044
   254
kpeter@1044
   255
    // Check eraseFront()
kpeter@1044
   256
    p.eraseFront();
kpeter@1044
   257
    p.addBack(a2);
kpeter@1044
   258
    check(!cp.empty(), "Wrong empty()");
kpeter@1044
   259
    check(cp.length() == 3, "Wrong length");
kpeter@1044
   260
    check(cp.front() == a4, "Wrong front()");
kpeter@1044
   261
    check(cp.back() == a2, "Wrong back()");
kpeter@1044
   262
    check(cp.nth(0) == a4, "Wrong nth()");
kpeter@1044
   263
    check(cp.nth(1) == a1, "Wrong nth()");
kpeter@1044
   264
    check(cp.nth(2) == a2, "Wrong nth()");
kpeter@1044
   265
    typename P::ArcIt ai2(cp);
kpeter@1044
   266
    check((tmp_a = ai2) == a4, "Wrong ArcIt");
kpeter@1044
   267
    check((tmp_a = ++ai2) == a1, "Wrong nthIt()");
kpeter@1044
   268
    check((tmp_a = ++ai2) == a2, "Wrong nthIt()");
kpeter@1044
   269
    check(++ai2 == INVALID, "Wrong nthIt()");
kpeter@1044
   270
    ai = cp.nthIt(0);
kpeter@1044
   271
    check((tmp_a = ai) == a4, "Wrong nthIt()");
kpeter@1044
   272
    check((tmp_a = ++ai) == a1, "Wrong nthIt()");
kpeter@1044
   273
    ai = cp.nthIt(2);
kpeter@1044
   274
    check((tmp_a = ai) == a2, "Wrong nthIt()");
kpeter@1044
   275
    check(++ai == INVALID, "Wrong nthIt()");
kpeter@1044
   276
    check(pathSource(cgr, cp) == n4, "Wrong pathSource()");
kpeter@1044
   277
    check(pathTarget(cgr, cp) == n3, "Wrong pathTarget()");
kpeter@1044
   278
    check(checkPath(cgr, cp), "Wrong checkPath()");
kpeter@1044
   279
  }
kpeter@1044
   280
kpeter@1202
   281
  template <typename P>
kpeter@1202
   282
  void checkSubscriptOperator() {
kpeter@1202
   283
    SimplePath<GR> p0;
kpeter@1202
   284
    p0.addBack(a1);
kpeter@1202
   285
    p0.addBack(a3);
kpeter@1202
   286
    p0.addBack(a2);
kpeter@1202
   287
    P p = p0;
kpeter@1202
   288
    check(!p.empty(), "Wrong empty()");
kpeter@1202
   289
    check(p.length() == 3, "Wrong length");
kpeter@1202
   290
    check(p.front() == a1, "Wrong front()");
kpeter@1202
   291
    check(p.back() == a2, "Wrong back()");
kpeter@1202
   292
    check(p.nth(0) == a1, "Wrong nth()");
kpeter@1202
   293
    check(p.nth(1) == a3, "Wrong nth()");
kpeter@1202
   294
    check(p.nth(2) == a2, "Wrong nth()");
kpeter@1202
   295
    check(p[0] == a1, "Wrong operator[]");
kpeter@1202
   296
    check(p[1] == a3, "Wrong operator[]");
kpeter@1202
   297
    check(p[2] == a2, "Wrong operator[]");
kpeter@1202
   298
  }
kpeter@1202
   299
kpeter@1044
   300
  void checkListPathSplitAndSplice() {
kpeter@1044
   301
kpeter@1044
   302
    // Build a path with spliceFront() and spliceBack()
kpeter@1044
   303
    ListPath<GR> p1, p2, p3, p4;
kpeter@1044
   304
    p1.addBack(a3);
kpeter@1044
   305
    p1.addFront(a2);
kpeter@1044
   306
    p2.addBack(a1);
kpeter@1044
   307
    p1.spliceFront(p2);
kpeter@1044
   308
    p3.addFront(a4);
kpeter@1044
   309
    p1.spliceBack(p3);
kpeter@1044
   310
    check(p1.length() == 4, "Wrong length");
kpeter@1044
   311
    check(p1.front() == a1, "Wrong front()");
kpeter@1044
   312
    check(p1.back() == a4, "Wrong back()");
kpeter@1044
   313
    ListPath<GR>::ArcIt ai(p1);
kpeter@1044
   314
    check((tmp_a = ai) == a1, "Wrong ArcIt");
kpeter@1044
   315
    check((tmp_a = ++ai) == a2, "Wrong nthIt()");
kpeter@1044
   316
    check((tmp_a = ++ai) == a3, "Wrong nthIt()");
kpeter@1044
   317
    check((tmp_a = ++ai) == a4, "Wrong nthIt()");
kpeter@1044
   318
    check(++ai == INVALID, "Wrong nthIt()");
kpeter@1044
   319
    check(checkPath(cgr, p1), "Wrong checkPath()");
kpeter@1044
   320
kpeter@1044
   321
    // Check split()
kpeter@1044
   322
    p1.split(p1.nthIt(2), p2);
kpeter@1044
   323
    check(p1.length() == 2, "Wrong length");
kpeter@1044
   324
    ai = p1.nthIt(0);
kpeter@1044
   325
    check((tmp_a = ai) == a1, "Wrong ArcIt");
kpeter@1044
   326
    check((tmp_a = ++ai) == a2, "Wrong nthIt()");
kpeter@1044
   327
    check(++ai == INVALID, "Wrong nthIt()");
kpeter@1044
   328
    check(checkPath(cgr, p1), "Wrong checkPath()");
kpeter@1044
   329
    check(p2.length() == 2, "Wrong length");
kpeter@1044
   330
    ai = p2.nthIt(0);
kpeter@1044
   331
    check((tmp_a = ai) == a3, "Wrong ArcIt");
kpeter@1044
   332
    check((tmp_a = ++ai) == a4, "Wrong nthIt()");
kpeter@1044
   333
    check(++ai == INVALID, "Wrong nthIt()");
kpeter@1044
   334
    check(checkPath(cgr, p2), "Wrong checkPath()");
kpeter@1044
   335
kpeter@1044
   336
    // Check split() and splice()
kpeter@1044
   337
    p1.spliceFront(p2);
kpeter@1044
   338
    p1.split(p1.nthIt(2), p2);
kpeter@1044
   339
    p2.split(p2.nthIt(1), p3);
kpeter@1044
   340
    p2.spliceBack(p1);
kpeter@1044
   341
    p2.splice(p2.nthIt(1), p3);
kpeter@1044
   342
    check(p2.length() == 4, "Wrong length");
kpeter@1044
   343
    check(p2.front() == a1, "Wrong front()");
kpeter@1044
   344
    check(p2.back() == a4, "Wrong back()");
kpeter@1044
   345
    ai = p2.nthIt(0);
kpeter@1044
   346
    check((tmp_a = ai) == a1, "Wrong ArcIt");
kpeter@1044
   347
    check((tmp_a = ++ai) == a2, "Wrong nthIt()");
kpeter@1044
   348
    check((tmp_a = ++ai) == a3, "Wrong nthIt()");
kpeter@1044
   349
    check((tmp_a = ++ai) == a4, "Wrong nthIt()");
kpeter@1044
   350
    check(++ai == INVALID, "Wrong nthIt()");
kpeter@1044
   351
    check(checkPath(cgr, p2), "Wrong checkPath()");
kpeter@1044
   352
  }
kpeter@1044
   353
kpeter@1044
   354
};
kpeter@1044
   355
alpar@96
   356
int main() {
kpeter@1044
   357
  checkPathConcepts();
kpeter@1044
   358
  checkPathCopy();
kpeter@1044
   359
  CheckPathFunctions cpf;
kpeter@1044
   360
  cpf.run();
alpar@990
   361
alpar@96
   362
  return 0;
alpar@96
   363
}