lemon-project-template-glpk

diff deps/glpk/src/glpssx02.c @ 9:33de93886c88

Import GLPK 4.47
author Alpar Juttner <alpar@cs.elte.hu>
date Sun, 06 Nov 2011 20:59:10 +0100
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/deps/glpk/src/glpssx02.c	Sun Nov 06 20:59:10 2011 +0100
     1.3 @@ -0,0 +1,479 @@
     1.4 +/* glpssx02.c */
     1.5 +
     1.6 +/***********************************************************************
     1.7 +*  This code is part of GLPK (GNU Linear Programming Kit).
     1.8 +*
     1.9 +*  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
    1.10 +*  2009, 2010, 2011 Andrew Makhorin, Department for Applied Informatics,
    1.11 +*  Moscow Aviation Institute, Moscow, Russia. All rights reserved.
    1.12 +*  E-mail: <mao@gnu.org>.
    1.13 +*
    1.14 +*  GLPK is free software: you can redistribute it and/or modify it
    1.15 +*  under the terms of the GNU General Public License as published by
    1.16 +*  the Free Software Foundation, either version 3 of the License, or
    1.17 +*  (at your option) any later version.
    1.18 +*
    1.19 +*  GLPK is distributed in the hope that it will be useful, but WITHOUT
    1.20 +*  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    1.21 +*  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
    1.22 +*  License for more details.
    1.23 +*
    1.24 +*  You should have received a copy of the GNU General Public License
    1.25 +*  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
    1.26 +***********************************************************************/
    1.27 +
    1.28 +#include "glpenv.h"
    1.29 +#include "glpssx.h"
    1.30 +
    1.31 +static void show_progress(SSX *ssx, int phase)
    1.32 +{     /* this auxiliary routine displays information about progress of
    1.33 +         the search */
    1.34 +      int i, def = 0;
    1.35 +      for (i = 1; i <= ssx->m; i++)
    1.36 +         if (ssx->type[ssx->Q_col[i]] == SSX_FX) def++;
    1.37 +      xprintf("%s%6d:   %s = %22.15g   (%d)\n", phase == 1 ? " " : "*",
    1.38 +         ssx->it_cnt, phase == 1 ? "infsum" : "objval",
    1.39 +         mpq_get_d(ssx->bbar[0]), def);
    1.40 +#if 0
    1.41 +      ssx->tm_lag = utime();
    1.42 +#else
    1.43 +      ssx->tm_lag = xtime();
    1.44 +#endif
    1.45 +      return;
    1.46 +}
    1.47 +
    1.48 +/*----------------------------------------------------------------------
    1.49 +// ssx_phase_I - find primal feasible solution.
    1.50 +//
    1.51 +// This routine implements phase I of the primal simplex method.
    1.52 +//
    1.53 +// On exit the routine returns one of the following codes:
    1.54 +//
    1.55 +// 0 - feasible solution found;
    1.56 +// 1 - problem has no feasible solution;
    1.57 +// 2 - iterations limit exceeded;
    1.58 +// 3 - time limit exceeded.
    1.59 +----------------------------------------------------------------------*/
    1.60 +
    1.61 +int ssx_phase_I(SSX *ssx)
    1.62 +{     int m = ssx->m;
    1.63 +      int n = ssx->n;
    1.64 +      int *type = ssx->type;
    1.65 +      mpq_t *lb = ssx->lb;
    1.66 +      mpq_t *ub = ssx->ub;
    1.67 +      mpq_t *coef = ssx->coef;
    1.68 +      int *A_ptr = ssx->A_ptr;
    1.69 +      int *A_ind = ssx->A_ind;
    1.70 +      mpq_t *A_val = ssx->A_val;
    1.71 +      int *Q_col = ssx->Q_col;
    1.72 +      mpq_t *bbar = ssx->bbar;
    1.73 +      mpq_t *pi = ssx->pi;
    1.74 +      mpq_t *cbar = ssx->cbar;
    1.75 +      int *orig_type, orig_dir;
    1.76 +      mpq_t *orig_lb, *orig_ub, *orig_coef;
    1.77 +      int i, k, ret;
    1.78 +      /* save components of the original LP problem, which are changed
    1.79 +         by the routine */
    1.80 +      orig_type = xcalloc(1+m+n, sizeof(int));
    1.81 +      orig_lb = xcalloc(1+m+n, sizeof(mpq_t));
    1.82 +      orig_ub = xcalloc(1+m+n, sizeof(mpq_t));
    1.83 +      orig_coef = xcalloc(1+m+n, sizeof(mpq_t));
    1.84 +      for (k = 1; k <= m+n; k++)
    1.85 +      {  orig_type[k] = type[k];
    1.86 +         mpq_init(orig_lb[k]);
    1.87 +         mpq_set(orig_lb[k], lb[k]);
    1.88 +         mpq_init(orig_ub[k]);
    1.89 +         mpq_set(orig_ub[k], ub[k]);
    1.90 +      }
    1.91 +      orig_dir = ssx->dir;
    1.92 +      for (k = 0; k <= m+n; k++)
    1.93 +      {  mpq_init(orig_coef[k]);
    1.94 +         mpq_set(orig_coef[k], coef[k]);
    1.95 +      }
    1.96 +      /* build an artificial basic solution, which is primal feasible,
    1.97 +         and also build an auxiliary objective function to minimize the
    1.98 +         sum of infeasibilities for the original problem */
    1.99 +      ssx->dir = SSX_MIN;
   1.100 +      for (k = 0; k <= m+n; k++) mpq_set_si(coef[k], 0, 1);
   1.101 +      mpq_set_si(bbar[0], 0, 1);
   1.102 +      for (i = 1; i <= m; i++)
   1.103 +      {  int t;
   1.104 +         k = Q_col[i]; /* x[k] = xB[i] */
   1.105 +         t = type[k];
   1.106 +         if (t == SSX_LO || t == SSX_DB || t == SSX_FX)
   1.107 +         {  /* in the original problem x[k] has lower bound */
   1.108 +            if (mpq_cmp(bbar[i], lb[k]) < 0)
   1.109 +            {  /* which is violated */
   1.110 +               type[k] = SSX_UP;
   1.111 +               mpq_set(ub[k], lb[k]);
   1.112 +               mpq_set_si(lb[k], 0, 1);
   1.113 +               mpq_set_si(coef[k], -1, 1);
   1.114 +               mpq_add(bbar[0], bbar[0], ub[k]);
   1.115 +               mpq_sub(bbar[0], bbar[0], bbar[i]);
   1.116 +            }
   1.117 +         }
   1.118 +         if (t == SSX_UP || t == SSX_DB || t == SSX_FX)
   1.119 +         {  /* in the original problem x[k] has upper bound */
   1.120 +            if (mpq_cmp(bbar[i], ub[k]) > 0)
   1.121 +            {  /* which is violated */
   1.122 +               type[k] = SSX_LO;
   1.123 +               mpq_set(lb[k], ub[k]);
   1.124 +               mpq_set_si(ub[k], 0, 1);
   1.125 +               mpq_set_si(coef[k], +1, 1);
   1.126 +               mpq_add(bbar[0], bbar[0], bbar[i]);
   1.127 +               mpq_sub(bbar[0], bbar[0], lb[k]);
   1.128 +            }
   1.129 +         }
   1.130 +      }
   1.131 +      /* now the initial basic solution should be primal feasible due
   1.132 +         to changes of bounds of some basic variables, which turned to
   1.133 +         implicit artifical variables */
   1.134 +      /* compute simplex multipliers and reduced costs */
   1.135 +      ssx_eval_pi(ssx);
   1.136 +      ssx_eval_cbar(ssx);
   1.137 +      /* display initial progress of the search */
   1.138 +      show_progress(ssx, 1);
   1.139 +      /* main loop starts here */
   1.140 +      for (;;)
   1.141 +      {  /* display current progress of the search */
   1.142 +#if 0
   1.143 +         if (utime() - ssx->tm_lag >= ssx->out_frq - 0.001)
   1.144 +#else
   1.145 +         if (xdifftime(xtime(), ssx->tm_lag) >= ssx->out_frq - 0.001)
   1.146 +#endif
   1.147 +            show_progress(ssx, 1);
   1.148 +         /* we do not need to wait until all artificial variables have
   1.149 +            left the basis */
   1.150 +         if (mpq_sgn(bbar[0]) == 0)
   1.151 +         {  /* the sum of infeasibilities is zero, therefore the current
   1.152 +               solution is primal feasible for the original problem */
   1.153 +            ret = 0;
   1.154 +            break;
   1.155 +         }
   1.156 +         /* check if the iterations limit has been exhausted */
   1.157 +         if (ssx->it_lim == 0)
   1.158 +         {  ret = 2;
   1.159 +            break;
   1.160 +         }
   1.161 +         /* check if the time limit has been exhausted */
   1.162 +#if 0
   1.163 +         if (ssx->tm_lim >= 0.0 && ssx->tm_lim <= utime() - ssx->tm_beg)
   1.164 +#else
   1.165 +         if (ssx->tm_lim >= 0.0 &&
   1.166 +             ssx->tm_lim <= xdifftime(xtime(), ssx->tm_beg))
   1.167 +#endif
   1.168 +         {  ret = 3;
   1.169 +            break;
   1.170 +         }
   1.171 +         /* choose non-basic variable xN[q] */
   1.172 +         ssx_chuzc(ssx);
   1.173 +         /* if xN[q] cannot be chosen, the sum of infeasibilities is
   1.174 +            minimal but non-zero; therefore the original problem has no
   1.175 +            primal feasible solution */
   1.176 +         if (ssx->q == 0)
   1.177 +         {  ret = 1;
   1.178 +            break;
   1.179 +         }
   1.180 +         /* compute q-th column of the simplex table */
   1.181 +         ssx_eval_col(ssx);
   1.182 +         /* choose basic variable xB[p] */
   1.183 +         ssx_chuzr(ssx);
   1.184 +         /* the sum of infeasibilities cannot be negative, therefore
   1.185 +            the auxiliary lp problem cannot have unbounded solution */
   1.186 +         xassert(ssx->p != 0);
   1.187 +         /* update values of basic variables */
   1.188 +         ssx_update_bbar(ssx);
   1.189 +         if (ssx->p > 0)
   1.190 +         {  /* compute p-th row of the inverse inv(B) */
   1.191 +            ssx_eval_rho(ssx);
   1.192 +            /* compute p-th row of the simplex table */
   1.193 +            ssx_eval_row(ssx);
   1.194 +            xassert(mpq_cmp(ssx->aq[ssx->p], ssx->ap[ssx->q]) == 0);
   1.195 +            /* update simplex multipliers */
   1.196 +            ssx_update_pi(ssx);
   1.197 +            /* update reduced costs of non-basic variables */
   1.198 +            ssx_update_cbar(ssx);
   1.199 +         }
   1.200 +         /* xB[p] is leaving the basis; if it is implicit artificial
   1.201 +            variable, the corresponding residual vanishes; therefore
   1.202 +            bounds of this variable should be restored to the original
   1.203 +            values */
   1.204 +         if (ssx->p > 0)
   1.205 +         {  k = Q_col[ssx->p]; /* x[k] = xB[p] */
   1.206 +            if (type[k] != orig_type[k])
   1.207 +            {  /* x[k] is implicit artificial variable */
   1.208 +               type[k] = orig_type[k];
   1.209 +               mpq_set(lb[k], orig_lb[k]);
   1.210 +               mpq_set(ub[k], orig_ub[k]);
   1.211 +               xassert(ssx->p_stat == SSX_NL || ssx->p_stat == SSX_NU);
   1.212 +               ssx->p_stat = (ssx->p_stat == SSX_NL ? SSX_NU : SSX_NL);
   1.213 +               if (type[k] == SSX_FX) ssx->p_stat = SSX_NS;
   1.214 +               /* nullify the objective coefficient at x[k] */
   1.215 +               mpq_set_si(coef[k], 0, 1);
   1.216 +               /* since coef[k] has been changed, we need to compute
   1.217 +                  new reduced cost of x[k], which it will have in the
   1.218 +                  adjacent basis */
   1.219 +               /* the formula d[j] = cN[j] - pi' * N[j] is used (note
   1.220 +                  that the vector pi is not changed, because it depends
   1.221 +                  on objective coefficients at basic variables, but in
   1.222 +                  the adjacent basis, for which the vector pi has been
   1.223 +                  just recomputed, x[k] is non-basic) */
   1.224 +               if (k <= m)
   1.225 +               {  /* x[k] is auxiliary variable */
   1.226 +                  mpq_neg(cbar[ssx->q], pi[k]);
   1.227 +               }
   1.228 +               else
   1.229 +               {  /* x[k] is structural variable */
   1.230 +                  int ptr;
   1.231 +                  mpq_t temp;
   1.232 +                  mpq_init(temp);
   1.233 +                  mpq_set_si(cbar[ssx->q], 0, 1);
   1.234 +                  for (ptr = A_ptr[k-m]; ptr < A_ptr[k-m+1]; ptr++)
   1.235 +                  {  mpq_mul(temp, pi[A_ind[ptr]], A_val[ptr]);
   1.236 +                     mpq_add(cbar[ssx->q], cbar[ssx->q], temp);
   1.237 +                  }
   1.238 +                  mpq_clear(temp);
   1.239 +               }
   1.240 +            }
   1.241 +         }
   1.242 +         /* jump to the adjacent vertex of the polyhedron */
   1.243 +         ssx_change_basis(ssx);
   1.244 +         /* one simplex iteration has been performed */
   1.245 +         if (ssx->it_lim > 0) ssx->it_lim--;
   1.246 +         ssx->it_cnt++;
   1.247 +      }
   1.248 +      /* display final progress of the search */
   1.249 +      show_progress(ssx, 1);
   1.250 +      /* restore components of the original problem, which were changed
   1.251 +         by the routine */
   1.252 +      for (k = 1; k <= m+n; k++)
   1.253 +      {  type[k] = orig_type[k];
   1.254 +         mpq_set(lb[k], orig_lb[k]);
   1.255 +         mpq_clear(orig_lb[k]);
   1.256 +         mpq_set(ub[k], orig_ub[k]);
   1.257 +         mpq_clear(orig_ub[k]);
   1.258 +      }
   1.259 +      ssx->dir = orig_dir;
   1.260 +      for (k = 0; k <= m+n; k++)
   1.261 +      {  mpq_set(coef[k], orig_coef[k]);
   1.262 +         mpq_clear(orig_coef[k]);
   1.263 +      }
   1.264 +      xfree(orig_type);
   1.265 +      xfree(orig_lb);
   1.266 +      xfree(orig_ub);
   1.267 +      xfree(orig_coef);
   1.268 +      /* return to the calling program */
   1.269 +      return ret;
   1.270 +}
   1.271 +
   1.272 +/*----------------------------------------------------------------------
   1.273 +// ssx_phase_II - find optimal solution.
   1.274 +//
   1.275 +// This routine implements phase II of the primal simplex method.
   1.276 +//
   1.277 +// On exit the routine returns one of the following codes:
   1.278 +//
   1.279 +// 0 - optimal solution found;
   1.280 +// 1 - problem has unbounded solution;
   1.281 +// 2 - iterations limit exceeded;
   1.282 +// 3 - time limit exceeded.
   1.283 +----------------------------------------------------------------------*/
   1.284 +
   1.285 +int ssx_phase_II(SSX *ssx)
   1.286 +{     int ret;
   1.287 +      /* display initial progress of the search */
   1.288 +      show_progress(ssx, 2);
   1.289 +      /* main loop starts here */
   1.290 +      for (;;)
   1.291 +      {  /* display current progress of the search */
   1.292 +#if 0
   1.293 +         if (utime() - ssx->tm_lag >= ssx->out_frq - 0.001)
   1.294 +#else
   1.295 +         if (xdifftime(xtime(), ssx->tm_lag) >= ssx->out_frq - 0.001)
   1.296 +#endif
   1.297 +            show_progress(ssx, 2);
   1.298 +         /* check if the iterations limit has been exhausted */
   1.299 +         if (ssx->it_lim == 0)
   1.300 +         {  ret = 2;
   1.301 +            break;
   1.302 +         }
   1.303 +         /* check if the time limit has been exhausted */
   1.304 +#if 0
   1.305 +         if (ssx->tm_lim >= 0.0 && ssx->tm_lim <= utime() - ssx->tm_beg)
   1.306 +#else
   1.307 +         if (ssx->tm_lim >= 0.0 &&
   1.308 +             ssx->tm_lim <= xdifftime(xtime(), ssx->tm_beg))
   1.309 +#endif
   1.310 +         {  ret = 3;
   1.311 +            break;
   1.312 +         }
   1.313 +         /* choose non-basic variable xN[q] */
   1.314 +         ssx_chuzc(ssx);
   1.315 +         /* if xN[q] cannot be chosen, the current basic solution is
   1.316 +            dual feasible and therefore optimal */
   1.317 +         if (ssx->q == 0)
   1.318 +         {  ret = 0;
   1.319 +            break;
   1.320 +         }
   1.321 +         /* compute q-th column of the simplex table */
   1.322 +         ssx_eval_col(ssx);
   1.323 +         /* choose basic variable xB[p] */
   1.324 +         ssx_chuzr(ssx);
   1.325 +         /* if xB[p] cannot be chosen, the problem has no dual feasible
   1.326 +            solution (i.e. unbounded) */
   1.327 +         if (ssx->p == 0)
   1.328 +         {  ret = 1;
   1.329 +            break;
   1.330 +         }
   1.331 +         /* update values of basic variables */
   1.332 +         ssx_update_bbar(ssx);
   1.333 +         if (ssx->p > 0)
   1.334 +         {  /* compute p-th row of the inverse inv(B) */
   1.335 +            ssx_eval_rho(ssx);
   1.336 +            /* compute p-th row of the simplex table */
   1.337 +            ssx_eval_row(ssx);
   1.338 +            xassert(mpq_cmp(ssx->aq[ssx->p], ssx->ap[ssx->q]) == 0);
   1.339 +#if 0
   1.340 +            /* update simplex multipliers */
   1.341 +            ssx_update_pi(ssx);
   1.342 +#endif
   1.343 +            /* update reduced costs of non-basic variables */
   1.344 +            ssx_update_cbar(ssx);
   1.345 +         }
   1.346 +         /* jump to the adjacent vertex of the polyhedron */
   1.347 +         ssx_change_basis(ssx);
   1.348 +         /* one simplex iteration has been performed */
   1.349 +         if (ssx->it_lim > 0) ssx->it_lim--;
   1.350 +         ssx->it_cnt++;
   1.351 +      }
   1.352 +      /* display final progress of the search */
   1.353 +      show_progress(ssx, 2);
   1.354 +      /* return to the calling program */
   1.355 +      return ret;
   1.356 +}
   1.357 +
   1.358 +/*----------------------------------------------------------------------
   1.359 +// ssx_driver - base driver to exact simplex method.
   1.360 +//
   1.361 +// This routine is a base driver to a version of the primal simplex
   1.362 +// method using exact (bignum) arithmetic.
   1.363 +//
   1.364 +// On exit the routine returns one of the following codes:
   1.365 +//
   1.366 +// 0 - optimal solution found;
   1.367 +// 1 - problem has no feasible solution;
   1.368 +// 2 - problem has unbounded solution;
   1.369 +// 3 - iterations limit exceeded (phase I);
   1.370 +// 4 - iterations limit exceeded (phase II);
   1.371 +// 5 - time limit exceeded (phase I);
   1.372 +// 6 - time limit exceeded (phase II);
   1.373 +// 7 - initial basis matrix is exactly singular.
   1.374 +----------------------------------------------------------------------*/
   1.375 +
   1.376 +int ssx_driver(SSX *ssx)
   1.377 +{     int m = ssx->m;
   1.378 +      int *type = ssx->type;
   1.379 +      mpq_t *lb = ssx->lb;
   1.380 +      mpq_t *ub = ssx->ub;
   1.381 +      int *Q_col = ssx->Q_col;
   1.382 +      mpq_t *bbar = ssx->bbar;
   1.383 +      int i, k, ret;
   1.384 +      ssx->tm_beg = xtime();
   1.385 +      /* factorize the initial basis matrix */
   1.386 +      if (ssx_factorize(ssx))
   1.387 +      {  xprintf("Initial basis matrix is singular\n");
   1.388 +         ret = 7;
   1.389 +         goto done;
   1.390 +      }
   1.391 +      /* compute values of basic variables */
   1.392 +      ssx_eval_bbar(ssx);
   1.393 +      /* check if the initial basic solution is primal feasible */
   1.394 +      for (i = 1; i <= m; i++)
   1.395 +      {  int t;
   1.396 +         k = Q_col[i]; /* x[k] = xB[i] */
   1.397 +         t = type[k];
   1.398 +         if (t == SSX_LO || t == SSX_DB || t == SSX_FX)
   1.399 +         {  /* x[k] has lower bound */
   1.400 +            if (mpq_cmp(bbar[i], lb[k]) < 0)
   1.401 +            {  /* which is violated */
   1.402 +               break;
   1.403 +            }
   1.404 +         }
   1.405 +         if (t == SSX_UP || t == SSX_DB || t == SSX_FX)
   1.406 +         {  /* x[k] has upper bound */
   1.407 +            if (mpq_cmp(bbar[i], ub[k]) > 0)
   1.408 +            {  /* which is violated */
   1.409 +               break;
   1.410 +            }
   1.411 +         }
   1.412 +      }
   1.413 +      if (i > m)
   1.414 +      {  /* no basic variable violates its bounds */
   1.415 +         ret = 0;
   1.416 +         goto skip;
   1.417 +      }
   1.418 +      /* phase I: find primal feasible solution */
   1.419 +      ret = ssx_phase_I(ssx);
   1.420 +      switch (ret)
   1.421 +      {  case 0:
   1.422 +            ret = 0;
   1.423 +            break;
   1.424 +         case 1:
   1.425 +            xprintf("PROBLEM HAS NO FEASIBLE SOLUTION\n");
   1.426 +            ret = 1;
   1.427 +            break;
   1.428 +         case 2:
   1.429 +            xprintf("ITERATIONS LIMIT EXCEEDED; SEARCH TERMINATED\n");
   1.430 +            ret = 3;
   1.431 +            break;
   1.432 +         case 3:
   1.433 +            xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n");
   1.434 +            ret = 5;
   1.435 +            break;
   1.436 +         default:
   1.437 +            xassert(ret != ret);
   1.438 +      }
   1.439 +      /* compute values of basic variables (actually only the objective
   1.440 +         value needs to be computed) */
   1.441 +      ssx_eval_bbar(ssx);
   1.442 +skip: /* compute simplex multipliers */
   1.443 +      ssx_eval_pi(ssx);
   1.444 +      /* compute reduced costs of non-basic variables */
   1.445 +      ssx_eval_cbar(ssx);
   1.446 +      /* if phase I failed, do not start phase II */
   1.447 +      if (ret != 0) goto done;
   1.448 +      /* phase II: find optimal solution */
   1.449 +      ret = ssx_phase_II(ssx);
   1.450 +      switch (ret)
   1.451 +      {  case 0:
   1.452 +            xprintf("OPTIMAL SOLUTION FOUND\n");
   1.453 +            ret = 0;
   1.454 +            break;
   1.455 +         case 1:
   1.456 +            xprintf("PROBLEM HAS UNBOUNDED SOLUTION\n");
   1.457 +            ret = 2;
   1.458 +            break;
   1.459 +         case 2:
   1.460 +            xprintf("ITERATIONS LIMIT EXCEEDED; SEARCH TERMINATED\n");
   1.461 +            ret = 4;
   1.462 +            break;
   1.463 +         case 3:
   1.464 +            xprintf("TIME LIMIT EXCEEDED; SEARCH TERMINATED\n");
   1.465 +            ret = 6;
   1.466 +            break;
   1.467 +         default:
   1.468 +            xassert(ret != ret);
   1.469 +      }
   1.470 +done: /* decrease the time limit by the spent amount of time */
   1.471 +      if (ssx->tm_lim >= 0.0)
   1.472 +#if 0
   1.473 +      {  ssx->tm_lim -= utime() - ssx->tm_beg;
   1.474 +#else
   1.475 +      {  ssx->tm_lim -= xdifftime(xtime(), ssx->tm_beg);
   1.476 +#endif
   1.477 +         if (ssx->tm_lim < 0.0) ssx->tm_lim = 0.0;
   1.478 +      }
   1.479 +      return ret;
   1.480 +}
   1.481 +
   1.482 +/* eof */