mpf_mps.c

Go to the documentation of this file.
00001 /****************************************************************************/
00002 /*                                                                          */
00003 /*  This file is part of QSopt_ex.                                          */
00004 /*                                                                          */
00005 /*  (c) Copyright 2006 by David Applegate, William Cook, Sanjeeb Dash,      */
00006 /*  and Daniel Espinoza                                                     */
00007 /*                                                                          */
00008 /*  Sanjeeb Dash ownership of copyright in QSopt_ex is derived from his     */
00009 /*  copyright in QSopt.                                                     */
00010 /*                                                                          */
00011 /*  This code may be used under the terms of the GNU General Public License */
00012 /*  (Version 2.1 or later) as published by the Free Software Foundation.    */
00013 /*                                                                          */
00014 /*  Alternatively, use is granted for research purposes only.               */
00015 /*                                                                          */
00016 /*  It is your choice of which of these two licenses you are operating      */
00017 /*  under.                                                                  */
00018 /*                                                                          */
00019 /*  We make no guarantees about the correctness or usefulness of this code. */
00020 /*                                                                          */
00021 /****************************************************************************/
00022 
00023 /* RCS_INFO = "$RCSfile: mpf_mps.c,v $ $Revision: 1.2 $ $Date: 2003/11/05 16:49:52 $"; */
00024 
00025 /****************************************************************************/
00026 /*                                                                          */
00027 /*               Routines for Reading and Writing MPS Files                 */
00028 /*                                                                          */
00029 /*  EXPORTED FUNCTIONS                                                      */
00030 /*                                                                          */
00031 /*    int  ILLlpdata_mpsread (mpf_ILLlpdata *lp, const char *filename);         */
00032 /*    int  ILLlpdata_mpswrite(mpf_ILLlpdata *lp, const char *filename);         */
00033 /*                                                                          */
00034 /*  NOTES                                                                   */
00035 /*                                                                          */
00036 /*    In the MPS reader, integer variables without an explicit bound are    */
00037 /*    set to binary; real variables without an explict lower bound and      */
00038 /*    either a nonnegative or free upperbound set to nonnegative.  (These   */
00039 /*    are standard settings.)                                               */
00040 /*                                                                          */
00041 /*    If a RHS is not specified for a row, it is set to 0.                  */
00042 /*                                                                          */
00043 /*    The MPS reader allows CPLEX's OBJSENSE extension to specify max or    */
00044 /*    min and the OBJNAME extension to specify an objective row.            */
00045 /*                                                                          */
00046 /****************************************************************************/
00047 
00048 #include "qs_config.h"
00049 #include "mpf_iqsutil.h"
00050 #include "mpf_mps.h"
00051 #include "mpf_rawlp.h"
00052 #include "mpf_lpdata.h"
00053 //extern mpf_t mpf_SZERO_TOLER;
00054 
00055 static int TRACE = 0;
00056 
00057 const char *mpf_ILLmps_section_name[ILL_MPS_N_SECTIONS + 2] = {
00058   "NAME", "OBJSENSE", "OBJNAME", "ROWS", "COLUMNS",
00059   "RHS", "RANGES", "BOUNDS", "REFROW", "ENDATA",
00060   NULL
00061 };
00062 
00063 static const char *mpf_mps_bound_name[] = {
00064   "LO", "UP", "FX", "FR", "MI", "PL", "BV", "UI", "LI", NULL
00065 };
00066 
00067 /****************************************************************************/
00068 /* reading 
00069  */
00070 static int mpf_read_mps_section (
00071   mpf_ILLread_mps_state * state,
00072   mpf_rawlpdata * lp);
00073 
00074 static int mpf_read_mps_name (
00075   mpf_ILLread_mps_state * state,
00076   mpf_rawlpdata * lp);
00077 static int mpf_read_mps_refrow (
00078   mpf_ILLread_mps_state * state,
00079   mpf_rawlpdata * lp);
00080 static int mpf_read_mps_objnamesense (
00081   ILLmps_section sec,
00082   mpf_ILLread_mps_state * state,
00083   mpf_rawlpdata * lp);
00084 static int mpf_read_mps_objname (
00085   mpf_ILLread_mps_state * state);
00086 static int mpf_read_mps_objsense (
00087   mpf_ILLread_mps_state * state,
00088   mpf_rawlpdata * lp);
00089 
00090 static int mpf_read_mps_line_in_section (
00091   mpf_ILLread_mps_state * state,
00092   mpf_rawlpdata * lp);
00093 
00094 
00095 static int mpf_add_row (
00096   mpf_ILLread_mps_state * state,
00097   mpf_rawlpdata * lp);
00098 static int mpf_add_col (
00099   mpf_ILLread_mps_state * state,
00100   mpf_rawlpdata * lp);
00101 static int mpf_add_rhs (
00102   mpf_ILLread_mps_state * state,
00103   mpf_rawlpdata * lp);
00104 static int mpf_add_ranges (
00105   mpf_ILLread_mps_state * state,
00106   mpf_rawlpdata * lp);
00107 static int mpf_add_bounds (
00108   mpf_ILLread_mps_state * state,
00109   mpf_rawlpdata * lp);
00110 
00111 static int mpf_mps_read_marker_line (
00112   mpf_ILLread_mps_state * state,
00113   mpf_rawlpdata * lp);
00114 static int mpf_is_marker_line (
00115   mpf_ILLread_mps_state * state);
00116 static int mpf_mps_read_col_line (
00117   mpf_ILLread_mps_state * state,
00118   mpf_rawlpdata * lp);
00119 
00120 static int mpf_mps_fill_in (
00121   mpf_rawlpdata * lp,
00122   const char *obj);
00123 
00124 
00125 static void mpf_mps_set_bound (
00126   mpf_rawlpdata * lp,
00127   mpf_ILLread_mps_state * state,
00128   int colind,
00129   const char *bndtype,
00130   mpf_t bnd);
00131 
00132 int mpf_ILLread_mps (
00133   mpf_qsline_reader * file,
00134   const char *f,
00135   mpf_rawlpdata * lp)
00136 {
00137   int rval = 0;
00138   int end = 0;
00139   mpf_ILLread_mps_state state;
00140 
00141   ILL_IFTRACE ("\tread_mps\n");
00142   if (ILLsymboltab_create (&lp->rowtab, 100) ||
00143       ILLsymboltab_create (&lp->coltab, 100))
00144   {
00145     rval = 1;
00146   }
00147   else
00148   {
00149     rval = mpf_ILLmps_state_init (&state, file, f);
00150   }
00151   if (rval != 0)
00152   {
00153     goto CLEANUP;
00154   }
00155 
00156   while (mpf_ILLmps_next_line (&state) == 0)
00157   {
00158     if (mpf_ILLmps_empty_key (&state))
00159     {
00160       if (mpf_read_mps_line_in_section (&state, lp) != 0)
00161       {
00162         rval++;
00163       }
00164     }
00165     else
00166     {
00167       /* found a section indicator in col 1 */
00168       if (!strcmp (state.key, mpf_ILLmps_section_name[ILL_MPS_ENDATA]))
00169       {
00170         end = 1;
00171         break;                  /* done reading */
00172       }
00173       if (mpf_read_mps_section (&state, lp) != 0)
00174       {
00175         rval++;
00176       }
00177     }
00178     if (rval == 50)
00179     {
00180       (void) mpf_ILLmps_error (&state, "Too many errors.\n");
00181     }
00182   }
00183 
00184   if (!end)
00185   {
00186     mpf_ILLmps_warn (&state, "Missing ENDATA.");
00187   }
00188   if (!mpf_ILLmps_next_line (&state))
00189   {
00190     mpf_ILLmps_warn (&state, "Ignoring text after ENDATA.");
00191   }
00192   if (rval == 0)
00193   {
00194     rval = mpf_mps_fill_in (lp, state.obj);
00195   }
00196 
00197 CLEANUP:
00198   ILL_RESULT (rval, "read_mps");
00199 }
00200 
00201 static int mpf_check_section_order (
00202   mpf_ILLread_mps_state * state,
00203   int sec)
00204 {
00205   switch (sec)
00206   {
00207   case ILL_MPS_REFROW:
00208     if (state->section[ILL_MPS_ROWS] == 1)
00209     {
00210       return mpf_ILLmps_error (state, "%s section after ROWS section.\n",
00211                            mpf_ILLmps_section_name[sec]);
00212     }
00213     break;
00214 
00215   case ILL_MPS_COLS:
00216   case ILL_MPS_RHS:
00217   case ILL_MPS_RANGES:
00218     if (state->section[ILL_MPS_ROWS] == 0)
00219     {
00220       return mpf_ILLmps_error (state, "%s section before ROWS section.\n",
00221                            mpf_ILLmps_section_name[sec]);
00222     };
00223     break;
00224 
00225   case ILL_MPS_BOUNDS:
00226     if (state->section[ILL_MPS_COLS] == 0)
00227     {
00228       return mpf_ILLmps_error (state, "%s section before COLUMNS section.\n",
00229                            mpf_ILLmps_section_name[sec]);
00230     }
00231     break;
00232   }
00233   return 0;
00234 }
00235 
00236 static int mpf_read_mps_section (
00237   mpf_ILLread_mps_state * state,
00238   mpf_rawlpdata * lp)
00239 {
00240   int sec;
00241   int rval = 0, r;
00242 
00243   ILL_FAILtrue (mpf_ILLmps_empty_key (state), "must have a key on this line");
00244 
00245   sec = mpf_ILLutil_index (mpf_ILLmps_section_name, state->key);
00246   if (sec < 0)
00247   {
00248     return mpf_ILLmps_error (state, "\"%s\" is not a key.\n", state->key);
00249   }
00250   rval = mpf_ILLmps_set_section (state, sec);
00251 
00252   state->active = ILL_MPS_NONE;
00253   rval = rval || mpf_check_section_order (state, sec);
00254   switch (sec)
00255   {
00256   case ILL_MPS_COLS:
00257   case ILL_MPS_ROWS:
00258     state->active = sec;
00259     break;
00260 
00261   case ILL_MPS_NAME:
00262     if (rval == 0)
00263     {
00264       rval = mpf_read_mps_name (state, lp);
00265     }
00266     break;
00267 
00268   case ILL_MPS_RHS:
00269     if (rval == 0)
00270     {
00271       rval = mpf_ILLraw_init_rhs (lp);
00272     }
00273     state->active = ILL_MPS_RHS;
00274     break;
00275 
00276   case ILL_MPS_RANGES:
00277     if (rval == 0)
00278     {
00279       rval = mpf_ILLraw_init_ranges (lp);
00280     }
00281     state->active = ILL_MPS_RANGES;
00282     break;
00283 
00284   case ILL_MPS_BOUNDS:
00285     if (rval == 0)
00286     {
00287       rval = mpf_ILLraw_init_bounds (lp);
00288     }
00289     state->active = ILL_MPS_BOUNDS;
00290     break;
00291 
00292   case ILL_MPS_OBJNAME:
00293   case ILL_MPS_OBJSENSE:
00294     r = mpf_read_mps_objnamesense (sec, state, lp);
00295     rval = r || rval;
00296     break;
00297 
00298   case ILL_MPS_REFROW:
00299     r = mpf_read_mps_refrow (state, lp);
00300     rval = r || rval;
00301     break;
00302 
00303   default:
00304     ILL_REPRT ("should never get here");
00305     goto CLEANUP;
00306   }
00307 CLEANUP:
00308   ILL_RESULT (rval, "mpf_read_mps_section");
00309 }
00310 
00311 static int mpf_read_mps_name (
00312   mpf_ILLread_mps_state * state,
00313   mpf_rawlpdata * lp)
00314 {
00315   int rval = 0;
00316 
00317   if (mpf_ILLmps_empty_field (state))
00318   {
00319     mpf_ILLmps_warn (state, "Blank NAME.");
00320   }
00321   else
00322   {
00323     mpf_ILL_UTIL_STR (lp->name, state->field);
00324   }
00325 CLEANUP:
00326   ILL_RESULT (rval, "mpf_read_mps_name");
00327 }
00328 
00329 static int mpf_read_mps_refrow (
00330   mpf_ILLread_mps_state * state,
00331   mpf_rawlpdata * lp)
00332 {
00333   int rval = 0;
00334 
00335   rval = mpf_ILLmps_next_line (state);
00336   if (state->section[ILL_MPS_REFROW] > 1)
00337   {
00338     /* this is the second time we see this section; 
00339      * don't complain about errors */
00340     return 0;
00341   }
00342   if (mpf_ILLmps_empty_key (state) && !mpf_ILLmps_empty_field (state))
00343   {
00344     mpf_ILL_UTIL_STR (lp->refrow, state->field);
00345     return 0;
00346   }
00347   else
00348   {
00349     return mpf_ILLmps_error (state, "Bad row name in REFROW section.\n");
00350   }
00351 CLEANUP:
00352   ILL_RETURN (rval, "mpf_read_mps_refrow");
00353 }
00354 
00355 static int mpf_read_mps_objnamesense (
00356   ILLmps_section sec,
00357   mpf_ILLread_mps_state * state,
00358   mpf_rawlpdata * lp)
00359 {
00360   if (state->section[sec] > 1)
00361   {
00362     /* this is the second time we see this section; just skip next line */
00363     (void) mpf_ILLmps_next_line (state);
00364     return 0;
00365   }
00366   if (mpf_ILLmps_next_line (state) != 0)
00367   {
00368     return mpf_ILLmps_error (state, "Missing %s line at end of file.\n",
00369                          mpf_ILLmps_section_name[sec]);
00370   }
00371   if ((!mpf_ILLmps_empty_key (state)) || mpf_ILLmps_empty_field (state))
00372   {
00373     (void) mpf_ILLmps_error (state, "Bad %s in %s record.\n",
00374                          ((sec == ILL_MPS_OBJNAME) ? "row name"
00375                           : "objective sense"), mpf_ILLmps_section_name[sec]);
00376     if (!mpf_ILLmps_empty_key (state))
00377     {
00378       (void) mpf_read_mps_section (state, lp);
00379     }
00380     return 1;
00381   }
00382   if (sec == ILL_MPS_OBJNAME)
00383   {
00384     if (mpf_read_mps_objname (state))
00385     {
00386       return 1;
00387     }
00388   }
00389   else
00390   {
00391     if (mpf_read_mps_objsense (state, lp) != 0)
00392     {
00393       return 1;
00394     }
00395   }
00396   return 0;
00397 }
00398 
00399 static int mpf_read_mps_objsense (
00400   mpf_ILLread_mps_state * state,
00401   mpf_rawlpdata * lp)
00402 {
00403   int rval = 0;
00404   char *objsense = state->field;
00405 
00406   ILL_FAILfalse (state->section[ILL_MPS_OBJSENSE] == 1, "should never happen");
00407   if (!strcmp (objsense, "MAX") ||
00408       !strcmp (objsense, "Max") ||
00409       !strcmp (objsense, "max") ||
00410       !strcmp (objsense, "MAXIMIZE") ||
00411       !strcmp (objsense, "Maximize") || !strcmp (objsense, "maximize"))
00412   {
00413     lp->objsense = mpf_ILL_MAX;
00414   }
00415   else if (!strcmp (objsense, "MIN") ||
00416            !strcmp (objsense, "Min") ||
00417            !strcmp (objsense, "min") ||
00418            !strcmp (objsense, "MINIMIZE") ||
00419            !strcmp (objsense, "Minimize") || !strcmp (objsense, "minimize"))
00420   {
00421     lp->objsense = mpf_ILL_MIN;
00422   }
00423   else
00424   {
00425     return mpf_ILLmps_error (state, "\"%s\" is no OBJSENSE.\n", objsense);
00426   }
00427 CLEANUP:
00428   ILL_RESULT (rval, "mpf_read_mps_objsense");
00429 }
00430 
00431 static int mpf_read_mps_objname (
00432   mpf_ILLread_mps_state * state)
00433 {
00434   int rval = 0;
00435 
00436   ILL_FAILfalse (state->section[ILL_MPS_OBJNAME] == 1, "should never happen");
00437   mpf_ILL_UTIL_STR (state->obj, state->field);
00438 CLEANUP:
00439   ILL_RETURN (rval, "mpf_read_mps_objname");
00440 }
00441 
00442 static int mpf_read_mps_line_in_section (
00443   mpf_ILLread_mps_state * state,
00444   mpf_rawlpdata * lp)
00445 {
00446   int rval = 0;
00447 
00448   ILL_FAILtrue (!mpf_ILLmps_empty_key (state) || mpf_ILLmps_empty_field (state),
00449                 "no key but at least one field on state->line");
00450 
00451   if (state->active == ILL_MPS_NONE)
00452   {
00453     return mpf_ILLmps_error (state, "Line is in no section.\n");
00454   }
00455   else
00456   {
00457     if (state->section[state->active] == 1)
00458     {
00459       switch (state->active)
00460       {
00461       case ILL_MPS_ROWS:
00462         rval = mpf_add_row (state, lp);
00463         break;
00464       case ILL_MPS_COLS:
00465         rval = mpf_add_col (state, lp);
00466         break;
00467       case ILL_MPS_RHS:
00468         rval = mpf_add_rhs (state, lp);
00469         break;
00470       case ILL_MPS_RANGES:
00471         rval = mpf_add_ranges (state, lp);
00472         break;
00473       case ILL_MPS_BOUNDS:
00474         rval = mpf_add_bounds (state, lp);
00475         break;
00476       default:
00477         ILL_REPRT ("should never get here");
00478         ILL_CLEANUP;
00479       }
00480       if (rval == 0)
00481       {
00482         /* see whether there are extra fields on line */
00483         mpf_ILLmps_check_end_of_line (state);
00484       }
00485     }
00486   }
00487 CLEANUP:
00488   ILL_RESULT (rval, "mpf_read_mps_line_in_section");
00489 }
00490 
00491 static int mpf_add_row (
00492   mpf_ILLread_mps_state * state,
00493   mpf_rawlpdata * lp)
00494 {
00495   int ind, hit, rval = 0;
00496   char sense;
00497 
00498   ILL_FAILtrue (!mpf_ILLmps_empty_key (state) || mpf_ILLmps_empty_field (state),
00499                 "no key but at least one field on state->line");
00500 
00501   /* field should contain exactly one character */
00502   if (state->field[1] == '\0')
00503   {
00504     sense = state->field[0];
00505     if (sense != 'L' && sense != 'G' && sense != 'E' && sense != 'N')
00506     {
00507       return mpf_ILLmps_error (state,
00508                            "Unknown rowsense '%c' in ROWS record.\n", sense);
00509     }
00510     if (mpf_ILLmps_next_field (state) == 0)
00511     {
00512       hit = ILLsymboltab_lookup (&lp->rowtab, state->field, &ind);
00513       if (!hit)
00514       {
00515         rval = mpf_ILLmps_error (state,
00516                              "Repeated row definition for \"%s\".\n",
00517                              state->field);
00518       }
00519       else
00520       {
00521         rval = mpf_ILLraw_add_row (lp, state->field, sense, mpf_zeroLpNum); /* default rhs is 0.0 */
00522       }
00523     }
00524     else
00525     {
00526       rval = mpf_ILLmps_error (state, "Missing rowname in ROWS record.\n");
00527     }
00528   }
00529   else
00530   {
00531     rval = mpf_ILLmps_error (state, "Unknown rowsense '%s' in ROWS record.\n",
00532                          state->field);
00533   }
00534 CLEANUP:
00535   ILL_RESULT (rval, "mpf_add_row");
00536 }
00537 
00538 static int mpf_add_col (
00539   mpf_ILLread_mps_state * state,
00540   mpf_rawlpdata * lp)
00541 {
00542   int rval = 0;
00543 
00544   ILL_FAILtrue (!mpf_ILLmps_empty_key (state) || mpf_ILLmps_empty_field (state),
00545                 "no key but at least one field on state->line");
00546 
00547   if (mpf_is_marker_line (state))
00548   {
00549     return mpf_mps_read_marker_line (state, lp);
00550   }
00551   else
00552   {
00553     return mpf_mps_read_col_line (state, lp);
00554   }
00555 CLEANUP:
00556   ILL_RETURN (rval, "mpf_add_col");
00557 }
00558 
00559 static int mpf_mps_read_col_line (
00560   mpf_ILLread_mps_state * state,
00561   mpf_rawlpdata * lp)
00562 {
00563   int hit, colind, rowind, rval = 0, more, ind;
00564   mpf_t ncoef;
00565 
00566   mpf_EGlpNumInitVar (ncoef);
00567 
00568   ILL_FAILtrue (!mpf_ILLmps_empty_key (state) || mpf_ILLmps_empty_field (state),
00569                 "no key but at least one field on state->line");
00570 
00571   hit = ILLsymboltab_lookup (&lp->coltab, state->field, &colind);
00572   if (hit)
00573   {
00574     rval = mpf_ILLraw_add_col (lp, state->field, state->intvar);
00575     ILL_CLEANUP_IF (rval);
00576     colind = lp->ncols - 1;
00577   }
00578   else
00579   {
00580     if (state->intvar)
00581     {
00582       /*previously declared variable is in integer section */
00583       lp->intmarker[colind] = 1;
00584     }
00585   }
00586 #ifndef NDEBUG
00587   hit = ILLsymboltab_lookup (&lp->coltab, state->field, &ind);
00588   ILL_FAILfalse (colind == ind, "colind should be index of state->field");
00589 #endif
00590   if (state->sosvar == 1)
00591   {
00592     if (mpf_ILLraw_is_mem_other_sos (lp, colind))
00593     {
00594       rval = mpf_ILLmps_error (state,
00595                            "\"%s\" is a member of SOS set #%d.\n",
00596                            mpf_ILLraw_colname (lp, colind),
00597                            lp->is_sos_member[colind] + 1);
00598     }
00599     else
00600     {
00601       rval = mpf_ILLraw_add_sos_member (lp, colind);
00602     }
00603     ILL_CLEANUP_IF (rval);
00604   }
00605 
00606   more = (mpf_ILLmps_next_field (state) == 0);
00607   if (!more)
00608   {
00609     return mpf_ILLmps_error (state, "Missing fields in COLUMNS record.\n");
00610   }
00611   for (more = 1; more; more = (mpf_ILLmps_next_field (state) == 0))
00612   {
00613     hit = ILLsymboltab_lookup (&lp->rowtab, state->field, &rowind);
00614     if (hit)
00615     {
00616       return mpf_ILLmps_error (state, "\"%s\" is not a row name.\n", state->field);
00617     }
00618     if (mpf_ILLmps_next_coef (state, &ncoef) != 0)
00619     {
00620       return mpf_ILLmps_error (state,
00621                            "Missing/Bad coefficient in COLUMNS record.\n");
00622     }
00623     rval = mpf_ILLraw_add_col_coef (lp, colind, rowind, ncoef);
00624   }
00625 CLEANUP:
00626   mpf_EGlpNumClearVar (ncoef);
00627   ILL_RESULT (rval, "mpf_mps_read_col_line");
00628 }
00629 
00630 static int mpf_is_marker_line (
00631   mpf_ILLread_mps_state * state)
00632 {
00633   const char *field = state->line;
00634 
00635   while ((field = mpf_ILLutil_strchr (field, '\'')))
00636   {
00637     if (strncmp (field, "'MARKER'", (size_t) 8) == 0)
00638     {
00639       return 1;
00640     }
00641     while ((!mpf_ILL_ISBLANK (field)) && (*field != '\0'))
00642     {
00643       field++;
00644     }
00645   }
00646   return 0;
00647 }
00648 
00649 static int mpf_mps_read_marker_line (
00650   mpf_ILLread_mps_state * state,
00651   mpf_rawlpdata * lp)
00652 {
00653   int rval = 0;
00654   int sos_type = mpf_ILL_SOS_TYPE1;
00655   int sos_line = 0;
00656   int cur_sos_mode = state->sosvar;
00657 
00658   if (strcmp (state->field, "S2") == 0)
00659   {
00660     sos_type = mpf_ILL_SOS_TYPE2;
00661     sos_line = 1;
00662   }
00663   else if (strcmp (state->field, "S1") == 0)
00664   {
00665     sos_line = 1;
00666   }
00667   if (sos_line)
00668   {
00669     rval = mpf_ILLmps_next_field (state);
00670   }
00671   rval = rval || mpf_ILLmps_next_field (state); /* swallow marker-id */
00672   if (strcmp (state->field, "'MARKER'"))
00673   {
00674     return mpf_ILLmps_error (state, "Bad 'MARKER' line.\n");
00675   }
00676   if (mpf_ILLmps_next_field (state))
00677   {
00678     return mpf_ILLmps_error (state, "Missing field on 'MARKER' line.\n");
00679   }
00680   rval = mpf_ILLmps_int_sos_mode (state);
00681   if (rval == 0)
00682   {
00683     if (cur_sos_mode != state->sosvar)
00684     {
00685       if (state->sosvar)
00686       {
00687         /* beginning of a new set */
00688         rval = mpf_ILLraw_add_sos (lp, sos_type);
00689       }
00690     }
00691   }
00692   ILL_RESULT (rval, "mpf_mps_read_marker_line");
00693 }
00694 
00695 static int mpf_add_rhs (
00696   mpf_ILLread_mps_state * state,
00697   mpf_rawlpdata * lp)
00698 {
00699   int rowind, more_fields, skip;
00700   const char *rhsname;
00701   mpf_t rhs;
00702 
00703   mpf_EGlpNumInitVar (rhs);
00704 
00705   rhsname = mpf_ILLmps_possibly_blank_name (state->field, state, &lp->rowtab);
00706   if (mpf_ILLraw_set_rhs_name (lp, rhsname, &skip))
00707   {
00708     mpf_ILLmps_error (state, "Could not add right hand side.\n");
00709   }
00710 
00711   if (skip)
00712   {
00713     mpf_ILLmps_set_end_of_line (state); /* to avoid warning about extra fields */
00714   }
00715   else
00716   {
00717     if (strcmp (rhsname, " "))
00718     {
00719       /* field is non blank rhs name; advance to row name  */
00720       if (mpf_ILLmps_next_field (state))
00721       {
00722         return mpf_ILLmps_error (state, "Missing row name in RHS record.\n");
00723       }
00724     }
00725     for (more_fields = 1; more_fields; more_fields = !mpf_ILLmps_next_field (state))
00726     {
00727       if (ILLsymboltab_lookup (&lp->rowtab, state->field, &rowind))
00728       {
00729         return mpf_ILLmps_error (state, "\"%s\" is not a row name.\n",
00730                              state->field);
00731       }
00732       if (mpf_ILLmps_next_coef (state, &rhs))
00733       {
00734         return mpf_ILLmps_error (state, "Missing/Bad coefficient in RHS record.\n");
00735       }
00736       if (lp->rhsind[rowind])
00737       {
00738         return mpf_ILLmps_error (state, "Two rhs values for row \"%s\".\n",
00739                              state->field);
00740       }
00741       else
00742       {
00743         if (lp->rowsense[rowind] == 'N')
00744         {
00745           mpf_ILLmps_warn (state,
00746                        "Ignoring right hand side for N-row \"%s\".",
00747                        mpf_ILLraw_rowname (lp, rowind));
00748         }
00749         else
00750         {
00751           mpf_EGlpNumCopy (lp->rhs[rowind], rhs);
00752           lp->rhsind[rowind] = 1;
00753         }
00754       }
00755     }
00756   }
00757   mpf_EGlpNumClearVar (rhs);
00758   return 0;
00759 }
00760 
00761 static int mpf_add_bounds (
00762   mpf_ILLread_mps_state * state,
00763   mpf_rawlpdata * lp)
00764 {
00765   char bndtype[3];
00766   const char *bounds_name;
00767   int colind, skip, rval = 0;
00768   mpf_t bnd;
00769 
00770   mpf_EGlpNumInitVar (bnd);
00771 
00772   ILL_FAILtrue (!mpf_ILLmps_empty_key (state) || mpf_ILLmps_empty_field (state),
00773                 "no key but at least one field on state->line");
00774 
00775   if (mpf_ILLutil_index (mpf_mps_bound_name, state->field) < 0)
00776   {
00777     return mpf_ILLmps_error (state, "\"%s\" is not a BOUNDS type.\n", state->field);
00778   }
00779   strcpy (bndtype, state->field);
00780 
00781   if (mpf_ILLmps_next_field (state) != 0)
00782   {
00783     return mpf_ILLmps_error (state,
00784                          "No bounds/column identifier in BOUNDS record.\n");
00785   }
00786 
00787   bounds_name = mpf_ILLmps_possibly_blank_name (state->field, state, &lp->coltab);
00788   if (bounds_name == NULL)
00789   {
00790     return 1;
00791   }
00792   if (mpf_ILLraw_set_bounds_name (lp, bounds_name, &skip))
00793   {
00794     return 1;
00795   }
00796   if (skip)
00797   {
00798     mpf_ILLmps_set_end_of_line (state); /* to avoid warning about extra fields */
00799   }
00800   else
00801   {
00802     if (strcmp (bounds_name, " "))
00803     {
00804       /* non empty bounds_name ==> advance to col name field */
00805       if (mpf_ILLmps_next_field (state))
00806       {
00807         return mpf_ILLmps_error (state, "Missing column field in BOUNDS record.\n");
00808       }
00809     }
00810     if (ILLsymboltab_lookup (&lp->coltab, state->field, &colind))
00811     {
00812       return mpf_ILLmps_error (state, "\"%s\" is not a column name.\n",
00813                            state->field);
00814     }
00815     mpf_EGlpNumZero (bnd);
00816     if (strcmp (bndtype, "FR") && strcmp (bndtype, "BV") &&
00817         strcmp (bndtype, "MI") && strcmp (bndtype, "PL"))
00818     {
00819       /* neither "FR", "BV", "MI" nor "PL" ==> there should be a bound */
00820       if (mpf_ILLmps_next_bound (state, &bnd))
00821       {
00822         return mpf_ILLmps_error (state,
00823                              "Missing/Bad bound field in BOUNDS record.\n");
00824       }
00825     }
00826     mpf_mps_set_bound (lp, state, colind, bndtype, bnd);
00827   }
00828 CLEANUP:
00829   mpf_EGlpNumClearVar (bnd);
00830   ILL_RESULT (rval, "mpf_add_bounds");
00831 }
00832 
00833 static void mpf_mps_set_bound (
00834   mpf_rawlpdata * lp,
00835   mpf_ILLread_mps_state * state,
00836   int colind,
00837   const char *bndtype,
00838   mpf_t bnd)
00839 {
00840   const char *msg = NULL;
00841 
00842   if (!strcmp (bndtype, "LO"))
00843   {
00844     msg = mpf_ILLraw_set_lowerBound (lp, colind, bnd);
00845   }
00846   else if (!strcmp (bndtype, "UP"))
00847   {
00848     msg = mpf_ILLraw_set_upperBound (lp, colind, bnd);
00849   }
00850   else if (!strcmp (bndtype, "FX"))
00851   {
00852     msg = mpf_ILLraw_set_fixedBound (lp, colind, bnd);
00853   }
00854   else if (!strcmp (bndtype, "FR"))
00855   {
00856     msg = mpf_ILLraw_set_unbound (lp, colind);
00857   }
00858   else if (!strcmp (bndtype, "BV"))
00859   {
00860     msg = mpf_ILLraw_set_binaryBound (lp, colind);
00861     if (msg == NULL)
00862     {
00863       lp->intmarker[colind] = 1;
00864     }
00865   }
00866   else if (!strcmp (bndtype, "UI"))
00867   {
00868     msg = mpf_ILLraw_set_upperBound (lp, colind, bnd);
00869     if (msg == NULL)
00870     {
00871       lp->intmarker[colind] = 1;
00872     }
00873   }
00874   else if (!strcmp (bndtype, "LI"))
00875   {
00876     msg = mpf_ILLraw_set_lowerBound (lp, colind, bnd);
00877     if (msg == NULL)
00878     {
00879       lp->intmarker[colind] = 1;
00880     }
00881   }
00882   else if (!strcmp (bndtype, "MI"))
00883   {
00884     msg = mpf_ILLraw_set_lowerBound (lp, colind, mpf_ILL_MINDOUBLE);
00885   }
00886   else if (!strcmp (bndtype, "PL"))
00887   {
00888     msg = mpf_ILLraw_set_upperBound (lp, colind, mpf_ILL_MAXDOUBLE);
00889   }
00890   else
00891   {
00892     ILL_REPRT ("should never get here");
00893     ILL_CLEANUP;
00894   }
00895   mpf_ILLmps_warn (state, msg);
00896 CLEANUP:
00897   return;
00898 }
00899 
00900 static int mpf_add_ranges (
00901   mpf_ILLread_mps_state * state,
00902   mpf_rawlpdata * lp)
00903 {
00904   const char *rangesname;
00905   int skip, more_fields, rowind;
00906   mpf_t ntmp;
00907 
00908   mpf_EGlpNumInitVar (ntmp);
00909 
00910   rangesname = mpf_ILLmps_possibly_blank_name (state->field, state, &lp->rowtab);
00911   if (mpf_ILLraw_set_ranges_name (lp, rangesname, &skip))
00912   {
00913     return mpf_ILLmps_error (state, "Could not add range.\n");
00914   }
00915   if (skip)
00916   {
00917     mpf_ILLmps_set_end_of_line (state); /* to avoid warning about extra fields */
00918   }
00919   else
00920   {
00921     if (strcmp (rangesname, " "))
00922     {
00923       /* field is non blank ranges name; advance to row name */
00924       if (mpf_ILLmps_next_field (state))
00925       {
00926         return mpf_ILLmps_error (state, "Missing row name in RANGES record.");
00927       }
00928     }
00929     for (more_fields = 1; more_fields; more_fields = !mpf_ILLmps_next_field (state))
00930     {
00931       if (ILLsymboltab_lookup (&lp->rowtab, state->field, &rowind))
00932       {
00933         return mpf_ILLmps_error (state, "\"%s\" is not a row name.\n",
00934                              state->field);
00935       }
00936       if (mpf_ILLmps_next_coef (state, &ntmp))
00937       {
00938         return mpf_ILLmps_error (state,
00939                              "Missing/Bad coefficient in RANGES record.\n");
00940       }
00941       if (lp->rangesind[rowind])
00942       {
00943         mpf_ILLmps_warn (state, "Ignoring second RANGE value %s \"%s\".",
00944                      "for row", mpf_ILLraw_rowname (lp, rowind));
00945       }
00946       else
00947       {
00948         if (lp->rowsense[rowind] != 'N')
00949         {
00950           if (mpf_ILLraw_add_ranges_coef (lp, rowind, ntmp))
00951             return 1;
00952         }
00953         else
00954         {
00955           mpf_ILLmps_warn (state, "Ignoring RANGE value for N-row \"%s\".",
00956                        mpf_ILLraw_rowname (lp, rowind));
00957         }
00958       }
00959     }
00960   }
00961   mpf_EGlpNumClearVar (ntmp);
00962   return 0;
00963 }
00964 
00965 
00966 static int mpf_mps_fill_in (
00967   mpf_rawlpdata * lp,
00968   const char *obj)
00969 {
00970   int i, hit, rval = 0;
00971 
00972   /* find the objective function -- the first N row if obj is not defined  */
00973   if (obj)
00974   {
00975     hit = ILLsymboltab_lookup (&lp->rowtab, obj, &lp->objindex);
00976     if (hit)
00977     {
00978       return mpf_ILLdata_error (lp->error_collector,
00979                             "Bad objective name \"%s\".\n", obj);
00980     }
00981     else if (lp->rowsense[lp->objindex] != 'N')
00982     {
00983       mpf_ILLdata_warn (lp->error_collector,
00984                     "Making objective row \"%s\" a N-row.", obj);
00985     }
00986     lp->rowsense[lp->objindex] = 'N';
00987   }
00988   else
00989   {
00990     for (i = 0; i < lp->nrows; i++)
00991     {
00992       if (lp->rowsense[i] == 'N')
00993       {
00994         lp->objindex = i;
00995         break;
00996       }
00997     }
00998     if (i == lp->nrows)
00999     {
01000       return mpf_ILLdata_error (lp->error_collector,
01001                             "No N-row in lp definition.\n");
01002     }
01003   }
01004 
01005   if (lp->ncols > 0)
01006   {
01007     rval = mpf_ILLraw_fill_in_bounds (lp);
01008   }
01009 
01010   /* set weights of sos set members */
01011   if (lp->refrow)
01012   {
01013     /* take the values from refrow */
01014     mpf_t weight;
01015     mpf_colptr *cp;
01016 
01017     mpf_EGlpNumInitVar (weight);
01018 
01019     hit = ILLsymboltab_lookup (&lp->rowtab, lp->refrow, &lp->refrowind);
01020     if (hit)
01021     {
01022       return mpf_ILLdata_error (lp->error_collector,
01023                             "REFROW \"%s\" is not a row name.\n", lp->refrow);
01024     }
01025     for (i = 0; i < lp->nsos_member; i++)
01026     {
01027       for (cp = lp->cols[lp->sos_col[i]]; cp != NULL; cp = cp->next)
01028       {
01029         if (cp->this_val == lp->refrowind)
01030           break;
01031       }
01032       if ((cp != NULL) && mpf_EGlpNumIsNeqqZero (cp->coef))
01033       {
01034         mpf_EGlpNumCopy (weight, cp->coef);
01035       }
01036       else
01037       {
01038         mpf_ILLdata_warn (lp->error_collector,
01039                       "\"%s\" has 0.0 coefficient in REFROW \"%s\".",
01040                       mpf_ILLraw_colname (lp, lp->sos_col[i]), lp->refrow);
01041         mpf_EGlpNumZero (weight);
01042       }
01043       mpf_EGlpNumCopy (lp->sos_weight[i], weight);
01044     }
01045     mpf_EGlpNumClearVar (weight);
01046   }
01047   else
01048   {                             /* no refrow */
01049     /* set weights to 1, 2, 3, ... in order of definition */
01050     int si, w;
01051     mpf_sosptr *set;
01052 
01053     for (si = 0; si < lp->nsos; si++)
01054     {
01055       set = lp->sos_set + si;
01056       w = 1;
01057       for (i = set->first; i < set->first + set->nelem; i++)
01058       {
01059         mpf_EGlpNumSet (lp->sos_weight[i], (double) w);
01060         w++;
01061       }
01062     }
01063   }
01064   ILL_IFTRACE ("bound %lf <= x1 <= %lf\n", mpf_EGlpNumToLf (lp->lower[0]),
01065                mpf_EGlpNumToLf (lp->upper[0]));
01066   ILL_IFTRACE ("MAXDOUBLE %lf MINDOUBLE %lf\n", mpf_EGlpNumToLf (mpf_ILL_MAXDOUBLE),
01067                mpf_EGlpNumToLf (mpf_ILL_MINDOUBLE));
01068   ILL_RESULT (rval, "mpf_mps_fill_in");
01069 }
01070 
01071 /****************************************************************************/
01072 /* writing 
01073  */
01074 
01075 static int mpf_mps_write_col (
01076   int i,
01077   int iorig,
01078   char *colname,
01079   mpf_ILLlpdata * lp,
01080   char **rownames,
01081   int intmode,
01082   char *objname);
01083 
01084 int mpf_ILLwrite_mps (
01085   mpf_ILLlpdata * lp,
01086   mpf_qserror_collector * collector)
01087 {
01088   int rval = 0;
01089   int marker = 0;
01090   int intmode = 0;
01091   int i, ri, set, el, empty, prtLower, prtUpper;
01092   char **colnames = (char **) NULL;
01093   char **rownames = (char **) NULL;
01094   mpf_ILLlp_rows lp_rows, *lprows = NULL;
01095   char buf[ILL_namebufsize];
01096   char *objname = NULL;
01097   char *str;
01098 
01099   ILL_CHECKnull (lp, "lp must not be null");
01100   ILL_FAILtrue (lp->probname == NULL, "oops should never happen");
01101 
01102   ILL_FAILfalse (lp->colnames != NULL, "colnames != NULL");
01103   ILL_FAILfalse (lp->rownames != NULL, "colnames != NULL");
01104   colnames = lp->colnames;
01105   rownames = lp->rownames;
01106 
01107   objname = lp->objname;
01108   if (objname == (char *) NULL)
01109   {
01110     strcpy (buf, "obj");
01111     rval = ILLsymboltab_uname (&lp->rowtab, buf, "", NULL);
01112     ILL_CLEANUP_IF (rval);
01113     mpf_ILL_UTIL_STR (objname, buf);
01114   }
01115   mpf_ILLprint_report (lp, "NAME    %s\n", lp->probname);
01116   mpf_ILLprint_report (lp, "OBJSENSE\n  %s\n",
01117                    (lp->objsense == mpf_ILL_MIN) ? "MIN" : "MAX");
01118   mpf_ILLprint_report (lp, "OBJNAME\n  %s\n", objname);
01119   if (lp->refrowname)
01120   {
01121     mpf_ILLprint_report (lp, "REFROW\n");
01122     mpf_ILLprint_report (lp, " %s\n", lp->refrowname);
01123   }
01124 
01125 
01126   mpf_ILLprint_report (lp, "ROWS\n");
01127   mpf_ILLprint_report (lp, " N  %s\n", objname);
01128   if (lp->refrowname && (lp->refind == -1))
01129   {
01130     mpf_ILLprint_report (lp, " N  %s\n", lp->refrowname);
01131   }
01132 
01133   lprows = &lp_rows;
01134   rval = mpf_ILLlp_rows_init (lprows, lp, 0);
01135   ILL_CLEANUP_IF (rval);
01136   for (i = 0; i < lp->nrows; i++)
01137   {
01138     if (lprows->rowcnt[i] == 0)
01139     {
01140       mpf_ILLdata_warn (collector,
01141                     "Deleting empty row \"%s\" from constraints.", rownames[i]);
01142       continue;
01143     }
01144     switch (lp->sense[i])
01145     {
01146     case 'G':
01147       mpf_ILLprint_report (lp, " G  ");
01148       break;
01149     case 'L':
01150       mpf_ILLprint_report (lp, " L  ");
01151       break;
01152     case 'E':
01153       mpf_ILLprint_report (lp, " E  ");
01154       break;
01155     case 'R':
01156       mpf_ILLprint_report (lp, " G  ");
01157       break;
01158     }
01159     mpf_ILLprint_report (lp, "%s\n", rownames[i]);
01160   }
01161 
01162   mpf_ILLprint_report (lp, "COLUMNS\n");
01163   for (set = 0; set < lp->sos.matcols; set++)
01164   {
01165     ILL_FAILtrue (lp->sos_type == NULL, "must have non NULL sos_type");
01166     ILL_FAILtrue (lp->is_sos_mem == NULL, "must have non NULL is_sos_mem");
01167     empty = 1;
01168     for (el = lp->sos.matbeg[set];
01169          el < lp->sos.matbeg[set] + lp->sos.matcnt[set]; el++)
01170     {
01171       if (empty)
01172       {
01173         mpf_ILLprint_report (lp, " %s SOS%dqs    'MARKER'    'SOSORG'\n",
01174                          ((lp->sos_type[set] == mpf_ILL_SOS_TYPE1)) ? "S1" : "S2",
01175                          marker++);
01176         empty = 0;
01177       }
01178       ri = lp->sos.matind[el];
01179       i = lp->structmap[ri];
01180       intmode = mpf_mps_write_col (i, ri, colnames[ri], lp, rownames,
01181                                intmode, objname);
01182       if (lp->refrowname && (lp->refind == -1))
01183       {
01184         mpf_ILLprint_report (lp, "  %s    %s    %g\n",
01185                          colnames[ri], lp->refrowname, lp->sos.matval[el]);
01186       }
01187     }
01188     if (!empty)
01189     {
01190       mpf_ILLprint_report (lp, " SOS%dqs       'MARKER'    'SOSEND'\n", marker++);
01191     }
01192   }
01193   for (ri = 0; ri < lp->nstruct; ri++)
01194   {
01195     if (lp->is_sos_mem == (int *) NULL || lp->is_sos_mem[ri] == -1)
01196     {
01197       i = lp->structmap[ri];
01198       intmode = mpf_mps_write_col (i, ri, colnames[ri], lp, rownames,
01199                                intmode, objname);
01200     }
01201   }
01202   if (intmode)
01203   {
01204     mpf_ILLprint_report (lp, " MARK%dqs      'MARKER'    'INTEND'\n", lp->nstruct);
01205   }
01206 
01207   mpf_ILLprint_report (lp, "RHS\n");
01208   for (i = 0; i < lp->nrows; i++)
01209   {
01210     if ((lprows->rowcnt[i] != 0) && mpf_EGlpNumIsNeqqZero (lp->rhs[i]))
01211     {
01212       str = mpf_EGlpNumGetStr(lp->rhs[i]);
01213       mpf_ILLprint_report (lp, " RHS    %s    %s\n", rownames[i], str);
01214       EGfree(str);
01215     }
01216   }
01217 
01218   if (lp->rangeval)
01219   {
01220     mpf_ILLprint_report (lp, "RANGES\n");
01221     for (i = 0; i < lp->nrows; i++)
01222     {
01223       if ((lprows->rowcnt[i] != 0) && mpf_EGlpNumIsNeqqZero (lp->rangeval[i]))
01224       {
01225         str = mpf_EGlpNumGetStr(lp->rangeval[i]);
01226         mpf_ILLprint_report (lp, " RANGE    %s    %s\n", rownames[i], str);
01227         EGfree(str);
01228       }
01229     }
01230   }
01231 
01232   ri = mpf_ILLraw_first_nondefault_bound (lp);
01233   if (ri != lp->nstruct)
01234   {
01235     mpf_ILLprint_report (lp, "BOUNDS\n");
01236     for (ri = ri; ri < lp->nstruct; ri++)
01237     {
01238       i = lp->structmap[ri];
01239       if (mpf_EGlpNumIsEqqual (lp->lower[i], lp->upper[i]))
01240       {
01241         str = mpf_EGlpNumGetStr(lp->lower[i]);
01242         mpf_ILLprint_report (lp, " FX BOUND    %s    %s\n", colnames[ri], str);
01243         EGfree(str);
01244         continue;
01245       }
01246       if ((mpf_EGlpNumIsEqqual (lp->lower[i], mpf_ILL_MINDOUBLE)) &&
01247           (mpf_EGlpNumIsEqqual (lp->upper[i], mpf_ILL_MAXDOUBLE)))
01248       {
01249         mpf_ILLprint_report (lp, " FR BOUND    %s\n", colnames[ri]);
01250         continue;
01251       }
01252       prtLower = !mpf_ILLraw_default_lower (lp, i);
01253       prtUpper = !mpf_ILLraw_default_upper (lp, i, ri);
01254       if (prtLower)
01255       {
01256         if (mpf_EGlpNumIsEqqual (lp->lower[i], mpf_ILL_MINDOUBLE))
01257         {
01258           mpf_ILLprint_report (lp, " MI BOUND    %s\n", colnames[ri]);
01259         }
01260         else
01261         {
01262           str = mpf_EGlpNumGetStr(lp->lower[i]);
01263           mpf_ILLprint_report (lp, " LO BOUND    %s    %s\n", colnames[ri], str);
01264           EGfree(str);
01265         }
01266       }
01267       if (prtUpper)
01268       {
01269         if (mpf_EGlpNumIsEqqual (lp->upper[i], mpf_ILL_MAXDOUBLE))
01270         {
01271           mpf_ILLprint_report (lp, " PL BOUND    %s\n", colnames[ri]);
01272         }
01273         else
01274         {
01275           str = mpf_EGlpNumGetStr(lp->upper[i]);
01276           mpf_ILLprint_report (lp, " UP BOUND    %s    %s\n", colnames[ri], str);
01277           EGfree(str);
01278         }
01279       }
01280     }
01281   }
01282   mpf_ILLprint_report (lp, "ENDATA\n");
01283 
01284 CLEANUP:
01285   mpf_ILLlp_rows_clear (lprows);
01286   if (!lp->colnames)
01287   {
01288     ILLfree_names (colnames, lp->ncols);
01289   }
01290   if (!lp->rownames)
01291   {
01292     ILLfree_names (rownames, lp->nrows);
01293   }
01294   if (objname != lp->objname)
01295   {
01296     ILL_IFFREE (objname, char);
01297   }
01298   ILL_RESULT (rval, "ILLlpdata_mpswrite");
01299 }
01300 
01301 static int mpf_mps_write_col (
01302   int i,
01303   int iorig,
01304   char *colname,
01305   mpf_ILLlpdata * lp,
01306   char **rownames,
01307   int intmode,
01308   char *objname)
01309 {
01310   int row, k;
01311   char*str;
01312   mpf_ILLmatrix *A;
01313 
01314   A = &lp->A;
01315   if (lp->intmarker && (lp->intmarker[iorig] != intmode))
01316   {
01317     mpf_ILLprint_report (lp, " MARK%dqs      'MARKER'    '%s'\n", iorig,
01318                      (lp->intmarker[iorig] ? "INTORG" : "INTEND"));
01319     intmode = lp->intmarker[iorig];
01320   }
01321   if (mpf_EGlpNumIsNeqqZero (lp->obj[i]))
01322   {
01323     str = mpf_EGlpNumGetStr(lp->obj[i]);
01324     mpf_ILLprint_report (lp, "  %s    %s    %s\n", colname, objname, str);
01325     EGfree(str);
01326   }
01327   for (k = A->matbeg[i]; k < A->matbeg[i] + A->matcnt[i]; k++)
01328   {
01329     row = A->matind[k];
01330     str = mpf_EGlpNumGetStr(A->matval[k]);
01331     mpf_ILLprint_report (lp, "  %s    %s    %s\n", colname, rownames[row], str);
01332     EGfree(str);
01333   }
01334   return intmode;
01335 }

Generated on Thu Mar 29 09:32:31 2012 for QSopt_ex by  doxygen 1.4.7