Index: ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/ChangeLog,v retrieving revision 1.3449 retrieving revision 1.3472 diff -u -w -r1.3449 -r1.3472 --- ChangeLog 19 Jul 2004 12:40:11 -0000 1.3449 +++ ChangeLog 7 Aug 2004 20:17:23 -0000 1.3472 @@ -1,3 +1,182 @@ +2004-08-02 Jody Goldberg + + * src/expr.c (gnm_expr_eval) : Functions returning an array when only + scalar results are permitted produce #VALUE! + + * src/style.c (style_default_halign) : Fix drilling down into arrays + + * src/wbc-gtk.c (cb_add_menus_toolbars) : Init state. + +2004-08-01 Jody Goldberg + + * src/func.c (function_call_with_list) : Array parms seem to accept + scalars. + + * src/value.c (value_dup) : Accept NULL + + * src/expr.c (gnm_expr_eval) : do intersection for constant arrays too + +2004-07-30 Jody Goldberg + + * src/wbc-gtk.c (wbc_gtk_init_zoom) : We need a label for the action + in case it is a menu item. + +2004-07-30 Morten Welinder + + * src/wbc-gtk.c (cb_add_menus_toolbars): Plug leaks. + + * src/file.c (gnm_file_saver_unregister): First use, then free. + +2004-07-30 Jon K Hellan + + * src/command-context.c (format_message): Strange things happen if + we treat the unknown string we are passed as a format string. + +2004-07-30 Yukihiro Nakai + + * src/wbcg-actions.c: Don't forget to mark with N_() + http://bugzilla.gnome.org/show_bug.cgi?id=148746 + +2004-07-28 Andreas J. Guelzow + + * src/hlink.h (GNM_HLINK_URL_TYPE): new + (GNM_HLINK_URL): new + (IS_GNM_HLINK_URL): new + +2004-07-27 Morten Welinder + + * configure.in: Check for log1p also. + +2004-07-27 Morten Welinder + + * src/gnumeric-gconf.c (gnm_conf_init_printer_decoration_font): (void). + * src/application.c (cb_flag_windows_changed): Ditto. + +2004-07-27 Andreas J. Guelzow + + * configure.in: use AM_PROG_LEX rather than AC_PROG_LEX + +2004-07-27 Jody Goldberg + + * src/wbc-gtk.c (cb_regenerate_window_menu) : new. + +2004-07-26 Jody Goldberg + + * configure.in : bump pxlib req to 0.3.0 per Uwe's request + + http://bugzilla.gnome.org/show_bug.cgi?id=148412 + * src/workbook-control-gui-priv.h : typo in the new combos + +2004-07-26 Morten Welinder + + * src/sheet.c (sheet_colrow_group_ungroup): Don't use ?: result + as a structure. Go via pointers. + +2004-07-23 Morten Welinder + + * src/wbcg-actions.c: Add Ctrl-Shift-D for double underline. + + * src/commands.c (cmd_merge_data): Fix 0/NULL mixup. + * src/complete.c (complete_finalize): Ditto. + * src/sheet.c (sheet_range_calc_spans, sheet_toggle_hide_zeros): Ditto. + * src/colrow.c (colrow_set_sizes, colrow_restore_state_group): Ditto. + * src/gui-util.c (gnumeric_popup_menu): Ditto. + * src/item-cursor.c (item_cursor_unrealize): Ditto. + * src/value.c (value_peek_string): Ditto. + + * src/expr.c (cb_bin_arith): Add const-casts. + + * src/format.c (format_entry_set_fmt): Use g_strdup, not plain + strdup. + + * src/wbcg-actions.c: Add Ctrl-5 for strikethough. + +2004-07-22 Jody Goldberg + + * src/workbook-edit.c (wbcg_edit_finish) : change 'accept' into an + enum and handle arrays and range fills here. + + * src/parser.y : Add distinct productions for array elements with + {1\2\3;4\5\6} vs {1,2,3;4,5,6} + (build_array) : add some error messages empty and asymetric arrays. + +2004-07-22 Jody Goldberg + + * src/workbook-control-gui.c (wbcg_set_standard_toolbar_visible) : delete + (wbcg_set_format_toolbar_visible) : delete + (wbcg_set_object_toolbar_visible) : delete + (wbcg_copy_toolbar_visibility) : make virtual for now + + * src/wbcg-actions.c (cb_view_standard_toolbar) : delete + (cb_view_format_toolbar) : delete + (cb_view_object_toolbar) : delete + +2004-07-22 Jody Goldberg + + * src/wbc-gtk.c (cb_handlebox_visible) : new. + (cb_add_menus_toolbars) : connect it here and create a menu entry in + the view menu to toggle the visibility. + +2004-07-21 Morten Welinder + + * src/gutils.c (expm1gnum): Supply this when missing. + +2004-07-20 Jody Goldberg + + * configure.in : add test for sysconf as per Mark's request + + * src/gnumeric-pane.c (cb_pane_popup_menu) : cheese up a little hack + to get 'popup-menu' binding context menus for col/row headers too. + (gnm_pane_clear_obj_size_tip) : new + (gnm_pane_display_obj_size_tip) : new + (gnm_pane_object_move) : use gnm_pane_display_obj_size_tip to provide + an indication of the object size. + +2004-07-20 Morten Welinder + + * src/mathfunc.c (lbeta3): Use lgamma_rgnum. + + * src/gutils.c (lgamma_rgnum): When needed, supply this. + + * src/numbers.h (lgamma_rgnum): Add definition for this. + + * configure.in (lgamma_r): Check for this. Check for long double + version when needed. + +2004-07-20 Morten Welinder + + * src/ranges.c (range_is_infinite): Fix. (Still unused, though.) + +2004-07-19 Jody Goldberg + win32 portability patch from Mark Gilbert + + * src/io-context.c (io_progress_update) : use g_get_current_time + instead of gettimeofday. + +2004-07-19 Jody Goldberg + + * src/dialogs/dialog-about.c : Update a bit. Add + * doc/C/about-authors.xml : ditto + - Emmanuel + - Jean + - Hal + - Uwe + - Michael Devine + +2004-07-19 Jody Goldberg + + * src/xml-io.c (xml_init) : make the dom exporter a lower priority + than the sax exporter so that we can start phasing it out. + +2004-07-19 Jody Goldberg + Uwe Steinmann + + * plugins/Makefile.am : merge in the paradox importer + +2004-07-19 Jody Goldberg + + * configure.in : Post release version bump + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: Makefile.am =================================================================== RCS file: /cvs/gnome/gnumeric/Makefile.am,v retrieving revision 1.83 retrieving revision 1.84 diff -u -w -r1.83 -r1.84 --- Makefile.am 7 Apr 2004 14:47:04 -0000 1.83 +++ Makefile.am 29 Jul 2004 04:36:36 -0000 1.84 @@ -24,11 +24,13 @@ Mimedir = $(datadir)/mime-info Mime_DATA = gnumeric.keys gnumeric.mime +if WITH_GNOME serverdir = $(libdir)/bonobo/servers/ server_in_files = GNOME_Gnumeric.server.in server_DATA = GNOME_Gnumeric.server GNOME_Gnumeric.server.in : GNOME_Gnumeric.server.in.in Makefile sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@ +endif @INTLTOOL_SERVER_RULE@ Index: NEWS =================================================================== RCS file: /cvs/gnome/gnumeric/NEWS,v retrieving revision 1.1532 retrieving revision 1.1549 diff -u -w -r1.1532 -r1.1549 --- NEWS 17 Jul 2004 14:06:37 -0000 1.1532 +++ NEWS 5 Aug 2004 14:40:08 -0000 1.1549 @@ -1,3 +1,47 @@ +Gnumeric 1.3.2 + +Andreas: + * Improve `F-Test' tool + http://bugzilla.gnome.org/show_bug.cgi?id=148724 + http://bugzilla.gnome.org/show_bug.cgi?id=149247 + * Some minor About box changes + +Emmanuel Pacaud: + * Add logarithmic mapping support to xy plots + +Harald Ashburner: + * Updates for the option pricers. + * Pricers for + : American/European options to exchange one asset for another + : Options on spreads between futures + : Payouts on the most favorable state during a period + +Jody: + * Use SAX exporter by default. + * Enable 'popup-menu' binding support + * Add tooltip for object resizing + * Misc ancient array formula parsing and entry problems + * Jump to lp_solve 5.0 + * Jump to GLPK 4.5 + * Add a View -> Windows menu + +Morten: + * Add Ctrl-5 for strike-through. + * Add Ctrl-Shift-D for double underline. + * Fix error return for GEOMEAN and HARMEAN. + * Fix IMCONJUGATE and IMABS for reals. [#148323, backported] + * New function INVSUMINV. + * Fix crash with format toolbar in menu mode. + * Fix crash on exit. [backported] + +Yukihiro Nakai: + * Export links when saving as html. + +Uwe Steinmann: + * Paradox DB import. + +-------------------------------------------------------------------------- + Gnumeric 1.3.1 Andreas: Index: configure.in =================================================================== RCS file: /cvs/gnome/gnumeric/configure.in,v retrieving revision 1.631 retrieving revision 1.640 diff -u -w -r1.631 -r1.640 --- configure.in 19 Jul 2004 12:40:12 -0000 1.631 +++ configure.in 28 Jul 2004 01:15:12 -0000 1.640 @@ -1,6 +1,6 @@ -*- mode: m4 -*- AC_PREREQ(2.52) -AC_INIT(gnumeric, 1.3.1, +AC_INIT(gnumeric, 1.3.2, http://bugzilla.gnome.org/enter_bug.cgi?product=gnumeric) AC_CONFIG_SRCDIR(src/sheet.h) @@ -19,6 +19,7 @@ AC_ISC_POSIX AC_PROG_CC AC_PROG_YACC +AM_PROG_LEX AC_PROG_LN_S AM_PROG_LIBTOOL AC_STDC_HEADERS @@ -315,6 +316,30 @@ enable_psiconv=false AM_CONDITIONAL(WITH_PSICONV,$enable_psiconv) +try_paradox=true +enable_paradox=false +AC_ARG_WITH(paradox, + [--{with,without}-paradox Compile with Paradox support or without it], + if test "x$withval" = xno; then + try_paradox=false + fi +) + +if $try_paradox; then + PKG_CHECK_MODULES(PARADOX, [ + pxlib >= 0.3.0 + ], + [paradox_msg="yes"], + [paradox_msg="missing dependencies"]) +fi + +if test "x$paradox_msg" = "xyes"; then + enable_paradox=true +fi +AM_CONDITIONAL(WITH_PARADOX, $enable_paradox) +AC_SUBST(PARADOX_LIBS) +AC_SUBST(PARADOX_CFLAGS) + dnl ************************************************** dnl stolen from gtk+ AC_MSG_CHECKING(whether make is GNU Make) @@ -368,7 +393,7 @@ AC_CHECK_HEADERS(ieeefp.h ieee754.h) dnl Check for some functions -AC_CHECK_FUNCS(random drand48 finite memmove mkdtemp uname times) +AC_CHECK_FUNCS(random drand48 finite memmove mkdtemp uname times sysconf) dnl FIXME: Does this really belong here? AC_CHECK_FUNC(bind_textdomain_codeset,,[AC_CHECK_LIB(intl,bind_textdomain_codeset)]) @@ -386,7 +411,7 @@ if test $ac_cv_func_finite = no; then AC_CHECK_LIB(m, finite, [AC_DEFINE(HAVE_FINITE, 1, - [Define if the finite() function is available] + [Define if the finite function is available] ) LIBS="$LIBS -lm"]) fi @@ -396,12 +421,17 @@ dnl iswprint/iswspace are sometimes in wctype.h AC_CHECK_HEADERS(wctype.h) -AC_CHECK_DECL(signgam, - [AC_DEFINE(HAVE_SIGNGAM, 1, - [Define if signgam component of gammal is available])], - [], [ - #include "math.h" - ]) +dnl We supply our own lgamma_r (using lgamma) when missing. +dnl We supply our own expm1 (using log1p) when missing. +AC_CHECK_FUNCS(lgamma) +if test $ac_cv_func_lgamma = no; then + AC_CHECK_LIB(m, lgamma, + [AC_DEFINE(HAVE_LGAMMA, 1, + [Define if the lgamma function is available] + ) + LIBS="$LIBS -lm"]) +fi +AC_CHECK_FUNCS(lgamma_r expm1 log1p) AC_C_LONG_DOUBLE float_msg=double @@ -415,7 +445,7 @@ need_sunmath=no for ldfunc in fabsl sqrtl expl expm1l logl log10l log1pl ceill floorl powl hypotl \ - sinl cosl tanl asinl acosl atanl atan2l fmodl lgammal \ + sinl cosl tanl asinl acosl atanl atan2l fmodl lgammal lgammal_r \ sinhl coshl tanhl asinhl acoshl atanhl \ isnanl finitel; do AC_CHECK_FUNC($ldfunc, @@ -842,6 +872,7 @@ plugins/psiconv/Makefile plugins/mps/Makefile plugins/sample_datasource/Makefile +plugins/paradox/Makefile po/Makefile.in schemas/Makefile templates/Makefile Index: doc/C/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/doc/C/ChangeLog,v retrieving revision 1.214 retrieving revision 1.215 diff -u -w -r1.214 -r1.215 --- doc/C/ChangeLog 19 Jul 2004 12:40:12 -0000 1.214 +++ doc/C/ChangeLog 19 Jul 2004 20:32:07 -0000 1.215 @@ -1,3 +1,9 @@ +2004-07-19 J.H.M. Dassen (Ray) + + * gnumeric-C.omf: Added DOCTYPE so the file validates with "xmllint + --noout --valid" (as per "Writing ScrollKeeper OMF Files V1.1"); + updated version/date information. + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: doc/C/about-authors.xml =================================================================== RCS file: /cvs/gnome/gnumeric/doc/C/about-authors.xml,v retrieving revision 1.2 retrieving revision 1.3 diff -u -w -r1.2 -r1.3 --- doc/C/about-authors.xml 16 Jan 2004 06:48:21 -0000 1.2 +++ doc/C/about-authors.xml 20 Jul 2004 00:37:12 -0000 1.3 @@ -47,6 +47,28 @@ Kenneth Christiansen i18n, misc stuff + + + Jean Brefort + Core charting engine + + + Emmanuel Pacaud + Many plot types for charting engine. + + + Harald Ashburner + Options pricers + + + Michael Devine + Radar/Polar plots + + + Uwe Steinmann + Paradox importer + + Zbigniew Chyla Plugin system, i18n, python Index: doc/C/gnumeric-C.omf =================================================================== RCS file: /cvs/gnome/gnumeric/doc/C/gnumeric-C.omf,v retrieving revision 1.6 retrieving revision 1.7 diff -u -w -r1.6 -r1.7 --- doc/C/gnumeric-C.omf 8 Jul 2003 02:50:04 -0000 1.6 +++ doc/C/gnumeric-C.omf 19 Jul 2004 20:32:07 -0000 1.7 @@ -1,4 +1,5 @@ + @@ -36,15 +37,15 @@ - The Gnumeric Manual, version 1.2. + The Gnumeric Manual, version 1.3. - 2003-05-01 + 2004-07-05 - + Index: plugins/Makefile.am =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/Makefile.am,v retrieving revision 1.80 retrieving revision 1.81 diff -u -w -r1.80 -r1.81 --- plugins/Makefile.am 22 May 2004 02:41:39 -0000 1.80 +++ plugins/Makefile.am 19 Jul 2004 13:18:49 -0000 1.81 @@ -52,8 +52,14 @@ PSICONV_DIR= endif +if WITH_PARADOX +PARADOX_DIR=paradox +else +PARADOX_DIR= +endif + SUBDIRS_FILE_FORMATS = excel lotus-123 oleo sc sylk xbase html dif qpro \ - plan-perfect xml_sax applix mps openoffice $(PSICONV_DIR) + plan-perfect xml_sax applix mps openoffice $(PSICONV_DIR) $(PARADOX_DIR) SUBDIRS_FUNCTIONS = numtheory fn-database fn-date fn-eng fn-financial fn-info \ fn-logical fn-complex fn-lookup fn-math fn-stat fn-string fn-random \ Index: plugins/applix/applix-read.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/applix/applix-read.c,v retrieving revision 1.90 retrieving revision 1.91 diff -u -w -r1.90 -r1.91 --- plugins/applix/applix-read.c 3 Jul 2004 05:00:00 -0000 1.90 +++ plugins/applix/applix-read.c 5 Aug 2004 18:58:46 -0000 1.91 @@ -1465,7 +1465,7 @@ { "IPAYMT", "IPMT" }, { "PAYMT", "PMT" }, { "PPAYMT", "PPMT" }, - { 0, 0 } + { NULL, NULL } }; static GnmExpr const * Index: plugins/corba/corba-application.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/corba/corba-application.c,v retrieving revision 1.13 retrieving revision 1.14 diff -u -w -r1.13 -r1.14 --- plugins/corba/corba-application.c 7 Jan 2004 04:54:13 -0000 1.13 +++ plugins/corba/corba-application.c 23 Jul 2004 03:59:32 -0000 1.14 @@ -66,7 +66,7 @@ } else { GnmCmdContext *cc = cmd_context_stderr_new (); IOContext *io_context = gnumeric_io_context_new (cc); - wbv = wb_view_new_from_file (file_name, NULL, io_context, NULL); + wbv = wb_view_new_from_uri (file_name, NULL, io_context, NULL); g_object_unref (G_OBJECT (io_context)); g_object_unref (G_OBJECT (cc)); } Index: plugins/derivatives/options.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/derivatives/options.c,v retrieving revision 1.42 retrieving revision 1.44 diff -u -w -r1.42 -r1.44 --- plugins/derivatives/options.c 29 Dec 2003 03:13:17 -0000 1.42 +++ plugins/derivatives/options.c 27 Jul 2004 21:09:42 -0000 1.44 @@ -48,12 +48,18 @@ OS_Error } OptionSide; -static gnm_float opt_BAW_call (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); -static gnm_float opt_BAW_put (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); +typedef enum{ + OT_Euro, + OT_Amer, + OT_Error +} OptionType; + +static gnm_float opt_baw_call (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); +static gnm_float opt_baw_put (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); static gnm_float NRA_c (gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); static gnm_float NRA_p (gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); -static gnm_float opt_bjerStens1_c (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); -/* static gnm_float opt_bjerStens1_p (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); */ +static gnm_float opt_bjer_stens1_c (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); +/* static gnm_float opt_bjer_stens1_p (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v); */ static gnm_float phi (gnm_float s, gnm_float t, gnm_float gamma, gnm_float H, gnm_float I, gnm_float r, gnm_float b, gnm_float v); static gnm_float CriticalValueOptionsOnOptions (OptionSide side, gnm_float x1, gnm_float x2, gnm_float t, gnm_float r, gnm_float b, gnm_float v); @@ -72,11 +78,19 @@ return OS_Error; } - +static OptionType +option_type (const char *s) +{ + if (s[0] == 'a' || s[0] == 'A') + return OT_Amer; + else if (s[0] == 'e' || s[0] == 'E') + return OT_Euro; + else + return OT_Error; +} /* The normal distribution function */ -#define calc_N(x) pnorm (x, 0, 1, TRUE, FALSE) -#define n_d(x) dnorm (x, 0, 1, FALSE) - +#define cum_norm_dist(x) pnorm (x, 0.0, 1.0, TRUE, FALSE) +#define n_d(x) dnorm (x, 0.0, 1.0, FALSE) static int Sgn (gnm_float a) { @@ -95,27 +109,29 @@ static const gnm_float x[] = {0.24840615, 0.39233107, 0.21141819, 0.03324666, 0.00082485334}; static const gnm_float y[] = {0.10024215, 0.48281397, 1.0609498, 1.7797294, 2.6697604}; - a1 = a / sqrtgnum (2 * (1 - (rho * rho))); - b1 = b / sqrtgnum (2 * (1 - (rho * rho))); + a1 = a / sqrtgnum (2.0 * (1 - (rho * rho))); + b1 = b / sqrtgnum (2.0 * (1 - (rho * rho))); if (a <= 0 && b <= 0 && rho <= 0) { - for (i = 0;i!=5;++i) - for (j = 0; j!=5; ++j) - sum = sum + x[i] * x[j] * expgnum (a1 * (2 * y[i] - a1) + b1 * (2 * -y[j] - b1) + 2 * rho * (y[i] - a1) * (y[j] - b1)); - return (sqrtgnum (1 - (rho * rho)) / M_PIgnum * sum); + for (i = 0;i!=5;++i){ + for (j = 0; j!=5; ++j){ + sum = sum + x[i] * x[j] * expgnum (a1 * (2.0 * y[i] - a1) + b1 * (2.0 * +y[j] - b1) + 2.0 * rho * (y[i] - a1) * (y[j] - b1)); + } + } + return (sqrtgnum (1.0 - (rho * rho)) / M_PIgnum * sum); } else if (a <= 0 && b >= 0 && rho >= 0) - return (calc_N (a) - cum_biv_norm_dist1 (a,-b,-rho)); + return (cum_norm_dist (a) - cum_biv_norm_dist1 (a,-b,-rho)); else if (a >= 0 && b <= 0 && rho >= 0) - return (calc_N (b) - cum_biv_norm_dist1 (-a,b,-rho)); + return (cum_norm_dist (b) - cum_biv_norm_dist1 (-a,b,-rho)); else if (a >= 0 && b >= 0 && rho <= 0) - return (calc_N (a) + calc_N (b) - 1 + cum_biv_norm_dist1 (-a,-b,rho)); + return (cum_norm_dist (a) + cum_norm_dist (b) - 1.0 + cum_biv_norm_dist1 (-a,-b,rho)); else if ((a * b * rho) > 0) { - rho1 = (rho * a - b) * Sgn (a) / sqrtgnum ((a * a) - 2 * rho * a + rho1 = (rho * a - b) * Sgn (a) / sqrtgnum ((a * a) - 2.0 * rho * a * b + (b * b)); - rho2 = (rho * b - a) * Sgn (b) / sqrtgnum ((a * a) - 2 * rho * a + rho2 = (rho * b - a) * Sgn (b) / sqrtgnum ((a * a) - 2.0 * rho * a * b + (b * b)); - delta = (1 - Sgn (a) * Sgn (b)) / 4; + delta = (1.0 - Sgn (a) * Sgn (b)) / 4.0; return (cum_biv_norm_dist1 (a,0.0,rho1) + cum_biv_norm_dist1 (b,0.0,rho2) - delta); } @@ -166,16 +182,16 @@ gnm_float d1; gnm_float d2; - d1 = (loggnum (s / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); + d1 = (loggnum (s / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); d2 = d1 - v * sqrtgnum (t); switch (side) { case OS_Call: - return (s * expgnum ((b - r) * t) * calc_N (d1) - - x * expgnum (-r * t) * calc_N (d2)); + return (s * expgnum ((b - r) * t) * cum_norm_dist (d1) - + x * expgnum (-r * t) * cum_norm_dist (d2)); case OS_Put: - return (x * expgnum (-r * t) * calc_N (-d2) - - s * expgnum ((b - r) * t) * calc_N (-d1)); + return (x * expgnum (-r * t) * cum_norm_dist (-d2) - + s * expgnum ((b - r) * t) * cum_norm_dist (-d1)); default: return -1; } @@ -230,15 +246,15 @@ gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b) { gnm_float d1 = - (loggnum (s / x) + (b + (v * v) / 2) * t) / + (loggnum (s / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); switch (side) { case OS_Call: - return expgnum ((b - r) * t) * calc_N (d1); + return expgnum ((b - r) * t) * cum_norm_dist (d1); case OS_Put: - return expgnum ((b - r) * t) * (calc_N (d1) - 1); + return expgnum ((b - r) * t) * (cum_norm_dist (d1) - 1.0); default: g_assert_not_reached (); @@ -256,7 +272,7 @@ gnm_float t = value_get_as_float (argv[3]); gnm_float r = value_get_as_float (argv[4]); gnm_float v = value_get_as_float (argv[5]); - gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0; + gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0.0; gnm_float gfresult = opt_bs_delta1 (call_put, s, x, t, r, v, b); if (gfresult == -123) @@ -299,7 +315,7 @@ { gnm_float d1; - d1 = (loggnum (s / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); + d1 = (loggnum (s / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); return (expgnum ((b - r) * t) * n_d (d1) / (s * v * sqrtgnum (t))); } @@ -312,7 +328,7 @@ gnm_float t = value_get_as_float (argv[2]); gnm_float r = value_get_as_float (argv[3]); gnm_float v = value_get_as_float (argv[4]); - gnm_float b = argv[5] ? value_get_as_float (argv[5]) : 0; + gnm_float b = argv[5] ? value_get_as_float (argv[5]) : 0.0; gnm_float gfresult = opt_bs_gamma1 (s,x,t,r,v,b); return value_new_float (gfresult); } @@ -352,16 +368,16 @@ opt_bs_theta1 (OptionSide side, gnm_float s,gnm_float x,gnm_float t,gnm_float r,gnm_float v,gnm_float b) { - gnm_float d1 = (loggnum (s / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); + gnm_float d1 = (loggnum (s / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); gnm_float d2 = d1 - v * sqrtgnum (t); switch (side) { case OS_Call: - return -s * expgnum ((b - r) * t) * n_d (d1) * v / (2 * sqrtgnum (t)) - - (b - r) * s * expgnum ((b - r) * t) * calc_N (d1) - r * x * expgnum (-r * t) * calc_N (d2); + return -s * expgnum ((b - r) * t) * n_d (d1) * v / (2.0 * sqrtgnum (t)) - + (b - r) * s * expgnum ((b - r) * t) * cum_norm_dist (d1) - r * x * expgnum (-r * t) * cum_norm_dist (d2); case OS_Put: - return -s * expgnum ((b - r) * t) * n_d (d1) * v / (2 * sqrtgnum (t)) + - (b - r) * s * expgnum ((b - r) * t) * calc_N (-d1) + r * x * expgnum (-r * t) * calc_N (-d2); + return -s * expgnum ((b - r) * t) * n_d (d1) * v / (2.0 * sqrtgnum (t)) + + (b - r) * s * expgnum ((b - r) * t) * cum_norm_dist (-d1) + r * x * expgnum (-r * t) * cum_norm_dist (-d2); default: return -123; } @@ -376,7 +392,7 @@ gnm_float t = value_get_as_float (argv[3]); gnm_float r = value_get_as_float (argv[4]); gnm_float v = value_get_as_float (argv[5]); - gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0; + gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0.0; gnm_float gfresult = opt_bs_theta1 (call_put, s, x, t, r, v, b); if (gfresult == -123) @@ -420,7 +436,7 @@ { gnm_float d1; - d1 = (loggnum (s / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); + d1 = (loggnum (s / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); return (s * expgnum ((b - r) * t) * n_d (d1) * sqrtgnum (t)); } @@ -432,7 +448,7 @@ gnm_float t = value_get_as_float (argv[2]); gnm_float r = value_get_as_float (argv[3]); gnm_float v = value_get_as_float (argv[4]); - gnm_float b = argv[5] ? value_get_as_float (argv[5]) : 0; + gnm_float b = argv[5] ? value_get_as_float (argv[5]) : 0.0; return value_new_float (opt_bs_vega1 (s,x,t,r,v,b)); } @@ -470,18 +486,18 @@ static gnm_float opt_bs_rho1 (OptionSide side, gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b) { - gnm_float d1 = (loggnum (s / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); + gnm_float d1 = (loggnum (s / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); gnm_float d2 = d1 - v * sqrtgnum (t); switch (side) { case OS_Call: if (b != 0) - return t * x * expgnum (-r * t) * calc_N (d2); + return t * x * expgnum (-r * t) * cum_norm_dist (d2); else return -t * opt_bs1 (side, s, x, t, r, v, b); case OS_Put: if (b != 0) - return -t * x * expgnum (-r * t) * calc_N (-d2); + return -t * x * expgnum (-r * t) * cum_norm_dist (-d2); else return -t * opt_bs1 (side, s, x, t, r, v, b); @@ -500,7 +516,7 @@ gnm_float t = value_get_as_float (argv[3]); gnm_float r = value_get_as_float (argv[4]); gnm_float v = value_get_as_float (argv[5]); - gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0; + gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0.0; gnm_float gfresult = opt_bs_rho1 (call_put, s, x, t, r, v, b); if (gfresult == -123) return value_new_error_NUM (ei->pos); @@ -541,14 +557,14 @@ static gnm_float opt_bs_carrycost1 (OptionSide side, gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b) { - gnm_float d1 = (loggnum (s / x) + (b + (v * v) / 2) * t) / + gnm_float d1 = (loggnum (s / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); switch (side) { case OS_Call: - return t * s * expgnum ((b - r) * t) * calc_N (d1); + return t * s * expgnum ((b - r) * t) * cum_norm_dist (d1); case OS_Put: - return -t * s * expgnum ((b - r) * t) * calc_N (-d1); + return -t * s * expgnum ((b - r) * t) * cum_norm_dist (-d1); default: return -123; /*should never get to here*/ } @@ -563,7 +579,7 @@ gnm_float t = value_get_as_float (argv[3]); gnm_float r = value_get_as_float (argv[4]); gnm_float v = value_get_as_float (argv[5]); - gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0; + gnm_float b = argv[6] ? value_get_as_float (argv[6]) : 0.0; gnm_float gfresult = opt_bs_carrycost1 (call_put, s,x,t,r,v,b); if (gfresult == -123) return value_new_error_NUM (ei->pos); @@ -610,13 +626,13 @@ gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float rf, gnm_float v) { - gnm_float d1 = (loggnum (s / x) + (r - rf + (v * v) / 2) * t) / (v * sqrtgnum (t)); + gnm_float d1 = (loggnum (s / x) + (r - rf + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); gnm_float d2 = d1 - v * sqrtgnum (t); switch (side) { case OS_Call: - return s * expgnum (-rf * t) * calc_N (d1) - x * expgnum (-r * t) * calc_N (d2); + return s * expgnum (-rf * t) * cum_norm_dist (d1) - x * expgnum (-r * t) * cum_norm_dist (d2); case OS_Put: - return x * expgnum (-r * t) * calc_N (-d2) - s * expgnum (-rf * t) * calc_N (-d1); + return x * expgnum (-r * t) * cum_norm_dist (-d2) - s * expgnum (-rf * t) * cum_norm_dist (-d1); default: return -123; /*should never get to here*/ } @@ -673,17 +689,17 @@ /* French (1984) adjusted Black and scholes model for trading day volatility */ static gnm_float -opt_french1 (OptionSide side, gnm_float s, gnm_float x, gnm_float t, gnm_float t1, +opt_french1 (OptionSide side, gnm_float s, gnm_float x, gnm_float trading_t, gnm_float calander_t, gnm_float r, gnm_float v, gnm_float b) { - gnm_float d1 = (loggnum (s / x) + b * t + (v * v) / 2 * t1) / (v * sqrtgnum (t1)); - gnm_float d2 = d1 - v * sqrtgnum (t1); + gnm_float d1 = (loggnum (s / x) + b * calander_t + ((v * v) / 2.0) * trading_t) / (v * sqrtgnum (trading_t)); + gnm_float d2 = d1 - v * sqrtgnum (trading_t); switch (side) { case OS_Call: - return s * expgnum ((b - r) * t) * calc_N (d1) - x * expgnum (-r * t) * calc_N (d2); + return s * expgnum ((b - r) * calander_t) * cum_norm_dist (d1) - x * expgnum (-r * calander_t) * cum_norm_dist (d2); case OS_Put: - return x * expgnum (-r * t) * calc_N (-d2) - s * expgnum ((b - r) * t) * calc_N (-d1); + return x * expgnum (-r * calander_t) * cum_norm_dist (-d2) - s * expgnum ((b - r) * calander_t) * cum_norm_dist (-d1); default: return -123; } @@ -703,7 +719,7 @@ gnm_float b = value_get_as_float (argv[7]); gnm_float gfresult; - gfresult = opt_french1 (call_put, s, x, t, t1, r, b, v); + gfresult = opt_french1 (call_put, s, x, t, t1, r, v, b); if (gfresult == -123) return value_new_error_NUM (ei->pos); else @@ -726,11 +742,9 @@ "@t2 is the number of trading days to exercise divided by trading days in the year.\n" "@rate is the risk-free interest rate.\n" "@cost_of_carry is the leakage in value of the underlying asset, " - "for common stocks, this would be the dividend yield " "to the exercise date, in percent.\n" + "For common stocks, this would be the dividend yield." "\n" - "* The returned value will be expressed as the rate of change of " - "option value, per 100% volatility.\n" "\n" "@EXAMPLES=\n" "\n" @@ -748,12 +762,12 @@ delta = sqrtgnum (gamma * (v * v) / lambda); Z = sqrtgnum ((v * v) - lambda * (delta * delta)); - sum = 0; + sum = 0.0; for(i = 0; i != 11; ++i) { vi = sqrtgnum ((Z * Z) + (delta * delta) * (i / t)); sum = sum + expgnum (-lambda * t) * powgnum (lambda * t, i) / fact(i) * - opt_bs1 (side, s, x, t, r, r, vi); + opt_bs1 (side, s, x, t, r, vi, r); } return sum; } @@ -786,7 +800,7 @@ "\n" "@call_put_flag is c or p to indicate whether the option is a call or a put \n" - "@spot is the spot price of the underlying\n" + "@spot is the spot price of the underlying asset\n" "@strike is the strike price of the option\n" "@time is the time to maturity of the option expressed in years\n" "@rate is the annualised rate of interest\n" @@ -810,32 +824,32 @@ gnm_float vz, vxz; gnm_float d1, d2; - vz = (v_s * v_s) * t1 + 2 * v_s * (v_f * rho_sf * 1 / kappa_f * (t1 - 1 / kappa_f * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - 1)) - - v_e * rho_se * 1 / kappa_e * (t1 - 1 / kappa_e * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - 1))) - + (v_e * v_e) * 1 / (kappa_e * kappa_e) * (t1 + 1 / (2 * kappa_e) * expgnum (-2 * kappa_e * t2) * (expgnum (2 * kappa_e * t1) - 1) - - 2 * 1 / kappa_e * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - 1)) - + (v_f * v_f) * 1 / (kappa_f * kappa_f) * (t1 + 1 / (2 * kappa_f) * expgnum (-2 * kappa_f * t2) * (expgnum (2 * kappa_f * t1) - 1) - - 2 * 1 / kappa_f * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - 1)) - - 2 * v_e * v_f * rho_ef * 1 / kappa_e * 1 / kappa_f * (t1 - 1 / kappa_e * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - 1) - - 1 / kappa_f * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - 1) - + 1 / (kappa_e + kappa_f) * expgnum (-(kappa_e + kappa_f) * t2) * (expgnum ((kappa_e + kappa_f) * t1) - 1)); - - vxz = v_f * 1 / kappa_f * (v_s * rho_sf * (t1 - 1 / kappa_f * (1 - expgnum (-kappa_f * t1))) - + v_f * 1 / kappa_f * (t1 - 1 / kappa_f * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - 1) - 1 / kappa_f * (1 - expgnum (-kappa_f * t1)) - + 1 / (2 * kappa_f) * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - expgnum (-kappa_f * t1))) - - v_e * rho_ef * 1 / kappa_e * (t1 - 1 / kappa_e * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - 1) - 1 / kappa_f * (1 - expgnum (-kappa_f * t1)) - + 1 / (kappa_e + kappa_f) * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - expgnum (-kappa_f * t1)))); + vz = (v_s * v_s) * t1 + 2.0 * v_s * (v_f * rho_sf * 1.0/ kappa_f * (t1 - 1.0/ kappa_f * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - 1.0)) + - v_e * rho_se * 1.0/ kappa_e * (t1 - 1.0/ kappa_e * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - 1.0))) + + (v_e * v_e) * 1.0/ (kappa_e * kappa_e) * (t1 + 1.0/ (2.0 * kappa_e) * expgnum (-2 * kappa_e * t2) * (expgnum (2.0 * kappa_e * t1) - 1.0) + - 2.0 * 1.0/ kappa_e * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - 1.0)) + + (v_f * v_f) * 1.0/ (kappa_f * kappa_f) * (t1 + 1.0/ (2.0 * kappa_f) * expgnum (-2.0 * kappa_f * t2) * (expgnum (2.0 * kappa_f * t1) - 1.0) + - 2.0 * 1.0/ kappa_f * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - 1.0)) + - 2.0 * v_e * v_f * rho_ef * 1.0/ kappa_e * 1.0/ kappa_f * (t1 - 1.0/ kappa_e * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - 1.0) + - 1.0/ kappa_f * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - 1.0) + + 1.0/ (kappa_e + kappa_f) * expgnum (-(kappa_e + kappa_f) * t2) * (expgnum ((kappa_e + kappa_f) * t1) - 1.0)); + + vxz = v_f * 1.0/ kappa_f * (v_s * rho_sf * (t1 - 1.0/ kappa_f * (1.0 - expgnum (-kappa_f * t1))) + + v_f * 1.0/ kappa_f * (t1 - 1.0/ kappa_f * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - 1.0) - 1.0/ kappa_f * (1 - expgnum (-kappa_f * t1)) + + 1.0/ (2.0 * kappa_f) * expgnum (-kappa_f * t2) * (expgnum (kappa_f * t1) - expgnum (-kappa_f * t1))) + - v_e * rho_ef * 1.0/ kappa_e * (t1 - 1.0/ kappa_e * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - 1.0) - 1.0/ kappa_f * (1.0 - expgnum (-kappa_f * t1)) + + 1.0/ (kappa_e + kappa_f) * expgnum (-kappa_e * t2) * (expgnum (kappa_e * t1) - expgnum (-kappa_f * t1)))); vz = sqrtgnum (vz); - d1 = (loggnum (f_t / x) - vxz + (vz * vz) / 2) / vz; - d2 = (loggnum (f_t / x) - vxz - (vz * vz) / 2) / vz; + d1 = (loggnum (f_t / x) - vxz + (vz * vz) / 2.0) / vz; + d2 = (loggnum (f_t / x) - vxz - (vz * vz) / 2.0) / vz; switch (side) { case OS_Call: - return p_t * (f_t * expgnum (-vxz) * calc_N (d1) - x * calc_N (d2)); + return p_t * (f_t * expgnum (-vxz) * cum_norm_dist (d1) - x * cum_norm_dist (d2)); case OS_Put: - return p_t * (x * calc_N (-d2) - f_t * expgnum (-vxz) * calc_N (-d1)); + return p_t * (x * cum_norm_dist (-d2) - f_t * expgnum (-vxz) * cum_norm_dist (-d1)); default: return -123; } @@ -921,23 +935,23 @@ infinity = 100000000; epsilon = 0.00001; sx = s - d * expgnum (-r * t1); - if (d <= (x * (1 - expgnum (-r * (t2 - t1))))) + if (d <= (x * (1.0 - expgnum (-r * (t2 - t1))))) /* Not optimal to exercise */ - return opt_bs1 (OS_Call, sx, x, t2, r, v,0); + return opt_bs1 (OS_Call, sx, x, t2, r, v,0.0); - ci = opt_bs1 (OS_Call, s, x, t2 - t1, r, v,0); + ci = opt_bs1 (OS_Call, s, x, t2 - t1, r, v,0.0); HighS = s; - while ((ci - HighS - d + x) > 0 && HighS < infinity) - { - HighS *= 2; - ci = opt_bs1 (OS_Call, HighS, x, t2 - t1, r, v,0); + while ((ci - HighS - d + x) > 0.0 && HighS < infinity){ + + HighS *= 2.0; + ci = opt_bs1 (OS_Call, HighS, x, t2 - t1, r, v,0.0); } if (HighS > infinity) - return opt_bs1 (OS_Call, sx, x, t2, r, v,0); + return opt_bs1 (OS_Call, sx, x, t2, r, v,0.0); - LowS = 0; + LowS = 0.0; i = HighS * 0.5; - ci = opt_bs1 (OS_Call, i, x, t2 - t1, r, v,0); + ci = opt_bs1 (OS_Call, i, x, t2 - t1, r, v,0.0); /* search algorithm to find the critical stock price i */ while (gnumabs(ci - i - d + x) > epsilon && HighS - LowS > epsilon) { @@ -945,18 +959,18 @@ HighS = i; else LowS = i; - i = (HighS + LowS) / 2; - ci = opt_bs1 (OS_Call, i, x, (t2 - t1), r, v,0); + i = (HighS + LowS) / 2.0; + ci = opt_bs1 (OS_Call, i, x, (t2 - t1), r, v,0.0); } - a1 = (loggnum (sx / x) + (r + (v * v) / 2) * t2) / (v * sqrtgnum (t2)); + a1 = (loggnum (sx / x) + (r + (v * v) / 2.0) * t2) / (v * sqrtgnum (t2)); a2 = a1 - v * sqrtgnum (t2); - b1 = (loggnum (sx / i) + (r + (v * v) / 2) * t1) / (v * sqrtgnum (t1)); + b1 = (loggnum (sx / i) + (r + (v * v) / 2.0) * t1) / (v * sqrtgnum (t1)); b2 = b1 - v * sqrtgnum (t1); - gfresult = sx * calc_N (b1) + sx * cum_biv_norm_dist1 (a1, -b1, -sqrtgnum (t1 / t2)) + gfresult = sx * cum_norm_dist (b1) + sx * cum_biv_norm_dist1 (a1, -b1, -sqrtgnum (t1 / t2)) - x * expgnum (-r * t2) * cum_biv_norm_dist1 (a2, -b2, -sqrtgnum (t1 / t2)) - (x - d) - * expgnum (-r * t1) * calc_N (b2); + * expgnum (-r * t1) * cum_norm_dist (b2); return gfresult; } @@ -971,7 +985,7 @@ gnm_float r = value_get_as_float (argv[4]); gnm_float d = value_get_as_float (argv[5]); gnm_float v = value_get_as_float (argv[6]); - gnm_float gfresult = 0; + gnm_float gfresult = 0.0; gfresult = opt_rgw1 (s, x, t1, t2, r, d, v); @@ -1004,23 +1018,23 @@ /* the Barone-Adesi and Whaley (1987) American approximation */ static GnmValue * -opt_BAW_amer (FunctionEvalInfo *ei, GnmValue *argv[]) +opt_baw_amer (FunctionEvalInfo *ei, GnmValue *argv[]) { OptionSide call_put = option_side (value_peek_string (argv[0])); gnm_float s = value_get_as_float (argv[1]); gnm_float x = value_get_as_float (argv[2]); gnm_float t = value_get_as_float (argv[3]); gnm_float r = value_get_as_float (argv[4]); - gnm_float b = value_get_as_float (argv[5]); - gnm_float v = value_get_as_float (argv[6]); + gnm_float v = value_get_as_float (argv[5]); + gnm_float b = value_get_as_float (argv[6]); gnm_float gfresult; switch (call_put) { case OS_Call: - gfresult = opt_BAW_call (s, x, t, r, b, v); + gfresult = opt_baw_call (s, x, t, r, v, b); break; case OS_Put: - gfresult = opt_BAW_put (s, x, t, r, b, v); + gfresult = opt_baw_put (s, x, t, r, v, b); break; default: return value_new_error_NUM (ei->pos); @@ -1032,7 +1046,7 @@ return value_new_float (gfresult); } -static char const *help_opt_BAW_amer = { +static char const *help_opt_baw_amer = { /* xgettext:no-c-format */ N_("@FUNCTION=OPT_BAW_AMER\n" @@ -1049,7 +1063,7 @@ "@rate is the risk annualised free rate of interest\n" "@cost_of_carry is the leakage in value of the underlying asset, " "for common stocks, this would be the dividend yield\n" - "@volatility is the annualised volatility in price of the underlying\n" + "@volatility is the annualised volatility in price of the underlying asset\n" "\n" "@EXAMPLES=\n" "\n" @@ -1058,7 +1072,7 @@ /* American call */ static gnm_float -opt_BAW_call (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v) +opt_baw_call (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b) { gnm_float sk, n, k; gnm_float d1, q2, a2; @@ -1067,12 +1081,12 @@ gfresult = opt_bs1 (OS_Call, s, x, t, r, v,b); else { - sk = NRA_c (x, t, r, b, v); - n = 2 * b / (v * v); - k = 2 * r / ((v * v) * (1 - expgnum (-r * t))); - d1 = (loggnum (sk / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); - q2 = (-(n - 1) + sqrtgnum ((n - 1) * (n - 1) + 4 * k)) / 2; - a2 = (sk / q2) * (1 - expgnum ((b - r) * t) * calc_N (d1)); + sk = NRA_c (x, t, r, v, b); + n = 2.0 * b / (v * v); + k = 2.0 * r / ((v * v) * (1.0 - expgnum (-r * t))); + d1 = (loggnum (sk / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); + q2 = (-(n - 1.0) + sqrtgnum ((n - 1.0) * (n - 1.0) + 4.0 * k)) / 2.0; + a2 = (sk / q2) * (1.0 - expgnum ((b - r) * t) * cum_norm_dist (d1)); if (s < sk) gfresult = opt_bs1 (OS_Call, s, x, t, r, v,b) + a2 * powgnum (s / sk, q2); else @@ -1088,7 +1102,7 @@ /* Newton Raphson algorithm to solve for the critical commodity price for a Call */ static gnm_float -NRA_c (gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v) +NRA_c (gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b) { gnm_float n, m; gnm_float su, si; @@ -1098,54 +1112,54 @@ gnm_float bi, e; /* Calculation of seed value, si */ - n = 2 * b / (v * v); - m = 2 * r / (v * v); - q2u = (-(n - 1) + sqrtgnum (((n - 1) * (n - 1)) + 4 * m)) / 2; - su = x / (1 - 1 / q2u); - h2 = -(b * t + 2 * v * sqrtgnum (t)) * x / (su - x); - si = x + (su - x) * (1 - expgnum (h2)); - - k = 2 * r / ((v * v) * (1 - expgnum (-r * t))); - d1 = (loggnum (si / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); - q2 = (-(n - 1) + sqrtgnum (((n - 1) * (n - 1)) + 4 * k)) / 2; + n = 2.0 * b / (v * v); + m = 2.0 * r / (v * v); + q2u = (-(n - 1.0) + sqrtgnum (((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0; + su = x / (1.0 - 1.0/ q2u); + h2 = -(b * t + 2.0 * v * sqrtgnum (t)) * x / (su - x); + si = x + (su - x) * (1.0 - expgnum (h2)); + + k = 2.0 * r / ((v * v) * (1.0 - expgnum (-r * t))); + d1 = (loggnum (si / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); + q2 = (-(n - 1.0) + sqrtgnum (((n - 1.0) * (n - 1.0)) + 4.0 * k)) / 2.0; LHS = si - x; - RHS = opt_bs1 (OS_Call, si, x, t, r, v, b) + (1 - expgnum ((b - r) * t) * calc_N (d1)) * si / q2; - bi = expgnum ((b - r) * t) * calc_N (d1) * (1 - 1 / q2) - + (1 - expgnum ((b - r) * t) * calc_N (d1) / (v * sqrtgnum (t))) / q2; + RHS = opt_bs1 (OS_Call, si, x, t, r, v, b) + (1.0 - expgnum ((b - r) * t) * cum_norm_dist (d1)) * si / q2; + bi = expgnum ((b - r) * t) * cum_norm_dist (d1) * (1.0 - 1.0/ q2) + + (1.0 - expgnum ((b - r) * t) * cum_norm_dist (d1) / (v * sqrtgnum (t))) / q2; e = 0.000001; /* Newton Raphson algorithm for finding critical price si */ while ((gnumabs(LHS - RHS) / x) > e) { - si = (x + RHS - bi * si) / (1 - bi); - d1 = (loggnum (si / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); + si = (x + RHS - bi * si) / (1.0 - bi); + d1 = (loggnum (si / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); LHS = si - x; - RHS = opt_bs1 (OS_Call, si, x, t, r, v, b) + (1 - expgnum ((b - r) * t) * calc_N (d1)) * si / q2; - bi = expgnum ((b - r) * t) * calc_N (d1) * (1 - 1 / q2) - + (1 - expgnum ((b - r) * t) * n_d (d1) / (v * sqrtgnum (t))) / q2; + RHS = opt_bs1 (OS_Call, si, x, t, r, v, b) + (1.0 - expgnum ((b - r) * t) * cum_norm_dist (d1)) * si / q2; + bi = expgnum ((b - r) * t) * cum_norm_dist (d1) * (1.0 - 1.0/ q2) + + (1.0 - expgnum ((b - r) * t) * n_d (d1) / (v * sqrtgnum (t))) / q2; } return si; } static gnm_float -opt_BAW_put (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v) +opt_baw_put (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b) { - gnm_float sk = NRA_p (x, t, r, b, v); - gnm_float n = 2 * b / (v * v); - gnm_float k = 2 * r / ((v * v) * (1 - expgnum (-r * t))); - gnm_float d1 = (loggnum (sk / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); - gnm_float q1 = (-(n - 1) - sqrtgnum (((n - 1) * (n - 1)) + 4 * k)) / 2; - gnm_float a1 = -(sk / q1) * (1 - expgnum ((b - r) * t) * calc_N (-d1)); + gnm_float sk = NRA_p (x, t, r, v, b); + gnm_float n = 2.0 * b / (v * v); + gnm_float k = 2.0 * r / ((v * v) * (1.0 - expgnum (-r * t))); + gnm_float d1 = (loggnum (sk / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); + gnm_float q1 = (-(n - 1.0) - sqrtgnum (((n - 1.0) * (n - 1.0)) + 4.0 * k)) / 2.0; + gnm_float a1 = -(sk / q1) * (1.0 - expgnum ((b - r) * t) * cum_norm_dist (-d1)); if (s > sk) - return opt_bs1 (OS_Put, s, x, t, r, v, b) + a1 * powgnum (a1 / sk, q1); + return opt_bs1 (OS_Put, s, x, t, r, v, b) + a1 * powgnum (s/ sk, q1); else return x - s; } /* Newton Raphson algorithm to solve for the critical commodity price for a Put*/ static gnm_float -NRA_p (gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v) +NRA_p (gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b) { gnm_float n, m; @@ -1156,51 +1170,51 @@ gnm_float bi, e; /* Calculation of seed value, si */ - n = 2 * b / (v * v); - m = 2 * r / (v * v); - q1u = (-(n - 1) - sqrtgnum (((n - 1) * (n - 1)) + 4 * m)) / 2; - su = x / (1 - 1 / q1u); - h1 = (b * t - 2 * v * sqrtgnum (t)) * x / (x - su); + n = 2.0 * b / (v * v); + m = 2.0 * r / (v * v); + q1u = (-(n - 1.0) - sqrtgnum (((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0; + su = x / (1.0 - 1.0/ q1u); + h1 = (b * t - 2.0 * v * sqrtgnum (t)) * x / (x - su); si = su + (x - su) * expgnum (h1); - k = 2 * r / ((v * v) * (1 - expgnum (-r * t))); - d1 = (loggnum (si / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); - q1 = (-(n - 1) - sqrtgnum (((n - 1) * (n - 1)) + 4 * k)) / 2; + k = 2.0 * r / ((v * v) * (1.0 - expgnum (-r * t))); + d1 = (loggnum (si / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); + q1 = (-(n - 1.0) - sqrtgnum (((n - 1.0) * (n - 1.0)) + 4.0 * k)) / 2.0; LHS = x - si; - RHS = opt_bs1 (OS_Put, si, x, t, r, v, b) - (1 - expgnum ((b - r) * t) * calc_N (-d1)) * si / q1; - bi = -expgnum ((b - r) * t) * calc_N (-d1) * (1 - 1 / q1) - - (1 + expgnum ((b - r) * t) * n_d (-d1) / (v * sqrtgnum (t))) / q1; + RHS = opt_bs1 (OS_Put, si, x, t, r, v, b) - (1.0 - expgnum ((b - r) * t) * cum_norm_dist (-d1)) * si / q1; + bi = -expgnum ((b - r) * t) * cum_norm_dist (-d1) * (1.0 - 1.0/ q1) + - (1.0 + expgnum ((b - r) * t) * n_d (-d1) / (v * sqrtgnum (t))) / q1; e = 0.000001; /* Newton Raphson algorithm for finding critical price si */ - while((gnumabs(LHS - RHS) / x) > e) { - si = (x - RHS + bi * si) / (1 + bi); - d1 = (loggnum (si / x) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); + while(gnumabs(LHS - RHS) / x > e) { + si = (x - RHS + bi * si) / (1.0 + bi); + d1 = (loggnum (si / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); LHS = x - si; - RHS = opt_bs1 (OS_Put, si, x, t, r, v, b) - (1 - expgnum ((b - r) * t) * calc_N (-d1)) * si / q1; - bi = -expgnum ((b - r) * t) * calc_N (-d1) * (1 - 1 / q1) - - (1 + expgnum ((b - r) * t) * calc_N (-d1) / (v * sqrtgnum (t))) / q1; + RHS = opt_bs1 (OS_Put, si, x, t, r, v, b) - (1.0 - expgnum ((b - r) * t) * cum_norm_dist (-d1)) * si / q1; + bi = -expgnum ((b - r) * t) * cum_norm_dist (-d1) * (1.0 - 1.0/ q1) + - (1.0 + expgnum ((b - r) * t) * cum_norm_dist (-d1) / (v * sqrtgnum (t))) / q1; } return si; } /* the Bjerksund and stensland (1993) American approximation */ static gnm_float -opt_bjerStens1 (OptionSide side, gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v) +opt_bjer_stens1 (OptionSide side, gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b) { switch (side) { case OS_Call: - return opt_bjerStens1_c (s, x, t, r, b, v); + return opt_bjer_stens1_c (s, x, t, r, v, b); case OS_Put: /* Use the Bjerksund and stensland put-call transformation */ - return opt_bjerStens1_c (x, s, t, r - b, -b, v); + return opt_bjer_stens1_c (x, s, t, r - b, v, -b); default: return -123; } } static GnmValue * -opt_bjerStens (FunctionEvalInfo *ei, GnmValue *argv[]) +opt_bjer_stens (FunctionEvalInfo *ei, GnmValue *argv[]) { OptionSide call_put = option_side (value_peek_string (argv[0])); gnm_float s = value_get_as_float (argv[1]); @@ -1210,12 +1224,12 @@ gnm_float v = value_get_as_float (argv[5]); gnm_float b = argv[6] ? value_get_as_float (argv[6]):0; gnm_float gfresult = - opt_bjerStens1 (call_put, s, x, t, r, b, v); + opt_bjer_stens1 (call_put, s, x, t, r, v, b); return value_new_float (gfresult); } -static char const *help_opt_bjerStens = { +static char const *help_opt_bjer_stens = { /* xgettext:no-c-format */ N_("@FUNCTION=OPT_BJERSTENS\n" @@ -1229,7 +1243,7 @@ "@strike is the strike price at which the option is struck\n" "@time is the number of days to maturity of the option\n" "@rate is the risk annualised free rate of interest\n" - "@volatility is the annualised volatility in price of the underlying \n" + "@volatility is the annualised volatility in price of the underlying asset\n" "@cost_of_carry is the leakage in value of the underlying asset, " "for common stocks, this would be the dividend yield.\n" "\n" @@ -1239,43 +1253,43 @@ }; static gnm_float -opt_bjerStens1_c (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float b, gnm_float v) +opt_bjer_stens1_c (gnm_float s, gnm_float x, gnm_float t, gnm_float r, gnm_float v, gnm_float b) { if (b >= r) /* Never optimal to exersice before maturity */ return opt_bs1 (OS_Call, s, x, t, r, v, b); else { gnm_float Beta = - (1 / 2 - b / (v * v)) + - sqrtgnum (powgnum (b / (v * v) - 1 / 2, 2) + 2 * r / (v * v)); - gnm_float BInfinity = Beta / (Beta - 1) * x; + (1.0/ 2.0 - b / (v * v)) + + sqrtgnum (powgnum (b / (v * v) - 1.0/ 2.0, 2) + 2.0 * r / (v * v)); + gnm_float BInfinity = Beta / (Beta - 1.0) * x; gnm_float B0 = MAX (x, r / (r - b) * x); - gnm_float ht = -(b * t + 2 * v * sqrtgnum (t)) * B0 / (BInfinity - B0); - gnm_float I = B0 + (BInfinity - B0) * (1 - expgnum (ht)); + gnm_float ht = -(b * t + 2.0 * v * sqrtgnum (t)) * B0 / (BInfinity - B0); + gnm_float I = B0 + (BInfinity - B0) * (1.0 - expgnum (ht)); if (s >= I) return s - x; else { gnm_float alpha = (I - x) * powgnum (I ,-Beta); return alpha * powgnum (s ,Beta) - - alpha * phi (s, t, Beta, I, I, r, b, v) + - phi (s, t, 1, I, I, r, b, v) - - phi (s, t, 1, x, I, r, b, v) - - x * phi (s, t, 0, I, I, r, b, v) + - x * phi (s, t, 0, x, I, r, b, v); + alpha * phi (s, t, Beta, I, I, r, v, b) + + phi (s, t, 1.0, I, I, r, v, b) - + phi (s, t, 1.0, x, I, r, v, b) - + x * phi (s, t, 0.0, I, I, r, v, b) + + x * phi (s, t, 0.0, x, I, r, v, b); } } } static gnm_float -phi (gnm_float s, gnm_float t, gnm_float gamma, gnm_float H, gnm_float I, gnm_float r, gnm_float b, gnm_float v) +phi (gnm_float s, gnm_float t, gnm_float gamma, gnm_float H, gnm_float I, gnm_float r, gnm_float v, gnm_float b) { gnm_float lambda, kappa; gnm_float d; gnm_float gfresult; - lambda = (-r + gamma * b + 0.5 * gamma * (gamma - 1) * (v * v)) * t; + lambda = (-r + gamma * b + 0.5 * gamma * (gamma - 1.0) * (v * v)) * t; d = -(loggnum (s / H) + (b + (gamma - 0.5) * (v * v)) * t) / (v * sqrtgnum (t)); - kappa = 2 * b / (v * v) + (2 * gamma - 1); - gfresult = expgnum (lambda) * powgnum (s, gamma) * (calc_N (d) - powgnum (I / s, kappa) * calc_N (d - 2 * loggnum (I / s) / (v * sqrtgnum (t)))); + kappa = 2.0 * b / (v * v) + (2.0 * gamma - 1.0); + gfresult = expgnum (lambda) * powgnum (s, gamma) * (cum_norm_dist (d) - powgnum (I / s, kappa) * cum_norm_dist (d - 2.0 * loggnum (I / s) / (v * sqrtgnum (t)))); return gfresult; } @@ -1313,7 +1327,7 @@ "@strike is the strike price at which the option is struck \n" "@time is the number of days to maturity of the option \n" "@rate is the risk annualised free rate of interest \n" - "@volatility is the annualised volatility in price of the underlying \n" + "@volatility is the annualised volatility in price of the underlying asset\n" "@cost_of_carry is the leakage in value of the underlying asset, " "for common stocks, this would be the dividend yield\n" "@lambda is the jump rate for executives." @@ -1362,7 +1376,7 @@ "@time1 is the number of days until the option starts \n" "@time is the number of days to maturity of the option \n" "@rate is the risk annualised free rate of interest \n" - "@volatility is the annualised volatility in price of the underlying \n" + "@volatility is the annualised volatility in price of the underlying asset\n" "@cost_of_carry is the leakage in value of the underlying asset, " "for common stocks, this would be the dividend yield" "\n" @@ -1398,11 +1412,11 @@ default: return value_new_float (-123); } - sum = 0; + sum = 0.0; n = t / dt; for (i = 1; i < n; ++i) { - d = (loggnum (s / x) + (b - (v * v) / 2) * i * dt) / (v * sqrtgnum (i * dt)); - sum = sum + calc_N (Z * d) * dt; + d = (loggnum (s / x) + (b - (v * v) / 2.0) * i * dt) / (v * sqrtgnum (i * dt)); + sum = sum + cum_norm_dist (Z * d) * dt; } gfresult = a * expgnum (-r * t) * sum + dt * a * expgnum (-r * t) * m; @@ -1457,11 +1471,11 @@ gnm_float d, y; - d = (loggnum (s / x) + (b + (v * v) / 2) * t2) / (v * sqrtgnum (t2)); - y = (loggnum (s / x) + b * t2 + (v * v) * t1 / 2) / (v * sqrtgnum (t1)); + d = (loggnum (s / x) + (b + (v * v) / 2.0) * t2) / (v * sqrtgnum (t2)); + y = (loggnum (s / x) + b * t2 + (v * v) * t1 / 2.0) / (v * sqrtgnum (t1)); - gfresult = s * expgnum ((b - r) * t2) * calc_N (d) - x * expgnum (-r * t2) * calc_N (d - v * sqrtgnum (t2)) - - s * expgnum ((b - r) * t2) * calc_N (-y) + x * expgnum (-r * t2) * calc_N (-y + v * sqrtgnum (t1)); + gfresult = s * expgnum ((b - r) * t2) * cum_norm_dist (d) - x * expgnum (-r * t2) * cum_norm_dist (d - v * sqrtgnum (t2)) + - s * expgnum ((b - r) * t2) * cum_norm_dist (-y) + x * expgnum (-r * t2) * cum_norm_dist (-y + v * sqrtgnum (t1)); return value_new_float (gfresult); @@ -1513,10 +1527,10 @@ gnm_float rho1, rho2, I; I = opt_crit_val_chooser (s, xc, xp, t, tc, tp, r, b, v); - d1 = (loggnum (s / I) + (b + (v * v) / 2) * t) / (v * sqrtgnum (t)); + d1 = (loggnum (s / I) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum (t)); d2 = d1 - v * sqrtgnum (t); - y1 = (loggnum (s / xc) + (b + (v * v) / 2) * tc) / (v * sqrtgnum (tc)); - y2 = (loggnum (s / xp) + (b + (v * v) / 2) * tp) / (v * sqrtgnum (tp)); + y1 = (loggnum (s / xc) + (b + (v * v) / 2.0) * tc) / (v * sqrtgnum (tc)); + y2 = (loggnum (s / xp) + (b + (v * v) / 2.0) * tp) / (v * sqrtgnum (tp)); rho1 = sqrtgnum (t / tc); rho2 = sqrtgnum (t / tp); @@ -1548,7 +1562,7 @@ "@rate is the risk annualised free rate of interest \n" "@cost_of_carry is the leakage in value of the underlying asset, " "for common stocks, this would be the dividend yield\n" - "@volatility is the annualised volatility in price of the underlying \n" + "@volatility is the annualised volatility in price of the underlying asset\n" "\n" "@EXAMPLES=\n" @@ -1619,23 +1633,23 @@ I = CriticalValueOptionsOnOptions (call_put, x1, x2, t2 - t1, r, b, v); rho = sqrtgnum (t1 / t2); - y1 = (loggnum (s / I) + (b + (v * v) / 2) * t1) / (v * sqrtgnum (t1)); + y1 = (loggnum (s / I) + (b + (v * v) / 2.0) * t1) / (v * sqrtgnum (t1)); y2 = y1 - v * sqrtgnum (t1); - z1 = (loggnum (s / x1) + (b + (v * v) / 2) * t2) / (v * sqrtgnum (t2)); + z1 = (loggnum (s / x1) + (b + (v * v) / 2.0) * t2) / (v * sqrtgnum (t2)); z2 = z1 - v * sqrtgnum (t2); if (!strcmp (type_flag , "cc")) gfresult = s * expgnum ((b - r) * t2) * cum_biv_norm_dist1 (z1, y1, rho) - - x1 * expgnum (-r * t2) * cum_biv_norm_dist1 (z2, y2, rho) - x2 * expgnum (-r * t1) * calc_N (y2); + x1 * expgnum (-r * t2) * cum_biv_norm_dist1 (z2, y2, rho) - x2 * expgnum (-r * t1) * cum_norm_dist (y2); else if (!strcmp (type_flag , "pc")) gfresult = x1 * expgnum (-r * t2) * cum_biv_norm_dist1 (z2, -y2, -rho) - - s * expgnum ((b - r) * t2) * cum_biv_norm_dist1 (z1, -y1, -rho) + x2 * expgnum (-r * t1) * calc_N (-y2); + s * expgnum ((b - r) * t2) * cum_biv_norm_dist1 (z1, -y1, -rho) + x2 * expgnum (-r * t1) * cum_norm_dist (-y2); else if (!strcmp (type_flag , "cp")) gfresult = x1 * expgnum (-r * t2) * cum_biv_norm_dist1 (-z2, -y2, rho) - - s * expgnum ((b - r) * t2) * cum_biv_norm_dist1 (-z1, -y1, rho) - x2 * expgnum (-r * t1) * calc_N (-y2); + s * expgnum ((b - r) * t2) * cum_biv_norm_dist1 (-z1, -y1, rho) - x2 * expgnum (-r * t1) * cum_norm_dist (-y2); else if (!strcmp (type_flag , "pp")) gfresult = s * expgnum ((b - r) * t2) * cum_biv_norm_dist1 (-z1, y1, -rho) - - x1 * expgnum (-r * t2) * cum_biv_norm_dist1 (-z2, y2, -rho) + expgnum (-r * t1) * x2 * calc_N (y2); + x1 * expgnum (-r * t2) * cum_biv_norm_dist1 (-z2, y2, -rho) + expgnum (-r * t1) * x2 * cum_norm_dist (y2); else { g_free (type_flag); return value_new_error_VALUE (ei->pos); @@ -1653,15 +1667,15 @@ "OPT_ON_OPTIONS models the theoretical price of options on options \n" "@type_flag is 'cc' for calls on calls, 'cp' for calls on puts, and so on for 'pc', and 'pp'\n" "@spot is the spot price of the underlying asset.\n" - "@strike1 is the strike price at which the option is struck.\n" - "@strike2 is the strike price at which the option is struck.\n" + "@strike1 is the strike price at which the option being valued is struck.\n" + "@strike2 is the strike price at which the underlying option is struck.\n" "@time1 is the time in years to maturity of the option.\n" "@time2 is the time in years to the maturity of the underlying option.\n" "(@time2 >= @time1)\n" "@rate is the risk annualised free rate of interest \n" - "@cost_of_carry is the leakage in value of the underlying asset, " + "@cost_of_carry is the leakage in value of the underlying asset of the underlying option" "for common stocks, this would be the dividend yield\n" - "@volatility is the annualised volatility in price of the underlying \n" + "@volatility is the annualised volatility in price of the underlying asset of the underlying option\n" "\n" "@EXAMPLES=\n" "\n" @@ -1705,8 +1719,8 @@ gnm_float v = value_get_as_float (argv[8]); gnm_float rho = sqrtgnum (t1 / t2); - gnm_float z1 = (loggnum (s / x2) + (b + (v * v) / 2) * t2) / (v * sqrtgnum (t2)); - gnm_float z2 = (loggnum (s / x1) + (b + (v * v) / 2) * t1) / (v * sqrtgnum (t1)); + gnm_float z1 = (loggnum (s / x2) + (b + (v * v) / 2.0) * t2) / (v * sqrtgnum (t2)); + gnm_float z2 = (loggnum (s / x1) + (b + (v * v) / 2.0) * t1) / (v * sqrtgnum (t1)); gnm_float gfresult; @@ -1715,12 +1729,12 @@ gfresult = opt_bs1 (call_put, s, x1, t1, r, v, b) + s * expgnum ((b - r) * t2) * cum_biv_norm_dist1 (z1, -z2, -rho) - x2 * expgnum (-r * t2) * cum_biv_norm_dist1 (z1 - sqrtgnum ((v * v) * t2), -z2 + sqrtgnum ((v * v) * t1), -rho); - + break; case OS_Put: gfresult = opt_bs1 (call_put, s, x1, t1, r, v, b) + x2 * expgnum (-r * t2) * cum_biv_norm_dist1 (-z1 + sqrtgnum ((v * v) * t2), z2 - sqrtgnum ((v * v) * t1), -rho) - s * expgnum ((b - r) * t2) * cum_biv_norm_dist1 (-z1, z2, -rho); - + break; default: gfresult = -123; } @@ -1742,20 +1756,411 @@ "@call_put_flag is c or p to indicate whether the option is a call or a put \n" "@spot is the spot price of the underlying asset.\n" "@strike1 is the strike price at which the option is struck.\n" - "@strike2 is the strike price at which the option is struck.\n" + "@strike2 is the strike price at which the option is re-struck if out of the money at @time1.\n" "@time1 is the initial maturity of the option in years.\n" "@time2 is the is the extended maturity in years if chosen.\n" "@rate is the risk annualised free rate of interest.\n" "@cost_of_carry is the leakage in value of the underlying asset, " "for common stocks, this would be the dividend yield.\n" - "@volatility is the annualised volatility in price of the underlying.\n" + "@volatility is the annualised volatility in price of the underlying asset.\n" + + "\n" + "@EXAMPLES=\n" + "\n" + "@SEEALSO=OPT_BS, OPT_BS_DELTA, OPT_BS_RHO, OPT_BS_THETA, OPT_BS_GAMMA") +}; + + + +/* Two asset correlation options */ +static GnmValue * +opt_2_asset_correlation(FunctionEvalInfo *ei, GnmValue *argv[]) +{ + OptionSide call_put = option_side(value_peek_string(argv[0])); + gnm_float s1 = value_get_as_float(argv[1]); + gnm_float s2 = value_get_as_float(argv[2]); + gnm_float x1 = value_get_as_float(argv[3]); + gnm_float x2 = value_get_as_float(argv[4]); + gnm_float t = value_get_as_float(argv[5]); + gnm_float b1 = value_get_as_float(argv[6]); + gnm_float b2 = value_get_as_float(argv[7]); + gnm_float r = value_get_as_float(argv[8]); + gnm_float v1 = value_get_as_float(argv[9]); + gnm_float v2 = value_get_as_float(argv[10]); + gnm_float rho = value_get_as_float(argv[11]); + gnm_float y1, y2; + + y1 = (loggnum(s1 / x1) + (b1 - (v1 * v1) / 2.0) * t) / (v1 * sqrtgnum(t)); + y2 = (loggnum(s2 / x2) + (b2 - (v2 * v2) / 2.0) * t) / (v2 * sqrtgnum(t)); + + if (call_put == OS_Call){ + return value_new_float(s2 * expgnum((b2 - r) * t) + * cum_biv_norm_dist1(y2 + v2 * sqrtgnum(t), y1 + rho * v2 * sqrtgnum(t), rho) + - x2 * expgnum(-r * t) * cum_biv_norm_dist1(y2, y1, rho)); + }else if (call_put == OS_Put){ + return value_new_float(x2 * expgnum(-r * t) * cum_biv_norm_dist1(-y2, -y1, rho) + - s2 * expgnum((b2 - r) * t) * cum_biv_norm_dist1(-y2 - v2 * sqrtgnum(t), -y1 - rho * v2 * sqrtgnum(t), rho)); + }else + return value_new_error_NUM (ei->pos); +} + +static char const *help_opt_2_asset_correlation = { + /* xgettext:no-c-format */ + N_("@FUNCTION=OPT_2_ASSET_CORRELATION\n" + + "@SYNTAX=OPT_2_ASSSET_CORRELATION(call_put_flag,spot1,spot2,strike1,strike2," + "time,cost_of_carry1,cost_of_carry2,rate,volatility1,volatility2,rho)\n" + "@DESCRIPTION=" + "OPT_2_ASSET_CORRELATION models the theoretical price of options " + + "on 2 assets with correlation @rho.\nThe payoff for a call is " + "max(@spot2 - @strike2,0) if @spot1 > @strike1 or 0 otherwise.\n" + "The payoff for a put is max (@strike2 - @spot2, 0) if @spot1 < @strike1 or 0 otherwise.\n" + "@call_put_flag is c or p to indicate whether the option is a call or a put \n" + "@spot1 & @spot2 are the spot prices of the underlying assets.\n" + "@strike1 & @strike2 are the strike prices at which the option" + " is struck.\n" + "@time is the initial maturity of the option in years.\n" + "@rate is the risk annualised free rate of interest.\n" + "@cost_of_carry1 & @cost_of_carry2 are the leakage in value of the underlying assets, " + "for common stocks, this would be the dividend yield.\n" + "@volatility1 & @volatility2 are the annualised volatility in price of the underlying assets.\n" + + "\n" + "@EXAMPLES=\n" + "\n" + "@SEEALSO=OPT_BS, OPT_BS_DELTA, OPT_BS_RHO, OPT_BS_THETA, OPT_BS_GAMMA") +}; + + +/* European option to exchange one asset for another */ +static GnmValue * +opt_euro_exchange(FunctionEvalInfo *ei, GnmValue *argv[]) +{ + gnm_float s1 = value_get_as_float(argv[0]); + gnm_float s2 = value_get_as_float(argv[1]); + gnm_float q1 = value_get_as_float(argv[2]); + gnm_float q2 = value_get_as_float(argv[3]); + gnm_float t = value_get_as_float(argv[4]); + gnm_float r = value_get_as_float(argv[5]); + gnm_float b1 = value_get_as_float(argv[6]); + gnm_float b2 = value_get_as_float(argv[7]); + gnm_float v1 = value_get_as_float(argv[8]); + gnm_float v2 = value_get_as_float(argv[9]); + gnm_float rho = value_get_as_float(argv[10]); + gnm_float v, d1, d2; + + v = sqrtgnum(v1 * v1 + v2 * v2 - 2.0 * rho * v1 * v2); + d1 = (loggnum(q1 * s1 / (q2 * s2)) + (b1 - b2 + (v * v) / 2.0) * t) / (v * sqrtgnum(t)); + d2 = d1 - v * sqrtgnum(t); + + return value_new_float(q1 * s1 * expgnum((b1 - r) * t) * cum_norm_dist(d1) - q2 * s2 * expgnum((b2 - r) * t) * cum_norm_dist(d2)); +} + +static char const *help_opt_euro_exchange = { + /* xgettext:no-c-format */ + N_("@FUNCTION=OPT_EURO_EXCHANGE\n" + "@SYNTAX=OPT_EURO_EXCHANGE(spot1,spot2,qty1,qty2," + "time,rate,cost_of_carry1,cost_of_carry2," + "volatility1,volatility2,rho)\n" + "@DESCRIPTION=" + "OPT_EURO_EXCHANGE models the theoretical price of a European " + "option to exchange one asset with quantity @qty2 and spot " + "price @spot2 for another, with quantity @qty1 and spot price " + "@spot1 \n" + "@time is the initial maturity of the option in years.\n" + "@rate is the risk annualised free rate of interest.\n" + "@cost_of_carry1 & @cost_of_carry2 are the leakage in value of the underlying assets, " + "for common stocks, this would be the dividend yield.\n" + "@volatility1 & @volatility2 are the annualised volatility in price of the underlying assets.\n" + "@rho is the correlation between the two assets.\n" + "\n" + "@EXAMPLES=\n" + "\n" + "@SEEALSO=OPT_BS, OPT_BS_DELTA, OPT_BS_RHO, OPT_BS_THETA, OPT_BS_GAMMA") +}; + + +/* American option to exchange one asset for another */ +static GnmValue * +opt_amer_exchange(FunctionEvalInfo *ei, GnmValue *argv[]) +{ + gnm_float s1 = value_get_as_float(argv[0]); + gnm_float s2 = value_get_as_float(argv[1]); + gnm_float q1 = value_get_as_float(argv[2]); + gnm_float q2 = value_get_as_float(argv[3]); + gnm_float t = value_get_as_float(argv[4]); + gnm_float r = value_get_as_float(argv[5]); + gnm_float b1 = value_get_as_float(argv[6]); + gnm_float b2 = value_get_as_float(argv[7]); + gnm_float v1 = value_get_as_float(argv[8]); + gnm_float v2 = value_get_as_float(argv[9]); + gnm_float rho = value_get_as_float(argv[10]); + gnm_float v = sqrtgnum(v1 * v1 + v2 * v2 - 2.0 * rho * v1 * v2); + return value_new_float(opt_bjer_stens1(OS_Call, q1 * s1, q2 * s2, t, r - b2, v,b1 - b2)); +} + +static char const *help_opt_amer_exchange = { + /* xgettext:no-c-format */ + N_("@FUNCTION=OPT_AMER_EXCHANGE\n" + "@SYNTAX=OPT_AMER_EXCHANGE(spot1,spot2,qty1,qty2,time,rate,cost_of_carry1,cost_of_carry2,volatility1, volatility2, rho)\n" + "@DESCRIPTION=" + "OPT_AMER_EXCHANGE models the theoretical price of an American " + "option to exchange one asset with quantity @qty2 and spot " + "price @spot2 for another, with quantity @qty1 and spot price " + "@spot1 \n" + "@time is the initial maturity of the option in years.\n" + "@rate is the risk annualised free rate of interest.\n" + "@cost_of_carry1 & @cost_of_carry2 are the leakage in value of the underlying assets, " + "for common stocks, this would be the dividend yield.\n" + "@volatility1 & @volatility2 are the annualised volatility in price of the underlying assets.\n" + "@rho is the correlation between the two assets.\n" + "\n" + "@EXAMPLES=\n" + "\n" + "@SEEALSO=OPT_EURO_EXCH, OPT_BS, OPT_BS_DELTA, OPT_BS_RHO, OPT_BS_THETA, OPT_BS_GAMMA") +}; + + +/* Spread option approximation */ +static GnmValue * +opt_spread_approx(FunctionEvalInfo *ei, GnmValue *argv[]) +{ + OptionSide call_put_flag = option_side(value_peek_string(argv[0])); + gnm_float f1 = value_get_as_float(argv[1]); + gnm_float f2 = value_get_as_float(argv[2]); + gnm_float x = value_get_as_float(argv[3]); + gnm_float t = value_get_as_float(argv[4]); + gnm_float r = value_get_as_float(argv[5]); + gnm_float v1 = value_get_as_float(argv[6]); + gnm_float v2 = value_get_as_float(argv[7]); + gnm_float rho = value_get_as_float(argv[8]); + + gnm_float v, F; + + v = sqrtgnum(v1 * v1 + powgnum((v2 * f2 / (f2 + x)) , 2) - 2.0 * rho * v1 * v2 * f2 / (f2 + x)); + F = f1 / (f2 + x); + + return value_new_float(opt_bs1(call_put_flag, F, 1.0, t, r, v,0.0) * (f2 + x)); +} + +static char const *help_opt_spread_approx = { + /* xgettext:no-c-format */ + N_("@FUNCTION=OPT_SPREAD_APPROX\n" + "@SYNTAX=OPT_SPREAD_APPROX(call_put_flag,fut_price1,fut_price2,strike,time, rate,volatility1,volatility2,rho)\n" + "@DESCRIPTION=" + "OPT_SPREAD_APPROX models the theoretical price of theoretical price of a European option on the spread between two futures contracts.\n" + "@fut_price1 & @fut_price2 are the prices of the two futures contracts.\n" + "@strike is the strike price at which the option is struck \n" + "@time is the initial maturity of the option in years.\n" + "@rate is the risk annualised free rate of interest.\n" + "@volatility1 & @volatility2 are the annualised volatility in price of the underlying futures contracts.\n" + "@rho is the correlation between the two futures contracts.\n" + "\n" + "@EXAMPLES=\n" + "\n" + "@SEEALSO=OPT_BS, OPT_BS_DELTA, OPT_BS_RHO, OPT_BS_THETA, OPT_BS_GAMMA") +}; + + +/* Floating strike lookback options */ +static GnmValue * opt_float_strk_lkbk(FunctionEvalInfo *ei, GnmValue *argv[]) +{ + OptionSide call_put_flag = option_side(value_peek_string(argv[0])); + gnm_float s = value_get_as_float(argv[1]); + gnm_float s_min = value_get_as_float(argv[2]); + gnm_float s_max = value_get_as_float(argv[3]); + gnm_float t = value_get_as_float(argv[4]); + gnm_float r = value_get_as_float(argv[5]); + gnm_float b = value_get_as_float(argv[6]); + gnm_float v = value_get_as_float(argv[7]); + + + gnm_float a1, a2, m; + if(OS_Call == call_put_flag) + m = s_min; + else if(OS_Put == call_put_flag) + m = s_max; + else return value_new_error_NUM(ei->pos); + + a1 = (loggnum(s / m) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum(t)); + a2 = a1 - v * sqrtgnum(t); + + + if(OS_Call == call_put_flag) + return value_new_float(s * expgnum((b - r) * t) * cum_norm_dist(a1) - m * expgnum(-r * t) * cum_norm_dist(a2) +expgnum(-r * t) * (v * v) / (2.0 * b) * s * (powgnum((s / m) , (-2.0 * b / (v * v))) * cum_norm_dist(-a1 + 2.0 * b / v * sqrtgnum(t)) - expgnum(b * t) * cum_norm_dist(-a1))); + else if(OS_Put == call_put_flag) + return value_new_float(m * expgnum(-r * t) * cum_norm_dist(-a2) - s * expgnum((b - r) * t) * cum_norm_dist(-a1) + expgnum(-r * t) * (v * v) / (2.0 * b) * s * (-powgnum((s / m) , ((-2.0 * b) / (v * v))) * cum_norm_dist(a1 - 2.0 * b / v * sqrtgnum(t)) + expgnum(b * t) * cum_norm_dist(a1))); +} + +static char const *help_opt_float_strk_lkbk = { + /* xgettext:no-c-format */ + N_("@FUNCTION=OPT_FLOAT_STRK_LKBK\n" + "@SYNTAX=OPT_FLOAT_STRK_LKBK(call_put_flag,spot,spot_min,spot_max,time,rate,cost_of_carry,volatility)\n" + "@DESCRIPTION=" + "OPT_FLOAT_STRK_LKBK models the theoretical price of an option where the holder of the option may exercise on expiry at the most favourable price observed during the options life of the underlying asset\n" + "@call_put_flag is c or p to indicate whether the option is a call or a put \n" + "@spot is the spot price of the underlying asset.\n" + "@spot_min is the minimum spot price of the underlying asset so far observed.\n" + "@spot_max is the maximum spot price of the underlying asset so far observed.\n" + "@time is the initial maturity of the option in years.\n" + "@rate is the risk annualised free rate of interest.\n" + "@cost_of_carry is the leakage in value of the underlying asset, " + "for common stocks, this would be the dividend yeild.\n" + "@volatility is the annualised volatility in price of the underlying asset.\n" "\n" "@EXAMPLES=\n" "\n" "@SEEALSO=OPT_BS, OPT_BS_DELTA, OPT_BS_RHO, OPT_BS_THETA, OPT_BS_GAMMA") }; + +/* Fixed strike lookback options */ + +static GnmValue * +opt_fixed_strk_lkbk(FunctionEvalInfo *ei, GnmValue *argv[]) +{ + OptionSide call_put_flag = option_side(value_peek_string(argv[0])); + gnm_float s = value_get_as_float(argv[1]); + gnm_float s_min = value_get_as_float(argv[2]); + gnm_float s_max = value_get_as_float(argv[3]); + gnm_float x = value_get_as_float(argv[4]); + gnm_float t = value_get_as_float(argv[5]); + gnm_float r = value_get_as_float(argv[6]); + gnm_float b = value_get_as_float(argv[7]); + gnm_float v = value_get_as_float(argv[8]); + + gnm_float d1, d2; + gnm_float e1, e2, m; + + if(OS_Call == call_put_flag) + m = s_max; + else if(OS_Put == call_put_flag) + m = s_min; + + d1 = (loggnum(s / x) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum(t)); + d2 = d1 - v * sqrtgnum(t); + e1 = (loggnum(s / m) + (b + (v * v) / 2.0) * t) / (v * sqrtgnum(t)); + e2 = e1 - v * sqrtgnum(t); + + if (OS_Call == call_put_flag && x > m) + return value_new_float(s * expgnum((b - r) * t) * cum_norm_dist(d1) - x * expgnum(-r * t) * cum_norm_dist(d2) + s * expgnum(-r * t) * (v * v) / (2.0 * b) * (-powgnum((s / x) , (-2.0 * b / (v * v))) * cum_norm_dist(d1 - 2.0 * b / v * sqrtgnum(t)) + expgnum(b * t) * cum_norm_dist(d1))); + + else if (OS_Call == call_put_flag && x <= m) + return value_new_float(expgnum(-r * t) * (m - x) + s * expgnum((b - r) * t) * cum_norm_dist(e1) - expgnum(-r * t) * m * cum_norm_dist(e2) + s * expgnum(-r * t) * (v * v) / (2.0 * b) * (-powgnum((s / m) , (-2.0 * b / (v * v))) * cum_norm_dist(e1 - 2.0 * b / v * sqrtgnum(t)) + expgnum(b * t) * cum_norm_dist(e1))); + + else if (OS_Put == call_put_flag && x < m) + return value_new_float(-s * expgnum((b - r) * t) * cum_norm_dist(-d1) + x * expgnum(-r * t) * cum_norm_dist(-d1 + v * sqrtgnum(t)) + s * expgnum(-r * t) * (v * v) / (2.0 * b) * (powgnum((s / x) , (-2.0 * b / (v * v))) * cum_norm_dist(-d1 + 2.0 * b / v * sqrtgnum(t)) - expgnum(b * t) * cum_norm_dist(-d1))); + + else if (OS_Put == call_put_flag && x >= m) + return value_new_float(expgnum(-r * t) * (x - m) - s * expgnum((b - r) * t) * cum_norm_dist(-e1) + expgnum(-r * t) * m * cum_norm_dist(-e1 + v * sqrtgnum(t)) + expgnum(-r * t) * (v * v) / (2.0 * b) * s * (powgnum((s / m) , (-2.0 * b / (v * v))) * cum_norm_dist(-e1 + 2 * b / v * sqrtgnum(t)) - expgnum(b * t) * cum_norm_dist(-e1))); + +} + +static char const *help_opt_fixed_strk_lkbk = { + /* xgettext:no-c-format */ + N_("@FUNCTION=OPT_FIXED_STRK_LKBK\n" + "@SYNTAX=OPT_FIXED_STRK_LKBK(call_put_flag,spot,spot_max,spot_min,strike,time,rate,cost_of_carry,volatility)\n" + "@DESCRIPTION=" + "OPT_FIXED_STRK_LKBK models the theoretical price of where the holder of the option may exercise on expiry at the most favourable price observed during the options life of the underlying asset\n" + "@call_put_flag is c or p to indicate whether the option is a call or a put \n" + "@spot is the spot price of the underlying asset.\n" + "@spot_min is the minimum spot price of the underlying asset so far observed.\n" + "@spot_max is the maximum spot price of the underlying asset so far observed.\n" + "@time is the initial maturity of the option in years.\n" + "@rate is the risk annualised free rate of interest.\n" + "@cost_of_carry is the leakage in value of the underlying asset, " + "for common stocks, this would be the dividend yeild.\n" + "@volatility is the annualised volatility in price of the underlying asset.\n" + "\n" + "@EXAMPLES=\n" + "\n" + "@SEEALSO=OPT_BS, OPT_BS_DELTA, OPT_BS_RHO, OPT_BS_THETA, OPT_BS_GAMMA") +}; + + + +/* Binomial Tree valuation */ +static GnmValue * +opt_binomial(FunctionEvalInfo *ei, GnmValue *argv[]) +{ + OptionType amer_euro_flag = option_type(value_peek_string(argv[0])); + OptionSide call_put_flag = option_side(value_peek_string(argv[1])); + guint n = value_get_as_int(argv[2]); + gnm_float s = value_get_as_float(argv[3]); + gnm_float x = value_get_as_float(argv[4]); + gnm_float t = value_get_as_float(argv[5]); + gnm_float r = value_get_as_float(argv[6]); + gnm_float v = value_get_as_float(argv[7]); + gnm_float b = argv[8] ? value_get_as_float(argv[8]):0; + + gnm_float *value_array; + gnm_float u, d, p, dt, Df, temp1, temp2, gf_result; + gint i, j, z; + + value_array = (gnm_float *) g_try_malloc ((n + 2)* sizeof(gnm_float)); + if (value_array == NULL) + return value_new_error_NUM (ei->pos); + + if (OS_Call == call_put_flag) + z = 1; + else if (OS_Put == call_put_flag) + z = -1; + else return value_new_error_NUM(ei->pos); + if (OT_Error == amer_euro_flag) + return value_new_error_NUM(ei->pos); + dt = t / n; + u = expgnum(v * sqrtgnum(dt)); + d = 1.0 / u; + p = (expgnum(b * dt) - d) / (u - d); + Df = expgnum(-r * dt); + + for(i = 0; i <= n; ++i){ + temp1 = z * (s * powgnum(u , i) * powgnum(d , (n - i)) - x); + value_array[i] = (temp1>0.0)?temp1:0.0; + } + + for(j = n - 1; j > -1; --j){ + for(i = 0; i<=j; ++i){ + /*if (0==i)printf("secondloop %d\n",j);*/ + if (OT_Euro == amer_euro_flag) + value_array[i] = (p * value_array[i + 1] + (1.0 - p) * value_array[i]) * Df; + else if (OT_Amer == amer_euro_flag){ + temp1 = (z * (s * powgnum(u , i) * powgnum(d , (gnumabs(i - j))) - x)); + temp2 = (p * value_array[i + 1] + (1.0 - p) * value_array[i]) * Df; + value_array[i] = (temp1>temp2)?temp1:temp2; + } + } + } + gf_result = value_array[0]; + g_free (value_array); + return value_new_float(gf_result); +} + +static char const *help_opt_binomial = { + /* xgettext:no-c-format */ + N_("@FUNCTION=OPT_BINOMIAL\n" + "@SYNTAX=OPT_BINOMIAL(amer_euro_flag,call_put_flag,num_time_steps, spot, strike, time, rate, volatility, cost_of_carry)\n" + "@DESCRIPTION=" + "OPT_ models the theoretical price of either an American or European style option using a binomial tree \n" + "@amer_euro_flag is either \"a\" or \"e\" to indicate weather the option being valued is an American or European style option respectively\n" + "@call_put_flag is c or p to indicate whether the option is a call or a put \n" + "@num_time_steps is the number of time steps used in the valuation, a greater number of time steps yeilds greater accuracy however is slower to calculate\n" + "@spot is the spot price of the underlying asset.\n" + "@strike is the strike price at which the option is struck \n" + "@time is the initial maturity of the option in years.\n" + "@rate is the risk annualised free rate of interest.\n" + "@volatility is the annualised volatility in price of the underlying asset.\n" + "@cost_of_carry is the leakage in value of the underlying asset, " + "\n" + "@EXAMPLES=\n" + "\n" + "@SEEALSO=OPT_BS, OPT_BS_DELTA, OPT_BS_RHO, OPT_BS_THETA, OPT_BS_GAMMA") +}; + + + GnmFuncDescriptor const derivatives_functions [] = { { "opt_bs", "sfffff|f", N_("call_put_flag, spot, strike, time, rate, volatility, cost_of_carry"), @@ -1817,9 +2222,9 @@ &help_opt_exec, opt_exec, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, - { "opt_bjerStens", + { "opt_bjer_stens", "sffffff", N_("call_put_flag, spot, strike, time, rate, cost_of_carry, volatility"), - &help_opt_bjerStens, opt_bjerStens, NULL, NULL, NULL, NULL, + &help_opt_bjer_stens, opt_bjer_stens, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, { "opt_miltersen_schwartz", @@ -1827,9 +2232,9 @@ &help_opt_miltersen_schwartz, opt_miltersen_schwartz, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, - { "opt_BAW_amer", + { "opt_baw_amer", "sffffff", N_("call_put_flag, spot, strike, time, rate, cost_of_carry, volatility"), - &help_opt_BAW_amer, opt_BAW_amer, NULL, NULL, NULL, NULL, + &help_opt_baw_amer, opt_baw_amer, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, { "opt_rgw", @@ -1866,6 +2271,44 @@ "sffffffff", N_("type_flag, spot, strike1, strike2, time1, time2, rate, cost_of_carry, volatility"), &help_opt_extendible_writer, opt_extendible_writer, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, + + { "opt_2_asset_correlation", + "sfffffffffff", N_("type_flag, spot1, spot2, strike1, strike2, time, cost_of_carry1, cost_of_carry2, rate, volatility1, volatility2, rho"), + &help_opt_2_asset_correlation, opt_2_asset_correlation, NULL, NULL, NULL, NULL, + GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, + + { "opt_euro_exchange", + "fffffffffff", N_("spot1,spot2,qty1,qty2,time,rate,cost_of_carry1,cost_of_carry2,volatility1,volatility2,rho"), + &help_opt_euro_exchange, opt_euro_exchange, NULL, NULL, NULL, NULL, + GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, + + { "opt_amer_exchange", + "fffffffffff", N_("spot1,spot2,qty1,qty2,time,rate,cost_of_carry1,cost_of_carry2,volatility1,volatility2,rho"), + &help_opt_amer_exchange, opt_amer_exchange, NULL, NULL, NULL, NULL, + GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, + + { "opt_spread_approx", + "sffffffff", N_("call_put_flag,fut_price1,fut_price2,strike,time, rate,volatility1,volatility2,rho"), + &help_opt_spread_approx, opt_spread_approx, NULL, NULL, NULL, NULL, + GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, + + { "opt_float_strk_lkbk", + "sfffffff", N_("call_put_flag,spot,spot_min,spot_max,time,rate,cost_of_carry,volatility"), + &help_opt_float_strk_lkbk, opt_float_strk_lkbk, NULL, NULL, NULL, NULL, + GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, + + { "opt_fixed_strk_lkbk", + "sffffffff", N_("call_put_flag,spot,spot_min,spot_max,strike,time,rate,cost_of_carry,volatility"), + &help_opt_fixed_strk_lkbk, opt_fixed_strk_lkbk, NULL, NULL, NULL, NULL, + GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, + + + { "opt_binomial", + "ssffffff|f", N_("amer_euro_flag,call_put_flag,num_time_steps, spot, strike, time, rate, volatility, cost_of_carry"), + &help_opt_binomial, opt_binomial, NULL, NULL, NULL, NULL, + GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, + + { NULL} }; Index: plugins/derivatives/plugin.xml.in =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/derivatives/plugin.xml.in,v retrieving revision 1.10 retrieving revision 1.12 diff -u -w -r1.10 -r1.12 --- plugins/derivatives/plugin.xml.in 23 Aug 2003 01:24:17 -0000 1.10 +++ plugins/derivatives/plugin.xml.in 27 Jul 2004 21:09:42 -0000 1.12 @@ -22,8 +22,8 @@ - - + + @@ -33,6 +33,13 @@ + + + + + + + Index: plugins/derivatives/tests.gnumeric =================================================================== RCS file: plugins/derivatives/tests.gnumeric diff -N plugins/derivatives/tests.gnumeric Binary files /dev/null and /tmp/cvsuWa3Cp differ Index: plugins/dif/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/dif/ChangeLog,v retrieving revision 1.94 retrieving revision 1.95 diff -u -w -r1.94 -r1.95 --- plugins/dif/ChangeLog 19 Jul 2004 12:40:12 -0000 1.94 +++ plugins/dif/ChangeLog 28 Jul 2004 19:35:54 -0000 1.95 @@ -1,3 +1,8 @@ +2004-07-28 Morten Welinder + + * dif.c (dif_file_open): Call gnumeric_io_error_occurred instead + of checking that its address is non-NULL. + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: plugins/dif/dif.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/dif/dif.c,v retrieving revision 1.60 retrieving revision 1.61 diff -u -w -r1.60 -r1.61 --- plugins/dif/dif.c 15 Jun 2004 03:08:31 -0000 1.60 +++ plugins/dif/dif.c 28 Jul 2004 19:35:54 -0000 1.61 @@ -265,7 +265,7 @@ gnumeric_io_error_push (io_context, error_info_new_str (_("Error while reading DIF file."))); dif_input_context_destroy (ctxt); - } else if (!gnumeric_io_error_occurred) + } else if (!gnumeric_io_error_occurred (io_context)) gnumeric_io_error_unknown (io_context); } Index: plugins/excel/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/excel/ChangeLog,v retrieving revision 1.855 retrieving revision 1.857 diff -u -w -r1.855 -r1.857 --- plugins/excel/ChangeLog 19 Jul 2004 12:40:12 -0000 1.855 +++ plugins/excel/ChangeLog 30 Jul 2004 00:59:15 -0000 1.857 @@ -1,3 +1,10 @@ +2004-07-10 Jody Goldberg + + * ms-excel-write.c (excel_write_workbook) : write WINDOW1 for all the views + (excel_write_sheet) : Drop the WINDOW1 here but expand to write a + WINDOW2, PANE, SCL, SELECTION tuple for all views, not just the + current + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: plugins/excel/ms-escher.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/excel/ms-escher.c,v retrieving revision 1.109 diff -u -w -r1.109 ms-escher.c --- plugins/excel/ms-escher.c 21 Jun 2004 02:16:01 -0000 1.109 +++ plugins/excel/ms-escher.c 9 Aug 2004 12:43:06 -0000 @@ -1120,6 +1120,7 @@ /* lets be really careful */ g_return_val_if_fail (6*num_properties + COMMON_HEADER_LEN <= h->len, TRUE); + g_return_val_if_fail (data != NULL, TRUE); for (i = 0; i < num_properties; ++i, fopte += 6) { guint16 const tmp = GSF_LE_GET_GUINT32(fopte); Index: plugins/excel/ms-excel-write.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/excel/ms-excel-write.c,v retrieving revision 1.267 diff -u -w -r1.267 ms-excel-write.c --- plugins/excel/ms-excel-write.c 3 Jul 2004 05:00:00 -0000 1.267 +++ plugins/excel/ms-excel-write.c 9 Aug 2004 12:43:08 -0000 @@ -274,7 +274,7 @@ GSF_LE_SET_GUINT16 (data+ 0, 0x0600); /* worksheet */ GSF_LE_SET_GUINT16 (data+ 4, 0x2775); /* build id == XP SP3 */ GSF_LE_SET_GUINT16 (data+ 6, 0x07cd); /* build year (= 1997) */ - GSF_LE_SET_GUINT32 (data+ 8, 0x000080c1); /* flags */ + GSF_LE_SET_GUINT32 (data+ 8, 0x000080c9); /* flags */ GSF_LE_SET_GUINT32 (data+12, 0x00000206); break; @@ -508,7 +508,7 @@ /* returns TRUE if a PANE record is necessary. */ static gboolean -excel_write_WINDOW2 (BiffPut *bp, ExcelWriteSheet *esheet) +excel_write_WINDOW2 (BiffPut *bp, ExcelWriteSheet *esheet, SheetView *sv) { /* 1 0x020 grids are the colour of the normal style */ /* 0 0x040 arabic */ @@ -518,7 +518,6 @@ guint8 *data; GnmCellPos top_left; Sheet const *sheet = esheet->gnum_sheet; - SheetView const *sv = sheet_get_view (sheet, esheet->ewb->gnum_wb_view); GnmColor *sheet_auto = sheet_style_get_auto_pattern_color (sheet); GnmColor *default_auto = style_color_auto_pattern (); guint32 biff_pat_col = 0x40; /* default grid color index == auto */ @@ -573,11 +572,9 @@ } static void -excel_write_PANE (BiffPut *bp, ExcelWriteSheet *esheet) +excel_write_PANE (BiffPut *bp, ExcelWriteSheet *esheet, SheetView *sv) { guint8 *data = ms_biff_put_len_next (bp, BIFF_PANE, 10); - SheetView const *sv = sheet_get_view (esheet->gnum_sheet, - esheet->ewb->gnum_wb_view); int const frozen_height = sv->unfrozen_top_left.row - sv->frozen_top_left.row; int const frozen_width = sv->unfrozen_top_left.col - @@ -3467,13 +3464,11 @@ ms_biff_put_commit (bp); } static void -excel_write_selections (BiffPut *bp, ExcelWriteSheet *esheet) +excel_write_selections (BiffPut *bp, ExcelWriteSheet *esheet, SheetView *sv) { GnmRange r; GnmCellPos pos; GList *tmp; - SheetView const *sv = sheet_get_view (esheet->gnum_sheet, - esheet->ewb->gnum_wb_view); excel_write_SELECTION (bp, sv->selections, &sv->edit_pos, 3); @@ -3839,14 +3834,13 @@ if (ewb->num_obj_groups > 0) excel_write_objs (esheet); -#warning check this. Why is there a window1 here ? - excel_write_WINDOW1 (ewb->bp, esheet->ewb->gnum_wb_view); - if (excel_write_WINDOW2 (ewb->bp, esheet)) - excel_write_PANE (ewb->bp, esheet); - - excel_write_SCL (ewb->bp, + SHEET_FOREACH_VIEW (esheet->gnum_sheet, view, { + if (excel_write_WINDOW2 (ewb->bp, esheet, view)) + excel_write_PANE (ewb->bp, esheet, view); + excel_write_SCL (ewb->bp, /* zoom will move to view eentually */ esheet->gnum_sheet->last_zoom_factor_used, FALSE); - excel_write_selections (ewb->bp, esheet); + excel_write_selections (ewb->bp, esheet, view); + }); /* These are actually specific to >= biff8 * but it can't hurt to have them here @@ -4284,7 +4278,8 @@ ms_biff_put_2byte (ewb->bp, BIFF_PROT4REVPASS, 0); } - excel_write_WINDOW1 (bp, ewb->gnum_wb_view); + WORKBOOK_FOREACH_VIEW (ewb->gnum_wb, view, + excel_write_WINDOW1 (bp, view);); ms_biff_put_2byte (ewb->bp, BIFF_BACKUP, 0); ms_biff_put_2byte (ewb->bp, BIFF_HIDEOBJ, 0); Index: plugins/excel/ms-formula-read.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/excel/ms-formula-read.c,v retrieving revision 1.210 diff -u -w -r1.210 ms-formula-read.c --- plugins/excel/ms-formula-read.c 11 Jun 2004 20:24:40 -0000 1.210 +++ plugins/excel/ms-formula-read.c 9 Aug 2004 12:43:08 -0000 @@ -6,7 +6,7 @@ * Michael Meeks (michael@ximian.com) * Jody Goldberg (jody@gnome.org) * - * (C) 1998-2003 Michael Meeks, Jody Goldberg + * (C) 1998-2004 Michael Meeks, Jody Goldberg */ #include #include Index: plugins/excel/ms-formula-write.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/excel/ms-formula-write.c,v retrieving revision 1.90 diff -u -w -r1.90 ms-formula-write.c --- plugins/excel/ms-formula-write.c 5 Jun 2004 16:19:34 -0000 1.90 +++ plugins/excel/ms-formula-write.c 9 Aug 2004 12:43:08 -0000 @@ -486,7 +486,7 @@ write_funcall (PolishData *pd, GnmExpr const *expr, XLOpType target_type) { - static guint8 zeros[12] = { 0,0,0,0,0,0,0,0,0,0,0,0 }; + static guint8 const zeros [12]; /* excel is limited to 128 args max */ int max_args = 126, num_args = 0; @@ -682,9 +682,11 @@ op = expr->any.oper; switch (op) { + case GNM_EXPR_OP_ANY_BINARY : + target_type = XL_VAL; + case GNM_EXPR_OP_RANGE_CTOR: - case GNM_EXPR_OP_INTERSECT: - case GNM_EXPR_OP_ANY_BINARY : { + case GNM_EXPR_OP_INTERSECT: { int const prec = operations[op].prec; write_node (pd, expr->binary.value_a, @@ -796,8 +798,7 @@ case GNM_EXPR_OP_ANY_UNARY : { int const prec = operations[op].prec; - write_node (pd, expr->unary.value, operations[op].prec, - target_type); + write_node (pd, expr->unary.value, operations[op].prec, XL_VAL); push_guint8 (pd, operations[op].xl_op); if (prec <= paren_level) push_guint8 (pd, FORMULA_PTG_PAREN); Index: plugins/fn-complex/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-complex/ChangeLog,v retrieving revision 1.35 retrieving revision 1.36 diff -u -w -r1.35 -r1.36 --- plugins/fn-complex/ChangeLog 19 Jul 2004 12:40:12 -0000 1.35 +++ plugins/fn-complex/ChangeLog 26 Jul 2004 13:22:58 -0000 1.36 @@ -1,3 +1,8 @@ +2004-07-26 Morten Welinder + + * functions.c (gnumeric_imabs): Accept plain numbers. + (gnumeric_imconjugate): Ditto. Fixes 148323. + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: plugins/fn-complex/functions.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-complex/functions.c,v retrieving revision 1.27 retrieving revision 1.28 diff -u -w -r1.27 -r1.28 --- plugins/fn-complex/functions.c 29 Dec 2003 03:13:24 -0000 1.27 +++ plugins/fn-complex/functions.c 26 Jul 2004 13:22:58 -0000 1.28 @@ -173,9 +173,6 @@ if (value_get_as_complex (argv[0], &c, &imunit)) return value_new_error_VALUE (ei->pos); - if (argv[0]->type != VALUE_STRING) - return value_new_error_VALUE (ei->pos); - return value_new_float (complex_mod (&c)); } @@ -237,9 +234,6 @@ char imunit; if (value_get_as_complex (argv[0], &c, &imunit)) - return value_new_error_VALUE (ei->pos); - - if (argv[0]->type != VALUE_STRING) return value_new_error_VALUE (ei->pos); complex_conj (&res, &c); Index: plugins/fn-eng/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-eng/ChangeLog,v retrieving revision 1.35 retrieving revision 1.36 diff -u -w -r1.35 -r1.36 --- plugins/fn-eng/ChangeLog 19 Jul 2004 12:40:13 -0000 1.35 +++ plugins/fn-eng/ChangeLog 27 Jul 2004 15:17:34 -0000 1.36 @@ -1,3 +1,7 @@ +2004-07-27 Morten Welinder + + * functions.c (gnumeric_invsuminv): New function. + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: plugins/fn-eng/functions.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-eng/functions.c,v retrieving revision 1.108 retrieving revision 1.110 diff -u -w -r1.108 -r1.110 --- plugins/fn-eng/functions.c 29 Dec 2003 03:13:28 -0000 1.108 +++ plugins/fn-eng/functions.c 28 Jul 2004 19:35:55 -0000 1.110 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -1165,6 +1166,59 @@ /***************************************************************************/ +static char const *help_invsuminv = { + N_("@FUNCTION=INVSUMINV\n" + "@SYNTAX=INVSUMINV(x1,x2,...)\n" + "@DESCRIPTION=" + "INVSUMINV sum calculates the inverse of the sum of inverses.\n" + "\n" + "The primary use of this is for calculating equivalent resistance for " + "parallel resistors or equivalent capacitance of a series of capacitors.\n" + "\n" + "* All arguments must be non-negative, or else a #VALUE! result is returned.\n" + "* If any argument is zero, the result is zero.\n" + "\n" + "@EXAMPLES=\n" + "INVSUMINV(2000,2000) equals 1000.\n" + "\n" + "@SEEALSO=HARMEAN") +}; + +static int +range_invsuminv (gnm_float const *xs, int n, gnm_float *res) +{ + int i; + gnm_float suminv = 0; + gboolean zerop = FALSE; + + if (n <= 0) return 1; + + for (i = 0; i < n; i++) { + gnm_float x = xs[i]; + if (x < 0) return 1; + if (x == 0) + zerop = TRUE; + else + suminv += 1 / x; + } + + *res = zerop ? 0 : 1 / suminv; + return 0; +} + +static GnmValue * +gnumeric_invsuminv (FunctionEvalInfo *ei, GnmExprList *nodes) +{ + return float_range_function (nodes, ei, + range_invsuminv, + COLLECT_IGNORE_STRINGS | + COLLECT_IGNORE_BOOLS | + COLLECT_IGNORE_BLANKS, + GNM_ERROR_VALUE); +} + +/***************************************************************************/ + const GnmFuncDescriptor engineering_functions[] = { { "besseli", "ff", "xnum,ynum", &help_besseli, gnumeric_besseli, NULL, NULL, NULL, NULL, @@ -1225,6 +1279,10 @@ { "hex2oct", "S|f", "xnum,ynum", &help_hex2oct, gnumeric_hex2oct, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, + + { "invsuminv", NULL, "", &help_invsuminv, + NULL, gnumeric_invsuminv, NULL, NULL, NULL, + GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, { "oct2bin", "S|f", "xnum,ynum", &help_oct2bin, gnumeric_oct2bin, NULL, NULL, NULL, NULL, Index: plugins/fn-eng/plugin.xml.in =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-eng/plugin.xml.in,v retrieving revision 1.4 retrieving revision 1.5 diff -u -w -r1.4 -r1.5 --- plugins/fn-eng/plugin.xml.in 23 Aug 2003 01:24:28 -0000 1.4 +++ plugins/fn-eng/plugin.xml.in 27 Jul 2004 15:17:34 -0000 1.5 @@ -29,6 +29,7 @@ + Index: plugins/fn-financial/functions.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-financial/functions.c,v retrieving revision 1.190 retrieving revision 1.191 diff -u -w -r1.190 -r1.191 --- plugins/fn-financial/functions.c 12 Apr 2004 17:39:43 -0000 1.190 +++ plugins/fn-financial/functions.c 5 Aug 2004 18:58:46 -0000 1.191 @@ -3819,7 +3819,7 @@ { "nper", "fff|ff", "rate,pmt,pv,fv,type", &help_nper, gnumeric_nper, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "npv", 0, "", + { "npv", NULL, "", &help_npv, NULL, gnumeric_npv, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_MONETARY, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, Index: plugins/fn-logical/functions.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-logical/functions.c,v retrieving revision 1.71 retrieving revision 1.72 diff -u -w -r1.71 -r1.72 --- plugins/fn-logical/functions.c 4 Jun 2004 20:04:58 -0000 1.71 +++ plugins/fn-logical/functions.c 5 Aug 2004 18:58:46 -0000 1.72 @@ -331,11 +331,11 @@ /***************************************************************************/ const GnmFuncDescriptor logical_functions[] = { - { "and", 0, N_("number,number,"), &help_and, NULL, + { "and", NULL, N_("number,number,"), &help_and, NULL, gnumeric_and, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_UNITLESS, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "or", 0, N_("number,number,"), &help_or, NULL, + { "or", NULL, N_("number,number,"), &help_or, NULL, gnumeric_or, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_UNITLESS, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, @@ -355,7 +355,7 @@ NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_UNITLESS, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, - { "xor", 0, N_("number,number,"), &help_xor, NULL, + { "xor", NULL, N_("number,number,"), &help_xor, NULL, gnumeric_xor, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_UNITLESS, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, Index: plugins/fn-lookup/functions.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-lookup/functions.c,v retrieving revision 1.129 retrieving revision 1.130 diff -u -w -r1.129 -r1.130 --- plugins/fn-lookup/functions.c 29 Dec 2003 03:13:33 -0000 1.129 +++ plugins/fn-lookup/functions.c 5 Aug 2004 18:58:46 -0000 1.130 @@ -1219,10 +1219,10 @@ { "address", "ff|ffs", N_("row_num,col_num,abs_num,a1,text"), &help_address, gnumeric_address, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_EXHAUSTIVE }, - { "areas", 0, N_("reference"), + { "areas", NULL, N_("reference"), &help_areas, NULL, gnumeric_areas, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "choose", 0, N_("index,value,"), + { "choose", NULL, N_("index,value,"), &help_choose, NULL, gnumeric_choose, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "column", "|A", N_("ref"), Index: plugins/fn-math/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-math/ChangeLog,v retrieving revision 1.52 retrieving revision 1.53 diff -u -w -r1.52 -r1.53 --- plugins/fn-math/ChangeLog 19 Jul 2004 12:40:13 -0000 1.52 +++ plugins/fn-math/ChangeLog 20 Jul 2004 19:23:41 -0000 1.53 @@ -1,3 +1,7 @@ +2004-07-20 Morten Welinder + + * functions.c (gnumeric_fact): Use lgamma_rgnum. + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: plugins/fn-math/functions.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-math/functions.c,v retrieving revision 1.226 retrieving revision 1.228 diff -u -w -r1.226 -r1.228 --- plugins/fn-math/functions.c 28 May 2004 22:48:20 -0000 1.226 +++ plugins/fn-math/functions.c 5 Aug 2004 18:58:46 -0000 1.228 @@ -908,8 +908,9 @@ return value_new_error_NUM (ei->pos); if (x > 12 || !x_is_integer) { - gnm_float tmp = lgammagnum (x + 1); - gnm_float res = signgam * expgnum (tmp); + int sign; + gnm_float tmp = lgamma_rgnum (x + 1, &sign); + gnm_float res = sign * expgnum (tmp); if (x_is_integer) res = floorgnum (res + 0.5); /* Round, just in case. */ return value_new_float (res); @@ -3201,14 +3202,14 @@ gnumeric_floor, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "gcd", 0, N_("number,number"), &help_gcd, + { "gcd", NULL, N_("number,number"), &help_gcd, NULL, gnumeric_gcd, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "int", "f", N_("number"), &help_int, gnumeric_int, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "lcm", 0, "", &help_lcm, + { "lcm", NULL, "", &help_lcm, NULL, gnumeric_lcm, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "ln", "f", N_("number"), &help_ln, @@ -3233,7 +3234,7 @@ gnumeric_mround, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "multinomial", 0, "", &help_multinomial, + { "multinomial", NULL, "", &help_multinomial, NULL, gnumeric_multinomial, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "odd" , "f", N_("number"), &help_odd, @@ -3242,7 +3243,7 @@ { "power", "ff", N_("base,exponent"), &help_power, gnumeric_power, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "g_product", 0, N_("number"), &help_g_product, + { "g_product", NULL, N_("number"), &help_g_product, NULL, gnumeric_g_product, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "quotient" , "ff", N_("numerator,denominator"), &help_quotient, @@ -3266,7 +3267,7 @@ gnumeric_roundup, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "seriessum", 0, N_("x,n,m,coefficients"), &help_seriessum, + { "seriessum", NULL, N_("x,n,m,coefficients"), &help_seriessum, NULL, gnumeric_seriessum, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sign", "f", N_("number"), &help_sign, @@ -3284,17 +3285,17 @@ { "sqrtpi", "f", N_("number"), &help_sqrtpi, gnumeric_sqrtpi, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "suma", 0, N_("number,number,"), &help_suma, + { "suma", NULL, N_("number,number,"), &help_suma, NULL, gnumeric_suma, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sumif", "rS|r", N_("range,criteria,actual_range"), &help_sumif, gnumeric_sumif, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "sumproduct", 0, N_("range,range,"), &help_sumproduct, + { "sumproduct", NULL, N_("range,range,"), &help_sumproduct, NULL, gnumeric_sumproduct, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "sumsq", 0, N_("number"), &help_sumsq, + { "sumsq", NULL, N_("number"), &help_sumsq, NULL, gnumeric_sumsq, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "sumx2my2", "AA", N_("array1,array2"), &help_sumx2my2, Index: plugins/fn-random/functions.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-random/functions.c,v retrieving revision 1.48 retrieving revision 1.49 diff -u -w -r1.48 -r1.49 --- plugins/fn-random/functions.c 29 Dec 2003 03:13:39 -0000 1.48 +++ plugins/fn-random/functions.c 5 Aug 2004 18:58:47 -0000 1.49 @@ -1187,7 +1187,7 @@ { "randweibull", "ff", N_("a,b"), &help_randweibull, gnumeric_randweibull, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, - { "simtable", 0, N_("d1,d2,...,dN"), &help_simtable, + { "simtable", NULL, N_("d1,d2,...,dN"), &help_simtable, NULL, gnumeric_simtable, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, Index: plugins/fn-stat/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-stat/ChangeLog,v retrieving revision 1.60 retrieving revision 1.61 diff -u -w -r1.60 -r1.61 --- plugins/fn-stat/ChangeLog 19 Jul 2004 12:40:13 -0000 1.60 +++ plugins/fn-stat/ChangeLog 26 Jul 2004 13:17:44 -0000 1.61 @@ -1,3 +1,8 @@ +2004-07-26 Morten Welinder + + * functions.c (gnumeric_harmean): Fix to return #NUM! on error. + (gnumeric_geomean): Ditto. + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: plugins/fn-stat/functions.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-stat/functions.c,v retrieving revision 1.227 retrieving revision 1.229 diff -u -w -r1.227 -r1.229 --- plugins/fn-stat/functions.c 12 Jul 2004 13:54:04 -0000 1.227 +++ plugins/fn-stat/functions.c 5 Aug 2004 18:58:47 -0000 1.229 @@ -746,7 +746,7 @@ COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS, - GNM_ERROR_VALUE); + GNM_ERROR_NUM); } /***************************************************************************/ @@ -777,7 +777,7 @@ COLLECT_IGNORE_STRINGS | COLLECT_IGNORE_BOOLS | COLLECT_IGNORE_BLANKS, - GNM_ERROR_VALUE); + GNM_ERROR_NUM); } /***************************************************************************/ @@ -5605,15 +5605,15 @@ /***************************************************************************/ const GnmFuncDescriptor stat_functions[] = { - { "avedev", 0, N_("number,number,"), + { "avedev", NULL, N_("number,number,"), &help_avedev, NULL, gnumeric_avedev, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "average", 0, N_("number,number,"), + { "average", NULL, N_("number,number,"), &help_average, NULL, gnumeric_average, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "averagea", 0, N_("number,number,"), + { "averagea", NULL, N_("number,number,"), &help_averagea, NULL, gnumeric_averagea, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, @@ -5651,10 +5651,10 @@ { "correl", "AA", N_("array1,array2"), &help_correl, gnumeric_correl, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "count", 0, N_("number,number,"), + { "count", NULL, N_("number,number,"), &help_count, NULL, gnumeric_count, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "counta", 0, N_("number,number,"), + { "counta", NULL, N_("number,number,"), &help_counta, NULL, gnumeric_counta, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "covar", "AA", N_("array1,array2"), @@ -5663,7 +5663,7 @@ { "critbinom", "fff", N_("trials,p,alpha"), &help_critbinom, gnumeric_critbinom, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "devsq", 0, N_("number,number,"), + { "devsq", NULL, N_("number,number,"), &help_devsq, NULL, gnumeric_devsq, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "expondist", "ffb", N_("x,y,cumulative"), @@ -5699,14 +5699,14 @@ { "gammaln", "f", N_("number"), &help_gammaln, gnumeric_gammaln, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "geomean", 0, N_("number,number,"), + { "geomean", NULL, N_("number,number,"), &help_geomean, NULL, gnumeric_geomean, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "growth", "A|AAb", N_("known_y's,known_x's,new_x's,const"), &help_growth, gnumeric_growth, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "harmean", 0, "", + { "harmean", NULL, "", &help_harmean, NULL, gnumeric_harmean, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, @@ -5716,10 +5716,10 @@ { "intercept", "AA", N_("number,number,"), &help_intercept, gnumeric_intercept, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "kurt", 0, N_("number,number,"), + { "kurt", NULL, N_("number,number,"), &help_kurt, NULL, gnumeric_kurt, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "large", 0, N_("number,number,"), + { "large", NULL, N_("number,number,"), &help_large, NULL, gnumeric_large, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, @@ -5741,27 +5741,27 @@ { "logreg", "A|Abb", N_("known_y's,known_x's,const,stat"), &help_logreg, gnumeric_logreg, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, - { "max", 0, N_("number,number,"), + { "max", NULL, N_("number,number,"), &help_max, NULL, gnumeric_max, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "maxa", 0, N_("number,number,"), + { "maxa", NULL, N_("number,number,"), &help_maxa, NULL, gnumeric_maxa, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "median", 0, N_("number,number,"), + { "median", NULL, N_("number,number,"), &help_median, NULL, gnumeric_median, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "min", 0, N_("number,number,"), + { "min", NULL, N_("number,number,"), &help_min, NULL, gnumeric_min, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "mina", 0, N_("number,number,"), + { "mina", NULL, N_("number,number,"), &help_mina, NULL, gnumeric_mina, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "mode", 0, N_("number,number,"), + { "mode", NULL, N_("number,number,"), &help_mode, NULL, gnumeric_mode, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, @@ -5807,7 +5807,7 @@ { "slope", "AA", N_("known_y's,known_x's"), &help_slope, gnumeric_slope, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "small", 0, N_("number,number,"), + { "small", NULL, N_("number,number,"), &help_small, NULL, gnumeric_small, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, @@ -5817,19 +5817,19 @@ { "ssmedian", "A|f", N_("array,interval"), &help_ssmedian, gnumeric_ssmedian, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, - { "stdev", 0, N_("number,number,"), + { "stdev", NULL, N_("number,number,"), &help_stdev, NULL, gnumeric_stdev, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "stdeva", 0, N_("number,number,"), + { "stdeva", NULL, N_("number,number,"), &help_stdeva, NULL, gnumeric_stdeva, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "stdevp", 0, N_("number,number,"), + { "stdevp", NULL, N_("number,number,"), &help_stdevp, NULL, gnumeric_stdevp, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "stdevpa", 0, N_("number,number,"), + { "stdevpa", NULL, N_("number,number,"), &help_stdevpa, NULL, gnumeric_stdevpa, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, @@ -5839,7 +5839,7 @@ { "rsq", "AA", N_("array1,array2"), &help_rsq, gnumeric_rsq, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "skew", 0, "", + { "skew", NULL, "", &help_skew, NULL, gnumeric_skew, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "tdist", "fff", N_("x,dof,tails"), @@ -5851,29 +5851,29 @@ { "trend", "A|AAb", N_("known_y's,known_x's,new_x's,const"), &help_trend, gnumeric_trend, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "trimmean", 0, N_("ref,fraction"), + { "trimmean", NULL, N_("ref,fraction"), &help_trimmean, NULL, gnumeric_trimmean, NULL, NULL, NULL, GNM_FUNC_SIMPLE + GNM_FUNC_AUTO_FIRST, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "ttest", "rrff", N_("array1,array2,tails,type"), &help_ttest, gnumeric_ttest, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "var", 0, N_("number,number,"), + { "var", NULL, N_("number,number,"), &help_var, NULL, gnumeric_var, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "vara", 0, N_("number,number,"), + { "vara", NULL, N_("number,number,"), &help_vara, NULL, gnumeric_vara, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "varp", 0, N_("number,number,"), + { "varp", NULL, N_("number,number,"), &help_varp, NULL, gnumeric_varp, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "varpa", 0, N_("number,number,"), + { "varpa", NULL, N_("number,number,"), &help_varpa, NULL, gnumeric_varpa, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "weibull", "fffb", N_("x.alpha,beta,cumulative"), &help_weibull, gnumeric_weibull, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "ztest", 0, N_("ref,x"), + { "ztest", NULL, N_("ref,x"), &help_ztest, NULL, gnumeric_ztest, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, @@ -5883,7 +5883,7 @@ { "geomdist", "ffb", N_("k,p"), &help_geomdist, gnumeric_geomdist, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, - { "kurtp", 0, N_("number,number,"), + { "kurtp", NULL, N_("number,number,"), &help_kurtp, NULL, gnumeric_kurtp, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, { "landau", "f", N_("x"), &help_landau, @@ -5904,13 +5904,13 @@ { "rayleightail", "fff", N_("x,a,sigma"), &help_rayleightail, gnumeric_rayleightail, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, - { "skewp", 0, "", + { "skewp", NULL, "", &help_skewp, NULL, gnumeric_skewp, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, - { "subtotal", 0, N_("function_nbr,ref,ref,"), + { "subtotal", NULL, N_("function_nbr,ref,ref,"), &help_subtotal, NULL, gnumeric_subtotal, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, - { "cronbach", 0, N_("ref,ref,"), + { "cronbach", NULL, N_("ref,ref,"), &help_cronbach, NULL, gnumeric_cronbach, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE }, Index: plugins/fn-string/functions.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/fn-string/functions.c,v retrieving revision 1.145 retrieving revision 1.146 diff -u -w -r1.145 -r1.146 --- plugins/fn-string/functions.c 23 Jun 2004 14:15:25 -0000 1.145 +++ plugins/fn-string/functions.c 5 Aug 2004 18:58:47 -0000 1.146 @@ -1131,7 +1131,7 @@ { "unicode", "S", N_("text"), &help_unicode, gnumeric_unicode, NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_BASIC }, - { "concatenate", 0, N_("text,text,"), &help_concatenate, + { "concatenate", NULL, N_("text,text,"), &help_concatenate, NULL, gnumeric_concatenate, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC }, { "dollar", "f|f", N_("num,decimals"), &help_dollar, Index: plugins/html/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/html/ChangeLog,v retrieving revision 1.181 retrieving revision 1.183 diff -u -w -r1.181 -r1.183 --- plugins/html/ChangeLog 19 Jul 2004 12:40:14 -0000 1.181 +++ plugins/html/ChangeLog 28 Jul 2004 06:51:49 -0000 1.183 @@ -1,3 +1,13 @@ +2004-07-28 Andreas J. Guelzow + + * html.c (html_write_cell_content): only export URL links + +2004-07-27 Andreas J. Guelzow + for: Yukihiro Nakai + + * html.c: Add link features. + * html.c: newline in cell turns to be
now + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: plugins/html/html.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/html/html.c,v retrieving revision 1.84 retrieving revision 1.86 diff -u -w -r1.84 -r1.86 --- plugins/html/html.c 12 Nov 2003 19:10:22 -0000 1.84 +++ plugins/html/html.c 28 Jul 2004 06:51:49 -0000 1.86 @@ -44,6 +44,7 @@ #include "style-border.h" #include #include "mstyle.h" +#include "hlink.h" #include #include @@ -91,6 +92,16 @@ case '\"': gsf_output_puts (output, """); break; + case '\n': + gsf_output_puts (output, "
\n"); + break; + case '\r': + gsf_output_puts (output, "
\r"); + if( *(str+1) == '\n' ) { + gsf_output_puts (output, "\n"); + str++; + } + break; default: c = g_utf8_get_char (str); if (((c >= 0x20) && (c < 0x80)) || @@ -148,6 +159,12 @@ guint b = 0; char *rendered_string; gboolean hidden = mstyle_get_content_hidden (mstyle); + GnmHLink* hlink = mstyle_get_hlink (mstyle); + const guchar* hlink_target = NULL; + + if (hlink && IS_GNM_HLINK_URL (hlink)) { + hlink_target = gnm_hlink_get_target (hlink); + } if (version == HTML32 && hidden) gsf_output_puts (output, ""); @@ -161,6 +178,9 @@ gsf_output_puts (output, ""); } + if (hlink_target) + gsf_output_printf (output, "", hlink_target); + if (cell != NULL) { if (mstyle != NULL && version != HTML40) { html_get_text_color (cell, mstyle, &r, &g, &b); @@ -175,6 +195,8 @@ if (r > 0 || g > 0 || b > 0) gsf_output_puts (output, ""); + if (hlink_target) + gsf_output_puts (output, ""); if (mstyle != NULL) { if (font_is_monospaced (mstyle)) gsf_output_puts (output, ""); Index: plugins/mps/mps.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/mps/mps.c,v retrieving revision 1.46 retrieving revision 1.47 diff -u -w -r1.46 -r1.47 --- plugins/mps/mps.c 17 Jul 2004 03:11:37 -0000 1.46 +++ plugins/mps/mps.c 28 Jul 2004 19:35:55 -0000 1.47 @@ -654,6 +654,6 @@ } else mps_create_sheet (ctxt, wbv); mps_input_context_destroy (ctxt); - } else if (!gnumeric_io_error_occurred) + } else if (!gnumeric_io_error_occurred (io_context)) gnumeric_io_error_unknown (io_context); } Index: plugins/openoffice/openoffice-read.c =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/openoffice/openoffice-read.c,v retrieving revision 1.45 retrieving revision 1.46 diff -u -w -r1.45 -r1.46 --- plugins/openoffice/openoffice-read.c 13 Jun 2004 05:34:18 -0000 1.45 +++ plugins/openoffice/openoffice-read.c 28 Jul 2004 19:35:55 -0000 1.46 @@ -1071,8 +1071,8 @@ res->unknown_function_handler = gnm_func_placeholder_factory; res->ref_parser = oo_rangeref_parse; -#warning TODO : OO adds a 'mode' parm to floor/ceiling -#warning TODO : OO missing 'A1' parm for address +#warning "TODO : OO adds a 'mode' parm to floor/ceiling" +#warning "TODO : OO missing 'A1' parm for address" res->function_rewriter_hash = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (res->function_rewriter_hash, Index: plugins/paradox/.cvsignore =================================================================== RCS file: plugins/paradox/.cvsignore diff -N plugins/paradox/.cvsignore --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/paradox/.cvsignore 19 Jul 2004 13:18:49 -0000 1.1 @@ -0,0 +1,7 @@ +Makefile +Makefile.in +.deps +.libs +*.lo +*.la +plugin.xml Index: plugins/paradox/Makefile.am =================================================================== RCS file: plugins/paradox/Makefile.am diff -N plugins/paradox/Makefile.am --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/paradox/Makefile.am 19 Jul 2004 13:18:49 -0000 1.1 @@ -0,0 +1,21 @@ +INCLUDES = \ + -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + $(GNUMERIC_CFLAGS) \ + $(PARADOX_CFLAGS) + + +gnumeric_plugin_paradoxdir = $(gnumeric_plugindir)/paradox +xmldir = $(gnumeric_plugin_paradoxdir) +gnumeric_plugin_paradox_LTLIBRARIES = paradox.la +paradox_la_LDFLAGS = -module -avoid-version +paradox_la_SOURCES = px.h paradox.c +paradox_la_LIBADD = $(PARADOX_LIBS) + +xml_in_files = plugin.xml.in +xml_DATA = $(xml_in_files:.xml.in=.xml) + +@INTLTOOL_XML_RULE@ + +EXTRA_DIST = $(xml_in_files) +DISTCLEANFILES = $(xml_DATA) Index: plugins/paradox/paradox.c =================================================================== RCS file: plugins/paradox/paradox.c diff -N plugins/paradox/paradox.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/paradox/paradox.c 27 Jul 2004 05:19:03 -0000 1.2 @@ -0,0 +1,241 @@ +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/** + * boot.c: Paradox support for Gnumeric + * + * Author: + * Uwe Steinmann + **/ +#include +#include +#include +#include "px.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GNUMERIC_MODULE_PLUGIN_INFO_DECL; + +void paradox_file_open (GnmFileOpener const *fo, IOContext *io_context, + WorkbookView *wb_view, GsfInput *input); + +gboolean +paradox_file_probe (GnmFileOpener const *fo, GsfInput *input, + FileProbeLevel pl); + +static void *gn_malloc(pxdoc_t *p, size_t len, const char *caller) { + return((void *) g_malloc(len)); +} + +static void *gn_realloc(pxdoc_t *p, void *mem, size_t len, const char *caller) { + return((void *) g_realloc((gpointer) mem, len)); +} + +static void gn_free(pxdoc_t *p, void *ptr) { + g_free((gpointer) ptr); + ptr = NULL; +} + +static void gn_errorhandler(pxdoc_t *p, int error, const char *str, void *data) { + g_warning(str); +} + +void +paradox_file_open (GnmFileOpener const *fo, IOContext *io_context, + WorkbookView *wb_view, GsfInput *input) +{ + Workbook *wb; + pxdoc_t *pxdoc; + pxhead_t *pxh; + pxfield_t *pxf; + char *data; + char *name; + Sheet *sheet = NULL; + GnmCell *cell; + GnmValue *val = NULL; + ErrorInfo *open_error = NULL; + guint row, i, j, offset; + + pxdoc = PX_new2(gn_errorhandler, gn_malloc, gn_realloc, gn_free); + if (PX_open_gsf (pxdoc, input) < 0) { + gnumeric_io_error_info_set (io_context, error_info_new_str_with_details ( + _("Error while opening paradox file."), + open_error)); + return; + } + pxh = pxdoc->px_head; + + fprintf(stderr, "Paradox %2.1f file, %d records, %d columns\n", pxh->px_fileversion/10.0, pxh->px_numrecords, pxh->px_numfields); + + PX_set_targetencoding(pxdoc, "UTF-8"); + + wb = wb_view_workbook (wb_view); + name = workbook_sheet_get_free_name (wb, pxh->px_tablename, FALSE, TRUE); + sheet = sheet_new (wb, name); + g_free (name); + workbook_sheet_attach (wb, sheet, NULL); + + pxf = pxh->px_fields; + for (i = 0 ; i < (guint) pxh->px_numfields; i++) { + char str[30]; + cell = sheet_cell_fetch (sheet, i, 0); + snprintf (str, 30, "%s,%d,%d", pxf->px_fname, pxf->px_ftype, pxf->px_flen); + cell_set_text (cell, str); + pxf++; + } + { + GnmRange r; + GnmStyle *bold = mstyle_new (); + mstyle_set_font_bold (bold, TRUE); + sheet_style_apply_range (sheet, + range_init (&r, 0, 0, pxh->px_numfields-1, 0), bold); + } + + if((data = (char *) pxdoc->malloc(pxdoc, pxh->px_recordsize, _("Could not allocate memory for record."))) == NULL) { + gnumeric_io_error_info_set (io_context, error_info_new_str_with_details ( + _("Error while opening paradox file."), + open_error)); + return; + } + row = 1; + for(j = 0; j < (guint)pxh->px_numrecords; j++) { + pxdatablockinfo_t pxdbinfo; + int isdeleted = 0; + if(NULL != PX_get_record2(pxdoc, j, data, &isdeleted, &pxdbinfo)) { + offset = 0; + pxf = pxh->px_fields; + for (i = 0; i < (guint) pxh->px_numfields ; i++) { + cell = sheet_cell_fetch (sheet, i, row); + val = NULL; + switch(pxf->px_ftype) { + case pxfAlpha: { + char *value; + if(0 < PX_get_data_alpha(pxdoc, &data[offset], pxf->px_flen, &value)) { + val = value_new_string_nocopy (value); +// value_set_fmt (val, field->fmt); + } + break; + } + case pxfShort: { + short int value; + if(0 < PX_get_data_short(pxdoc, &data[offset], pxf->px_flen, &value)) { + val = value_new_int (value); + } + break; + } + case pxfAutoInc: + case pxfTimestamp: + case pxfLong: { + long value; + if(0 < PX_get_data_long(pxdoc, &data[offset], pxf->px_flen, &value)) { + val = value_new_int (value); + } + break; + } + case pxfCurrency: + case pxfNumber: { + double value; + if(0 < PX_get_data_double(pxdoc, &data[offset], pxf->px_flen, &value)) { + val = value_new_float (value); + } + break; + } + case pxfLogical: { + char value; + if(0 < PX_get_data_byte(pxdoc, &data[offset], pxf->px_flen, &value)) { + val = value_new_bool (value ? TRUE : FALSE); + } + break; + } + case pxfDate: { + long value; + int year, month, day; + if(0 < PX_get_data_long(pxdoc, &data[offset], pxf->px_flen, &value)) { + PX_SdnToGregorian(value+1721425, &year, &month, &day); + GDate *date = g_date_new_dmy (day, month, year); + val = value_new_int (datetime_g_to_serial (date, NULL)); + g_date_free (date); + } + break; + } + case pxfBCD: { + char *value; + if(0 < PX_get_data_bcd(pxdoc, &data[offset], pxf->px_fdc, &value)) { + val = value_new_string_nocopy (value); + } + break; + } + default: { + val = value_new_string_nocopy ( + g_strdup_printf (_("Field type %d is not unsupported."), pxf->px_ftype)); + } + } + if(val) + cell_set_value (cell, val); + offset += pxf->px_flen; + pxf++; + } + if(pxh->px_filetype == pxfFileTypPrimIndex) { + short int value; + cell = sheet_cell_fetch (sheet, i++, row); + if(0 < PX_get_data_short(pxdoc, &data[offset], 2, &value)) { + val = value_new_int (value); + cell_set_value (cell, val); + } + offset += 2; + cell = sheet_cell_fetch (sheet, i++, row); + if(0 < PX_get_data_short(pxdoc, &data[offset], 2, &value)) { + val = value_new_int (value); + cell_set_value (cell, val); + } + offset += 2; + cell = sheet_cell_fetch (sheet, i++, row); + if(0 < PX_get_data_short(pxdoc, &data[offset], 2, &value)) { + val = value_new_int (value); + cell_set_value (cell, val); + } + cell = sheet_cell_fetch (sheet, i++, row); + val = value_new_int (pxdbinfo.number); + cell_set_value (cell, val); + } + } + row++; + } + pxdoc->free(pxdoc, data); + + PX_close (pxdoc); + PX_delete (pxdoc); + + sheet_flag_recompute_spans (sheet); +} + +gboolean +paradox_file_probe (GnmFileOpener const *fo, GsfInput *input, + FileProbeLevel pl) +{ + pxdoc_t *pxdoc; + pxhead_t *pxh; + + pxdoc = PX_new(); + if (PX_open_gsf (pxdoc, input) < 0) { + return false; + } + + pxh = pxdoc->px_head; + + PX_close (pxdoc); + PX_delete (pxdoc); + + return true; +} + Index: plugins/paradox/plugin.xml.in =================================================================== RCS file: plugins/paradox/plugin.xml.in diff -N plugins/paradox/plugin.xml.in --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/paradox/plugin.xml.in 19 Jul 2004 13:18:49 -0000 1.1 @@ -0,0 +1,21 @@ + + + + <_name>Paradox + <_description>Imports Paradox files + + + + + + + + <_description>Paradox database or primary index file + + + + + + + + Index: plugins/paradox/px.h =================================================================== RCS file: plugins/paradox/px.h diff -N plugins/paradox/px.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/paradox/px.h 19 Jul 2004 13:18:49 -0000 1.1 @@ -0,0 +1,8 @@ +#ifndef GNUMERIC_PLUGIN_PARADOX_PARADOX_H +#define GNUMERIC_PLUGIN_PARADOX_PARADOX_H + +#include +#include +#include + +#endif Index: plugins/xml_sax/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/xml_sax/ChangeLog,v retrieving revision 1.131 retrieving revision 1.132 diff -u -w -r1.131 -r1.132 --- plugins/xml_sax/ChangeLog 19 Jul 2004 12:40:16 -0000 1.131 +++ plugins/xml_sax/ChangeLog 19 Jul 2004 13:18:50 -0000 1.132 @@ -1,5 +1,9 @@ 2004-07-19 Jody Goldberg + * plugin.xml.in : Make the sax exporter the default + +2004-07-19 Jody Goldberg + * Release 1.3.1 2004-06-05 Jody Goldberg Index: plugins/xml_sax/plugin.xml.in =================================================================== RCS file: /cvs/gnome/gnumeric/plugins/xml_sax/plugin.xml.in,v retrieving revision 1.13 retrieving revision 1.14 diff -u -w -r1.13 -r1.14 --- plugins/xml_sax/plugin.xml.in 5 Jun 2004 16:19:34 -0000 1.13 +++ plugins/xml_sax/plugin.xml.in 19 Jul 2004 13:18:50 -0000 1.14 @@ -14,9 +14,9 @@ + format_level="auto" default_saver_priority="50"> - <_description>EXPERIMENTAL SAX based Gnumeric (*.gnumeric) + <_description>Gnumeric XML (*.gnumeric) -This is the lp_solve 3.2 release - -This lp_solve versions is released under the LGPL license. See LGPL.txt - -bug reports, succes stories and requests for changes are welcome at: - -Michel Berkelaar -michel@ics.ele.tue.nl - -Most of the changes between 1.5 and 2.0 were contributed by: - -Jeroen Dirks -jeroend@tor.numetrix.com - -The lastest version of lp_solve can always be found at: - -ftp://ftp.ics.ele.tue.nl/pub/lp_solve/ - -BUILDING LP_SOLVE - -Just edit the Makefile to pick the compiler, lex and yacc of your choice, and -to set the desired compiler options. Then type 'make'. After a succesful make, -type 'make test' to run a couple of examples and to test the results against -the ones I got. Also run program 'demo' to test the procedural interface. - +July 21, 2004 +This is the release version of lp_solve 5.0 +------------------------------------------- +The engine of this new version has changed fundamentally (thanks to Kjell Eikland). The result +is more speed and more stability, which means that larger and tougher models can be solved. +The file and module structure of lp_solve is now also a very much-improved platform for further +development. Check lp_solve.chm, or the HTML help files for a fuller description changes since +version 4.0. Begin by taking a look at 'Changes compared to version 4' and 'lp_solve usage' +This gives a good starting point. + +lp_solve v5 is herewith released. It is a major enhancement compared to v4 in +terms of functionality, usability, accessibility and documentation. While the +new core v5 engine has been tested for 6 months, including a beta phase of 3 months, +there are areas of the code that contain placeholders for coming functionality +and can therefore not be expected to be verified. This includes partial and +multiple pricing and, to a lesser extent, Steepest Edge-based pricing. In +addition, the development team is aware of issues related to particulare settings +that in some cases can make worst-case performance unsatisfactory. + +For example, the default tolerance settings are known to be very thight compared +to many other solvers. This can in a few very rare cases lead to excessive +running times or that lp_solve determines the model to be infeasible. We have +chosen to keep the tight settings since in many cases we have found that other +solvers produce solutions even if the underlying solution quality is unacceptable +or strictly considered even infeasible. We may, however, decide to selectively +loosen tolerances in a v5 subrelease if we feel that this is warranted. + + +BFP stands for Basis Factorization Package, which is a unique lp_solve feature. Considerable +effort has been put in this new feature and we have big expectations for this. BFP is a generic +interface model and users can develop their own implementations based on the provided templates. +We are very interested in providing as many different BFPs as possible to the community. + +lp_solve has the v3.2 etaPFI built in as default engine, versioned 1.0, In addition two other +BFPs are included for both Windows and Linux: bfp_LUSOL.dll, bfp_etaPFI.dll for Windows and +libbfp_LUSOL.so, libbfp_etaPFI.so for Linux. The standalone bfp_etaPFI is v2.0 and includes +advanced column ordering using the COLAMD library, as well as better pivot management for +stability. For complex models, however, the LU factorization approach is much better, and +lp_solve now includes LUSOL as one of the most stable engines available anywhere. LUSOL was +originally developed by Prof. Saunders at Stanford, and it has now been ported to C +and enhanced by Kjell. It is expected that the LUSOL source will be made available. + +Note that for the time being only bfp_etaPFI can be recompiled by you using the included source. +A third BFP based on GLPK may be included later, but license issues must first be resolved. In +the interim, you may experiment with the working sample BFP interface files for GLPK. + +If you compile BFPs yourself, make sure that under Windows, you use __stdcall convention and +use 4 byte alignments. This is needed for the BFPs to work correctly with the general +distribution of lp_solve and also to make sharing BFPs as uncomplicated as possible. + +Don't forget that the API has changed compared to previous versions of lpsolve and that you just +can't use the version 5 lpsolve library with your version 4 or older code. That is also the +reason why the library is now called lpsolve5.dll/lpsolve5.a. lpsolve5.dll or lpsolve5.a are +only needed when you call the lpsolve library via the API interface from your program. +The lp_solve program is still a stand-alone executable program. + +There are examples interfaces for different language like C, VB, C#, VB.NET, JAVA (Windows only), +Delphi, and there is now also even a COM object to access the lpsolve library. This means that +the door is wide-open for using lp_solve in many different situations. Thus everything that is +available in version 4 is now also available in version 5 and already much more! + +Note that lp2mps and mps2lp don't exist anymore. However this functionality is now implemented +in lp_solve: + +lp2mps can be simulated as following: +lp_solve -parse_only -lp infile -wmps outfile + +mps2lp can be simulated as following: +lp_solve -parse_only -mps infile -wlp outfile + + +Change history: + +??/??/?? version incorrect, probably 5.0.0.0 +- Some small bugs are solved +- print_lp reported wrong information on ranges on constraints +- A new function print_tableau is added +- The demo program failed at some points. Fixed +- Solving time is improved again. Calculation of sensitivity is only done when asked for. + +01/05/04 version incorrect, probably 5.0.0.0 +- Some compiler warnings solved +- New API function print_str +- Some functions were not exported in the lpsolve5 dll. Fixed. + +02/05/04 version incorrect, probably 5.0.0.0 +- Solved some small bugs +- New API routine is_maxim added. +- New API routine is_constr_type added. +- New API routine is_piv_rule added. +- Routine get_reduced_costs renamed to get_dual_solution. +- Routine get_ptr_reduced_costs renamed to get_ptr_dual_solution. +- Return codes of solve API call are changed. +- ANTIDEGEN codes have changed. See set_anti_degen. + +08/05/04 version 5.0.4.0 +- Routine write_debugdump renamed to print_debugdump +- Routine set_scalemode renamed to set_scaling +- Routine get_scalemode renamed to get_scaling +- New API routine set_add_rowmode added +- New API routine is_add_rowmode added +- Fix of a B&B subperformance issue +- File lp_lp.h renamed to lp_wlp.h +- File lp_lp.c renamed to lp_wlp.c +- File lp_lpt.h renamed to lp_rlpt.h +- File lp_lpt.c renamed to lp_rlpt.c +- File read.h renamed to yacc_read.h +- File read.c renamed to yacc_read.c +- File lex.l renamed to lp_rlp.l +- File lp.y renamed to lp_rlp.y +- File lex.c renamed to lp_rlp.h +- File lp.c renamed to lp_rlp.c + +If you build your model via the routine add_constraint, then take a look at the +new routine set_add_rowmode. It will result in a spectacular performance improvement +to build your model. + +17/05/04 version 5.0.5.0 +- Reading from lp and mps file is considerably made faster. Especially for larger models + The improvement can be spectacular. + Also writing lp and mps files are made faster. +- Improvements in B&B routines. Big models take less memory. Can be up to 50% +- Under Windows, the byte alignment is changed to 4 (before it was 1 in version 5). + This results in some speed improvements. Test gave some 5% improvement. +- is_anti_degen did not return the correct status with ANTIDEGEN_NONE. Fixed. +- is_presolve did not return the correct status with PRESOLVE_NONE. Fixed. +- Under non-windows system, BFP_CALLMODEL doesn't have to be specified anymore + +25/05/04 version 5.0.6.0 +- lp_solve5.zip now also contains the needed header files to build a C/C++ application. + The source files that use the lpsolve API functions must now include lp_lib.h + lpkit.h is obsolete. It is only used internally in the lpsolve library. + Developers using the lpsolve library must use lp_lib.h + The examples are changed accordingly. +- set_piv_rule renamed to set_pivoting +- get_piv_rule renamed to get_pivoting +- is_piv_strategy renamed to is_piv_mode +- Constants INFEASIBLE, UNBOUNDED, DEGENERATE, NUMFAILURE have new values +- New solve return value: SUBOPTIMAL +- When there are negative bounds on variables such that these variables must be split in + a negative and a positive part and solve was called multiple time, then each time the solve + was done, a new split was done. This could lead to bad performance. Corrected. +- API function get_row is now considerably faster, especially for large models. +- When compiled with VC, some warnings were given about structure alignment. Solved. +- Creation of an mps file (via write_mps) sometimes created numbers with the wrong sign. + This problem was introduced in 5.0.5.0. Corrected. +- In several source files there were improper /* */ comments. Corrected. +- Several improvements/corrections in the algorithms. +- Some corrections in writing the model to an lp/lpt file. + +29/05/04 version 5.0.7.0 +- A small error corrected in the lp parser. If there was a constraint with both a lower + and upper restriction on the same line and with only one variable, then the parser did + not accept this. For example: + R1: 1 <= x <= 2; + This is now corrected. + Note that normally it is preferred to make of this a bound instead of a constraint: + 1 <= x <= 2; + This results in a smaller model (less constraints) and faster solution times. + By adding the label: prefix it is forced as a constraint. This can be for testing purposes + of in some special circumstances. +- Return values in case of user abort and timeout is now cleaner, and makes it possible to recover + sub optimal values directly (if the problem was feasible).. +- calculate_duals and sensitivity duals could not be constructed in the presence of free variables + when these were deleted. The deletion / cleanup of free variables was enforced in the previous beta. +- Added constant PRESOLVE_DUALS so that the program precalculates duals, and always if there are free + variables. The PRESOLVE_SENSDUALS are only calculated if the user has explicitly asked for it, even + if there are split variables. The only way to get around this problem is to convert the simplex + algorithm to a general bounded version, which is a v6 issue. +- Presolve is accelerated significantly for large models with many row and column removals. + This is done by moving the basis definition logic to after the presolve logic. +- Factorization efficiency statistics were sometimes computed incorrectly with non-etaPFI BFPs. + This is now fixed without any effect on the user. +- Some minor tuning of bfp_LUSOL has been done. There will be more coming in this department later. +- Routines set_add_rowmode and is_add_rowmode were not available in the lpsolve5.dll dll. Corrected. +- Constant PRESOLVE_SENSDUALS has new value. Before it was 128. Now it is 256. + +04/06/04 version 5.0.8.0 +- There was an error in the CPLEX lp parser. When there is a constraint (Subject To section) with only + one variable (like -C1 >= -99900), the file could not be read. Fixed. + Note that it is better to have bounds on this kind of constraints. (C1 <= 99900). This gives + better performance. On the other hand, presolve detects these conditions and auto converts them + to bounds. +- Under Windows, byte alignment is now set to 8 (previously it was 4). Gives some better performance. +- There were a couple of problems when a restart was done. lp_solve failed, gave numerical unstable + errors, NUMFAILURE return code. + Also when the objective function was changed after a solve and solve was done again, lp_solve + stated that the model was infeasible. + Also when the objective function or an element in the matrix was changed and a previous non-zero value + was set to zero, the matrix was blown up with unpredictable effects. Even an unhandled exception error/ + core dump could be the result of this problem. + These problems should be solved now. +- There was a potential problem in the MPS reader. When column entries were not in ascending order, + the model could not be read (add_columnex error message). + Note that lp_solve always generates column entries in ascending order. So the problem only + occurred when the MPS file was created outside of lp_solve. +- add_columnex did not return FALSE if column entries were not in ascending order. +- Added the option -S7 to the lp_solve program to also print the tableau +- Added a functional index in the lp_solve API reference. API calls are grouped per functionality. + This can be a great help to find which API routines are needed at which time. +- Added in the manual a section about Basis Factorization Packages (BFPs) + +08/06/04 version 5.0.8.1 +- There was a memory leak when solve was called multiple times after each other. Fixed. + +18/06/04 version 5.0.9.0 +- There was a possible memory overrun in some specific cases. This could result in protection + errors and crashes. +- The default branch-and-bound depth limit is now set to 50 (get_bb_depthlimit) +- There are two new API functions: add_constraintex and set_obj_fnex +- On some systems, the macro CHAR_BIT is already defined and the compiler gave a warning about it. Fixed. +- variable vector is renamed. In some situations this confliced with a macro definition. +- Before, version 5 had to be compiled with __stdcall calling convention. This is no longer + required. Any calling convention is now ok. +- Big coefficients (>2147483647) in the matrix were handled incorrectly resulting in totally + wrong results. Corrected. +- column_in_lp now returns an integer indicating the column number where it is found in the lp + If the column is not in the lp it returns 0. +- get_orig_index and get_lp_index returned wrong values if the functions were used before solve. + Fixed. +- New routines added: add_constraintex and set_obj_fnex +- New constants PRICE_MULTIPLE and PRICE_AUTOPARTIAL added for set_pivoting/get_pivoting/is_piv_mode + added new options -pivm and -pivp to lp_solve program to support these new constants. +- New routines read_XLI, write_XLI, has_XLI, is_nativeXLI added to support user written + model language interfaces. Not yet documented in the manual. Here is a short explanation: + As you know, lp_solve supports reading and writing of lp, mps and CPLEX lp style model files. + In addition, lp_solve is interfaced with other systems via LIB code link-ins or the DLL, + but this has never been an officially supported part of lp_solve. + + To facilitate the development of other model language interfaces, a quite + simple "eXternal Language Interface" (XLI) has been defined, which allows + lp_solve to be dynamically configured (at run-time) to use alternative XLIs. + At present, a MathProg (AMPL subset) interface is in place, and the template + is provided for other platforms. An XML-based interface is likely to come + fairly soon also. Obviously, all existing interface methods will remain + unchanged. + +21/07/04 version 5.0.10.0 +- This is the first official release of version 5!!! +- The default branch-and-bound depth limit is not 50 as specified in 5.0.9.0, but -50 + This indicates a relative depth limit. See the docs. +- The callback functions were not working properly because they didn't have the __stdcall + attributes. Fixed. Also modified the VB, VB.NET, CS.NET examples to demonstrate the + callback features. +- lp_colamdMDO.c and lp_colamdMDO.h renamed to lp_MDO.c and lp_MDO.h +- lp_pricerPSE.c and lp_pricerPSE.h renamed to lp_price.h/lp_pricePSE.h and lp_price.c/lp_pricePSE.c +- New routines read_freemps, read_freeMPS, write_freemps, write_freeMPS for reading/writing + free MPS format. +- Added options -mfps, -wfmps to lp_solve command line program to handle free MPS files +- There was an error in set_obj_fnex when not in row add mode such that the object function + was not set correct. Fixed. +- read_XLI, write_XLI now has an extra parameter: options +- Revised default_basis, set_basis to be more robust for wrong provided data. + set_basis now returns a boolean indicating success of not. +- several internal improvements +- New pricer strategies constants: PRICE_PARTIAL, PRICE_LOOPLEFT, PRICE_LOOPALTERNATE. +- PRICE_ADAPTIVE, PRICE_RANDOMIZE have new values. +- All API functions are now available via the lp structure. This is needed for the XLIs. +- MathProg XLI now uses lp_solves verbose level to print messages. +- MathProg XLI fix for constant term in constraint. +- Fixed possible memory overrun in report function. +- added options -rxli, -rxlidata, -rxliopt to lp_solve command line program to read with XLI library +- added options -wxli, -wxliopt to lp_solve command line program to write with XLI library +- write_lpt, write_LPT now writes to the output set by set_outputstream/set_outputfile when NULL is + given as filename, stream. Previously output was then written to stdout. +- set_break_at_value (-o option in lp_solve program) didn't work. Fixed. +- The lp format now also allows constant terms in the objective function. + This value is added with the objective function value. + This constant was already allowed by the MPS format and is also written when an lp file + is created (see lp-format in help file). +- Fixed a runtime/protection/core dump error when a timeout occured. + +We are thrilled to hear from you and your experiences with this new version. The good and the bad. +Also we would be pleased to hear about your experiences with the different BFPs on your models. + +Please send reactions to: +Peter Notebaert: peno@mailme.org +Kjell Eikland: kjell.eikland@broadpark.no Index: src/tools/solver/lp_solve/colamd.c =================================================================== RCS file: src/tools/solver/lp_solve/colamd.c diff -N src/tools/solver/lp_solve/colamd.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/colamd.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,3469 @@ +/* ========================================================================== */ +/* === colamd/symamd - a sparse matrix column ordering algorithm ============ */ +/* ========================================================================== */ + +/* + colamd: an approximate minimum degree column ordering algorithm, + for LU factorization of symmetric or unsymmetric matrices, + QR factorization, least squares, interior point methods for + linear programming problems, and other related problems. + + symamd: an approximate minimum degree ordering algorithm for Cholesky + factorization of symmetric matrices. + + Purpose: + + Colamd computes a permutation Q such that the Cholesky factorization of + (AQ)'(AQ) has less fill-in and requires fewer floating point operations + than A'A. This also provides a good ordering for sparse partial + pivoting methods, P(AQ) = LU, where Q is computed prior to numerical + factorization, and P is computed during numerical factorization via + conventional partial pivoting with row interchanges. Colamd is the + column ordering method used in SuperLU, part of the ScaLAPACK library. + It is also available as built-in function in Matlab Version 6, + available from MathWorks, Inc. (http://www.mathworks.com). This + routine can be used in place of colmmd in Matlab. By default, the \ + and / operators in Matlab perform a column ordering (using colmmd + or colamd) prior to LU factorization using sparse partial pivoting, + in the built-in Matlab lu(A) routine. + + Symamd computes a permutation P of a symmetric matrix A such that the + Cholesky factorization of PAP' has less fill-in and requires fewer + floating point operations than A. Symamd constructs a matrix M such + that M'M has the same nonzero pattern of A, and then orders the columns + of M using colmmd. The column ordering of M is then returned as the + row and column ordering P of A. + + Authors: + + The authors of the code itself are Stefan I. Larimore and Timothy A. + Davis (davis@cise.ufl.edu), University of Florida. The algorithm was + developed in collaboration with John Gilbert, Xerox PARC, and Esmond + Ng, Oak Ridge National Laboratory. + + Date: + + May 4, 2001. Version 2.1. + + Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974 and DMS-9803599. + + Notice: + + Copyright (c) 1998-2001 by the University of Florida. + All Rights Reserved. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY + EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to use or copy this program for any + purpose, provided the above notices are retained on all copies. + User documentation of any code that uses this code must cite the + Authors, the Copyright, and "Used by permission." If this code is + accessible from within Matlab, then typing "help colamd" and "help + symamd" must cite the Authors. Permission to modify the code and to + distribute modified code is granted, provided the above notices are + retained, and a notice that the code was modified is included with the + above copyright notice. You must also retain the Availability + information below, of the original version. + + This software is provided free of charge. + + Availability: + + The colamd/symamd library is available at + + http://www.cise.ufl.edu/research/sparse/colamd/ + + This is the http://www.cise.ufl.edu/research/sparse/colamd/colamd.c + file. It requires the colamd.h file. It is required by the colamdmex.c + and symamdmex.c files, for the Matlab interface to colamd and symamd. + + Changes to the colamd library since Version 1.0 and 1.1: + + No bugs were found in version 1.1. These changes merely add new + functionality. + + * added the COLAMD_RECOMMENDED (nnz, n_row, n_col) macro. + + * moved the output statistics, from A, to a separate output argument. + The arguments changed for the C-callable routines. + + * added colamd_report and symamd_report. + + * added a C-callable symamd routine. Formerly, symamd was only + available as a mexFunction from Matlab. + + * added error-checking to symamd. Formerly, it assumed its input + was error-free. + + * added the optional stats and knobs arguments to the symamd mexFunction + + * deleted colamd_help. A help message is still available from + "help colamd" and "help symamd" in Matlab. + + * deleted colamdtree.m and symamdtree.m. Now, colamd.m and symamd.m + also do the elimination tree post-ordering. The Version 1.1 + colamd and symamd mexFunctions, which do not do the post- + ordering, are now visible as colamdmex and symamdmex from + Matlab. Essentialy, the post-ordering is now the default + behavior of colamd.m and symamd.m, to match the behavior of + colmmd and symmmd. The post-ordering is only available in the + Matlab interface, not the C-callable interface. + + * made a slight change to the dense row/column detection in symamd, + to match the stated specifications. + + Changes from Version 2.0 to 2.1: + + * TRUE and FALSE are predefined on some systems, so they are defined + here only if not already defined. + + * web site changed + + * UNIX Makefile modified, to handle the case if "." is not in your path. + +*/ + +/* ========================================================================== */ +/* === Description of user-callable routines ================================ */ +/* ========================================================================== */ + +/* + ---------------------------------------------------------------------------- + colamd_recommended: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + int colamd_recommended (int nnz, int n_row, int n_col) ; + + or as a C macro + + #include "colamd.h" + Alen = COLAMD_RECOMMENDED (int nnz, int n_row, int n_col) ; + + Purpose: + + Returns recommended value of Alen for use by colamd. Returns -1 + if any input argument is negative. The use of this routine + or macro is optional. Note that the macro uses its arguments + more than once, so be careful for side effects, if you pass + expressions as arguments to COLAMD_RECOMMENDED. Not needed for + symamd, which dynamically allocates its own memory. + + Arguments (all input arguments): + + int nnz ; Number of nonzeros in the matrix A. This must + be the same value as p [n_col] in the call to + colamd - otherwise you will get a wrong value + of the recommended memory to use. + + int n_row ; Number of rows in the matrix A. + + int n_col ; Number of columns in the matrix A. + + ---------------------------------------------------------------------------- + colamd_set_defaults: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + colamd_set_defaults (int knobs [COLAMD_KNOBS]) ; + + Purpose: + + Sets the default parameters. The use of this routine is optional. + + Arguments: + + double knobs [COLAMD_KNOBS] ; Output only. + + Colamd: rows with more than (knobs [COLAMD_DENSE_ROW] * n_col) + entries are removed prior to ordering. Columns with more than + (knobs [COLAMD_DENSE_COL] * n_row) entries are removed prior to + ordering, and placed last in the output column ordering. + + Symamd: uses only knobs [COLAMD_DENSE_ROW], which is knobs [0]. + Rows and columns with more than (knobs [COLAMD_DENSE_ROW] * n) + entries are removed prior to ordering, and placed last in the + output ordering. + + COLAMD_DENSE_ROW and COLAMD_DENSE_COL are defined as 0 and 1, + respectively, in colamd.h. Default values of these two knobs + are both 0.5. Currently, only knobs [0] and knobs [1] are + used, but future versions may use more knobs. If so, they will + be properly set to their defaults by the future version of + colamd_set_defaults, so that the code that calls colamd will + not need to change, assuming that you either use + colamd_set_defaults, or pass a (double *) NULL pointer as the + knobs array to colamd or symamd. + + ---------------------------------------------------------------------------- + colamd: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + int colamd (int n_row, int n_col, int Alen, int *A, int *p, + double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS]) ; + + Purpose: + + Computes a column ordering (Q) of A such that P(AQ)=LU or + (AQ)'AQ=LL' have less fill-in and require fewer floating point + operations than factorizing the unpermuted matrix A or A'A, + respectively. + + Returns: + + TRUE (1) if successful, FALSE (0) otherwise. + + Arguments: + + int n_row ; Input argument. + + Number of rows in the matrix A. + Restriction: n_row >= 0. + Colamd returns FALSE if n_row is negative. + + int n_col ; Input argument. + + Number of columns in the matrix A. + Restriction: n_col >= 0. + Colamd returns FALSE if n_col is negative. + + int Alen ; Input argument. + + Restriction (see note): + Alen >= 2*nnz + 6*(n_col+1) + 4*(n_row+1) + n_col + Colamd returns FALSE if these conditions are not met. + + Note: this restriction makes an modest assumption regarding + the size of the two typedef's structures in colamd.h. + We do, however, guarantee that + + Alen >= colamd_recommended (nnz, n_row, n_col) + + or equivalently as a C preprocessor macro: + + Alen >= COLAMD_RECOMMENDED (nnz, n_row, n_col) + + will be sufficient. + + int A [Alen] ; Input argument, undefined on output. + + A is an integer array of size Alen. Alen must be at least as + large as the bare minimum value given above, but this is very + low, and can result in excessive run time. For best + performance, we recommend that Alen be greater than or equal to + colamd_recommended (nnz, n_row, n_col), which adds + nnz/5 to the bare minimum value given above. + + On input, the row indices of the entries in column c of the + matrix are held in A [(p [c]) ... (p [c+1]-1)]. The row indices + in a given column c need not be in ascending order, and + duplicate row indices may be be present. However, colamd will + work a little faster if both of these conditions are met + (Colamd puts the matrix into this format, if it finds that the + the conditions are not met). + + The matrix is 0-based. That is, rows are in the range 0 to + n_row-1, and columns are in the range 0 to n_col-1. Colamd + returns FALSE if any row index is out of range. + + The contents of A are modified during ordering, and are + undefined on output. + + int p [n_col+1] ; Both input and output argument. + + p is an integer array of size n_col+1. On input, it holds the + "pointers" for the column form of the matrix A. Column c of + the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first + entry, p [0], must be zero, and p [c] <= p [c+1] must hold + for all c in the range 0 to n_col-1. The value p [n_col] is + thus the total number of entries in the pattern of the matrix A. + Colamd returns FALSE if these conditions are not met. + + On output, if colamd returns TRUE, the array p holds the column + permutation (Q, for P(AQ)=LU or (AQ)'(AQ)=LL'), where p [0] is + the first column index in the new ordering, and p [n_col-1] is + the last. That is, p [k] = j means that column j of A is the + kth pivot column, in AQ, where k is in the range 0 to n_col-1 + (p [0] = j means that column j of A is the first column in AQ). + + If colamd returns FALSE, then no permutation is returned, and + p is undefined on output. + + double knobs [COLAMD_KNOBS] ; Input argument. + + See colamd_set_defaults for a description. + + int stats [COLAMD_STATS] ; Output argument. + + Statistics on the ordering, and error status. + See colamd.h for related definitions. + Colamd returns FALSE if stats is not present. + + stats [0]: number of dense or empty rows ignored. + + stats [1]: number of dense or empty columns ignored (and + ordered last in the output permutation p) + Note that a row can become "empty" if it + contains only "dense" and/or "empty" columns, + and similarly a column can become "empty" if it + only contains "dense" and/or "empty" rows. + + stats [2]: number of garbage collections performed. + This can be excessively high if Alen is close + to the minimum required value. + + stats [3]: status code. < 0 is an error code. + > 1 is a warning or notice. + + 0 OK. Each column of the input matrix contained + row indices in increasing order, with no + duplicates. + + 1 OK, but columns of input matrix were jumbled + (unsorted columns or duplicate entries). Colamd + had to do some extra work to sort the matrix + first and remove duplicate entries, but it + still was able to return a valid permutation + (return value of colamd was TRUE). + + stats [4]: highest numbered column that + is unsorted or has duplicate + entries. + stats [5]: last seen duplicate or + unsorted row index. + stats [6]: number of duplicate or + unsorted row indices. + + -1 A is a null pointer + + -2 p is a null pointer + + -3 n_row is negative + + stats [4]: n_row + + -4 n_col is negative + + stats [4]: n_col + + -5 number of nonzeros in matrix is negative + + stats [4]: number of nonzeros, p [n_col] + + -6 p [0] is nonzero + + stats [4]: p [0] + + -7 A is too small + + stats [4]: required size + stats [5]: actual size (Alen) + + -8 a column has a negative number of entries + + stats [4]: column with < 0 entries + stats [5]: number of entries in col + + -9 a row index is out of bounds + + stats [4]: column with bad row index + stats [5]: bad row index + stats [6]: n_row, # of rows of matrx + + -10 (unused; see symamd.c) + + -999 (unused; see symamd.c) + + Future versions may return more statistics in the stats array. + + Example: + + See http://www.cise.ufl.edu/research/sparse/colamd/example.c + for a complete example. + + To order the columns of a 5-by-4 matrix with 11 nonzero entries in + the following nonzero pattern + + x 0 x 0 + x 0 x x + 0 x x 0 + 0 0 x x + x x 0 0 + + with default knobs and no output statistics, do the following: + + #include "colamd.h" + #define ALEN COLAMD_RECOMMENDED (11, 5, 4) + int A [ALEN] = {1, 2, 5, 3, 5, 1, 2, 3, 4, 2, 4} ; + int p [ ] = {0, 3, 5, 9, 11} ; + int stats [COLAMD_STATS] ; + colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ; + + The permutation is returned in the array p, and A is destroyed. + + ---------------------------------------------------------------------------- + symamd: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + int symamd (int n, int *A, int *p, int *perm, + int knobs [COLAMD_KNOBS], int stats [COLAMD_STATS], + void (*allocate) (size_t, size_t), void (*release) (void *)) ; + + Purpose: + + The symamd routine computes an ordering P of a symmetric sparse + matrix A such that the Cholesky factorization PAP' = LL' remains + sparse. It is based on a column ordering of a matrix M constructed + so that the nonzero pattern of M'M is the same as A. The matrix A + is assumed to be symmetric; only the strictly lower triangular part + is accessed. You must pass your selected memory allocator (usually + calloc/free or mxCalloc/mxFree) to symamd, for it to allocate + memory for the temporary matrix M. + + Returns: + + TRUE (1) if successful, FALSE (0) otherwise. + + Arguments: + + int n ; Input argument. + + Number of rows and columns in the symmetrix matrix A. + Restriction: n >= 0. + Symamd returns FALSE if n is negative. + + int A [nnz] ; Input argument. + + A is an integer array of size nnz, where nnz = p [n]. + + The row indices of the entries in column c of the matrix are + held in A [(p [c]) ... (p [c+1]-1)]. The row indices in a + given column c need not be in ascending order, and duplicate + row indices may be present. However, symamd will run faster + if the columns are in sorted order with no duplicate entries. + + The matrix is 0-based. That is, rows are in the range 0 to + n-1, and columns are in the range 0 to n-1. Symamd + returns FALSE if any row index is out of range. + + The contents of A are not modified. + + int p [n+1] ; Input argument. + + p is an integer array of size n+1. On input, it holds the + "pointers" for the column form of the matrix A. Column c of + the matrix A is held in A [(p [c]) ... (p [c+1]-1)]. The first + entry, p [0], must be zero, and p [c] <= p [c+1] must hold + for all c in the range 0 to n-1. The value p [n] is + thus the total number of entries in the pattern of the matrix A. + Symamd returns FALSE if these conditions are not met. + + The contents of p are not modified. + + int perm [n+1] ; Output argument. + + On output, if symamd returns TRUE, the array perm holds the + permutation P, where perm [0] is the first index in the new + ordering, and perm [n-1] is the last. That is, perm [k] = j + means that row and column j of A is the kth column in PAP', + where k is in the range 0 to n-1 (perm [0] = j means + that row and column j of A are the first row and column in + PAP'). The array is used as a workspace during the ordering, + which is why it must be of length n+1, not just n. + + double knobs [COLAMD_KNOBS] ; Input argument. + + See colamd_set_defaults for a description. + + int stats [COLAMD_STATS] ; Output argument. + + Statistics on the ordering, and error status. + See colamd.h for related definitions. + Symamd returns FALSE if stats is not present. + + stats [0]: number of dense or empty row and columns ignored + (and ordered last in the output permutation + perm). Note that a row/column can become + "empty" if it contains only "dense" and/or + "empty" columns/rows. + + stats [1]: (same as stats [0]) + + stats [2]: number of garbage collections performed. + + stats [3]: status code. < 0 is an error code. + > 1 is a warning or notice. + + 0 OK. Each column of the input matrix contained + row indices in increasing order, with no + duplicates. + + 1 OK, but columns of input matrix were jumbled + (unsorted columns or duplicate entries). Symamd + had to do some extra work to sort the matrix + first and remove duplicate entries, but it + still was able to return a valid permutation + (return value of symamd was TRUE). + + stats [4]: highest numbered column that + is unsorted or has duplicate + entries. + stats [5]: last seen duplicate or + unsorted row index. + stats [6]: number of duplicate or + unsorted row indices. + + -1 A is a null pointer + + -2 p is a null pointer + + -3 (unused, see colamd.c) + + -4 n is negative + + stats [4]: n + + -5 number of nonzeros in matrix is negative + + stats [4]: # of nonzeros (p [n]). + + -6 p [0] is nonzero + + stats [4]: p [0] + + -7 (unused) + + -8 a column has a negative number of entries + + stats [4]: column with < 0 entries + stats [5]: number of entries in col + + -9 a row index is out of bounds + + stats [4]: column with bad row index + stats [5]: bad row index + stats [6]: n_row, # of rows of matrx + + -10 out of memory (unable to allocate temporary + workspace for M or count arrays using the + "allocate" routine passed into symamd). + + -999 internal error. colamd failed to order the + matrix M, when it should have succeeded. This + indicates a bug. If this (and *only* this) + error code occurs, please contact the authors. + Don't contact the authors if you get any other + error code. + + Future versions may return more statistics in the stats array. + + void * (*allocate) (size_t, size_t) + + A pointer to a function providing memory allocation. The + allocated memory must be returned initialized to zero. For a + C application, this argument should normally be a pointer to + calloc. For a Matlab mexFunction, the routine mxCalloc is + passed instead. + + void (*release) (size_t, size_t) + + A pointer to a function that frees memory allocated by the + memory allocation routine above. For a C application, this + argument should normally be a pointer to free. For a Matlab + mexFunction, the routine mxFree is passed instead. + + + ---------------------------------------------------------------------------- + colamd_report: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + colamd_report (int stats [COLAMD_STATS]) ; + + Purpose: + + Prints the error status and statistics recorded in the stats + array on the standard error output (for a standard C routine) + or on the Matlab output (for a mexFunction). + + Arguments: + + int stats [COLAMD_STATS] ; Input only. Statistics from colamd. + + + ---------------------------------------------------------------------------- + symamd_report: + ---------------------------------------------------------------------------- + + C syntax: + + #include "colamd.h" + symamd_report (int stats [COLAMD_STATS]) ; + + Purpose: + + Prints the error status and statistics recorded in the stats + array on the standard error output (for a standard C routine) + or on the Matlab output (for a mexFunction). + + Arguments: + + int stats [COLAMD_STATS] ; Input only. Statistics from symamd. + + +*/ + +/* ========================================================================== */ +/* === Scaffolding code definitions ======================================== */ +/* ========================================================================== */ + +/* Ensure that debugging is turned off: */ +#ifndef NDEBUG +#define NDEBUG +#endif /* NDEBUG */ + +/* + Our "scaffolding code" philosophy: In our opinion, well-written library + code should keep its "debugging" code, and just normally have it turned off + by the compiler so as not to interfere with performance. This serves + several purposes: + + (1) assertions act as comments to the reader, telling you what the code + expects at that point. All assertions will always be true (unless + there really is a bug, of course). + + (2) leaving in the scaffolding code assists anyone who would like to modify + the code, or understand the algorithm (by reading the debugging output, + one can get a glimpse into what the code is doing). + + (3) (gasp!) for actually finding bugs. This code has been heavily tested + and "should" be fully functional and bug-free ... but you never know... + + To enable debugging, comment out the "#define NDEBUG" above. For a Matlab + mexFunction, you will also need to modify mexopts.sh to remove the -DNDEBUG + definition. The code will become outrageously slow when debugging is + enabled. To control the level of debugging output, set an environment + variable D to 0 (little), 1 (some), 2, 3, or 4 (lots). When debugging, + you should see the following message on the standard output: + + colamd: debug version, D = 1 (THIS WILL BE SLOW!) + + or a similar message for symamd. If you don't, then debugging has not + been enabled. + +*/ + +/* ========================================================================== */ +/* === Include files ======================================================== */ +/* ========================================================================== */ + +#include "colamd.h" +#include + +#ifdef MATLAB_MEX_FILE +#include "mex.h" +#include "matrix.h" +#else +#include +#include +#endif /* MATLAB_MEX_FILE */ + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* ========================================================================== */ +/* === Definitions ========================================================== */ +/* ========================================================================== */ + +/* Routines are either PUBLIC (user-callable) or PRIVATE (not user-callable) */ +#define PUBLIC +#define PRIVATE static + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + +#define ONES_COMPLEMENT(r) (-(r)-1) + +/* -------------------------------------------------------------------------- */ +/* Change for version 2.1: define TRUE and FALSE only if not yet defined */ +/* -------------------------------------------------------------------------- */ + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +/* -------------------------------------------------------------------------- */ + +#define EMPTY (-1) + +/* Row and column status */ +#define ALIVE (0) +#define DEAD (-1) + +/* Column status */ +#define DEAD_PRINCIPAL (-1) +#define DEAD_NON_PRINCIPAL (-2) + +/* Macros for row and column status update and checking. */ +#define ROW_IS_DEAD(r) ROW_IS_MARKED_DEAD (Row[r].shared2.mark) +#define ROW_IS_MARKED_DEAD(row_mark) (row_mark < ALIVE) +#define ROW_IS_ALIVE(r) (Row [r].shared2.mark >= ALIVE) +#define COL_IS_DEAD(c) (Col [c].start < ALIVE) +#define COL_IS_ALIVE(c) (Col [c].start >= ALIVE) +#define COL_IS_DEAD_PRINCIPAL(c) (Col [c].start == DEAD_PRINCIPAL) +#define KILL_ROW(r) { Row [r].shared2.mark = DEAD ; } +#define KILL_PRINCIPAL_COL(c) { Col [c].start = DEAD_PRINCIPAL ; } +#define KILL_NON_PRINCIPAL_COL(c) { Col [c].start = DEAD_NON_PRINCIPAL ; } + +/* ========================================================================== */ +/* === Colamd reporting mechanism =========================================== */ +/* ========================================================================== */ + +#ifdef MATLAB_MEX_FILE + +/* use mexPrintf in a Matlab mexFunction, for debugging and statistics output */ +#define PRINTF mexPrintf + +/* In Matlab, matrices are 1-based to the user, but 0-based internally */ +#define INDEX(i) ((i)+1) + +#else + +/* Use printf in standard C environment, for debugging and statistics output. */ +/* Output is generated only if debugging is enabled at compile time, or if */ +/* the caller explicitly calls colamd_report or symamd_report. */ +#define PRINTF printf + +/* In C, matrices are 0-based and indices are reported as such in *_report */ +#define INDEX(i) (i) + +#endif /* MATLAB_MEX_FILE */ + +/* ========================================================================== */ +/* === Prototypes of PRIVATE routines ======================================= */ +/* ========================================================================== */ + +PRIVATE int init_rows_cols +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int p [], + int stats [COLAMD_STATS] +) ; + +PRIVATE void init_scoring +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int head [], + double knobs [COLAMD_KNOBS], + int *p_n_row2, + int *p_n_col2, + int *p_max_deg +) ; + +PRIVATE int find_ordering +( + int n_row, + int n_col, + int Alen, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int head [], + int n_col2, + int max_deg, + int pfree +) ; + +PRIVATE void order_children +( + int n_col, + Colamd_Col Col [], + int p [] +) ; + +PRIVATE void detect_super_cols +( + +#ifndef NDEBUG + int n_col, + Colamd_Row Row [], +#endif /* NDEBUG */ + + Colamd_Col Col [], + int A [], + int head [], + int row_start, + int row_length +) ; + +PRIVATE int garbage_collection +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int *pfree +) ; + +PRIVATE int clear_mark +( + int n_row, + Colamd_Row Row [] +) ; + +PRIVATE void print_report +( + char *method, + int stats [COLAMD_STATS] +) ; + +/* ========================================================================== */ +/* === Debugging prototypes and definitions ================================= */ +/* ========================================================================== */ + +#ifndef NDEBUG + +/* colamd_debug is the *ONLY* global variable, and is only */ +/* present when debugging */ + +PRIVATE int colamd_debug ; /* debug print level */ + +#define DEBUG0(params) { (void) PRINTF params ; } +#define DEBUG1(params) { if (colamd_debug >= 1) (void) PRINTF params ; } +#define DEBUG2(params) { if (colamd_debug >= 2) (void) PRINTF params ; } +#define DEBUG3(params) { if (colamd_debug >= 3) (void) PRINTF params ; } +#define DEBUG4(params) { if (colamd_debug >= 4) (void) PRINTF params ; } + +#ifdef MATLAB_MEX_FILE +#define ASSERT(expression) (mxAssert ((expression), "")) +#else +#define ASSERT(expression) (assert (expression)) +#endif /* MATLAB_MEX_FILE */ + +PRIVATE void colamd_get_debug /* gets the debug print level from getenv */ +( + char *method +) ; + +PRIVATE void debug_deg_lists +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int head [], + int min_score, + int should, + int max_deg +) ; + +PRIVATE void debug_mark +( + int n_row, + Colamd_Row Row [], + int tag_mark, + int max_mark +) ; + +PRIVATE void debug_matrix +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [] +) ; + +PRIVATE void debug_structures +( + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int n_col2 +) ; + +#else /* NDEBUG */ + +/* === No debugging ========================================================= */ + +#define DEBUG0(params) ; +#define DEBUG1(params) ; +#define DEBUG2(params) ; +#define DEBUG3(params) ; +#define DEBUG4(params) ; + +#define ASSERT(expression) ((void) 0) + +#endif /* NDEBUG */ + +/* ========================================================================== */ + + + +/* ========================================================================== */ +/* === USER-CALLABLE ROUTINES: ============================================== */ +/* ========================================================================== */ + + +/* ========================================================================== */ +/* === colamd_recommended =================================================== */ +/* ========================================================================== */ + +/* + The colamd_recommended routine returns the suggested size for Alen. This + value has been determined to provide good balance between the number of + garbage collections and the memory requirements for colamd. If any + argument is negative, a -1 is returned as an error condition. This + function is also available as a macro defined in colamd.h, so that you + can use it for a statically-allocated array size. +*/ + +PUBLIC int colamd_recommended /* returns recommended value of Alen. */ +( + /* === Parameters ======================================================= */ + + int nnz, /* number of nonzeros in A */ + int n_row, /* number of rows in A */ + int n_col /* number of columns in A */ +) +{ + return (COLAMD_RECOMMENDED (nnz, n_row, n_col)) ; +} + + +/* ========================================================================== */ +/* === colamd_set_defaults ================================================== */ +/* ========================================================================== */ + +/* + The colamd_set_defaults routine sets the default values of the user- + controllable parameters for colamd: + + knobs [0] rows with knobs[0]*n_col entries or more are removed + prior to ordering in colamd. Rows and columns with + knobs[0]*n_col entries or more are removed prior to + ordering in symamd and placed last in the output + ordering. + + knobs [1] columns with knobs[1]*n_row entries or more are removed + prior to ordering in colamd, and placed last in the + column permutation. Symamd ignores this knob. + + knobs [2..19] unused, but future versions might use this +*/ + +PUBLIC void colamd_set_defaults +( + /* === Parameters ======================================================= */ + + double knobs [COLAMD_KNOBS] /* knob array */ +) +{ + /* === Local variables ================================================== */ + + int i ; + + if (!knobs) + { + return ; /* no knobs to initialize */ + } + for (i = 0 ; i < COLAMD_KNOBS ; i++) + { + knobs [i] = 0 ; + } + knobs [COLAMD_DENSE_ROW] = 0.5 ; /* ignore rows over 50% dense */ + knobs [COLAMD_DENSE_COL] = 0.5 ; /* ignore columns over 50% dense */ +} + + +/* ========================================================================== */ +/* === symamd =============================================================== */ +/* ========================================================================== */ + +PUBLIC int symamd /* return TRUE if OK, FALSE otherwise */ +( + /* === Parameters ======================================================= */ + + int n, /* number of rows and columns of A */ + int A [], /* row indices of A */ + int p [], /* column pointers of A */ + int perm [], /* output permutation, size n+1 */ + double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ + int stats [COLAMD_STATS], /* output statistics and error codes */ + void * (*allocate) (size_t, size_t), + /* pointer to calloc (ANSI C) or */ + /* mxCalloc (for Matlab mexFunction) */ + void (*release) (void *) + /* pointer to free (ANSI C) or */ + /* mxFree (for Matlab mexFunction) */ +) +{ + /* === Local variables ================================================== */ + + int *count ; /* length of each column of M, and col pointer*/ + int *mark ; /* mark array for finding duplicate entries */ + int *M ; /* row indices of matrix M */ + int Mlen ; /* length of M */ + int n_row ; /* number of rows in M */ + int nnz ; /* number of entries in A */ + int i ; /* row index of A */ + int j ; /* column index of A */ + int k ; /* row index of M */ + int mnz ; /* number of nonzeros in M */ + int pp ; /* index into a column of A */ + int last_row ; /* last row seen in the current column */ + int length ; /* number of nonzeros in a column */ + + double cknobs [COLAMD_KNOBS] ; /* knobs for colamd */ + double default_knobs [COLAMD_KNOBS] ; /* default knobs for colamd */ + int cstats [COLAMD_STATS] ; /* colamd stats */ + +#ifndef NDEBUG + colamd_get_debug ("symamd") ; +#endif /* NDEBUG */ + + /* === Check the input arguments ======================================== */ + + if (!stats) + { + DEBUG0 (("symamd: stats not present\n")) ; + return (FALSE) ; + } + for (i = 0 ; i < COLAMD_STATS ; i++) + { + stats [i] = 0 ; + } + stats [COLAMD_STATUS] = COLAMD_OK ; + stats [COLAMD_INFO1] = -1 ; + stats [COLAMD_INFO2] = -1 ; + + if (!A) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; + DEBUG0 (("symamd: A not present\n")) ; + return (FALSE) ; + } + + if (!p) /* p is not present */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; + DEBUG0 (("symamd: p not present\n")) ; + return (FALSE) ; + } + + if (n < 0) /* n must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; + stats [COLAMD_INFO1] = n ; + DEBUG0 (("symamd: n negative %d\n", n)) ; + return (FALSE) ; + } + + nnz = p [n] ; + if (nnz < 0) /* nnz must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; + stats [COLAMD_INFO1] = nnz ; + DEBUG0 (("symamd: number of entries negative %d\n", nnz)) ; + return (FALSE) ; + } + + if (p [0] != 0) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; + stats [COLAMD_INFO1] = p [0] ; + DEBUG0 (("symamd: p[0] not zero %d\n", p [0])) ; + return (FALSE) ; + } + + /* === If no knobs, set default knobs =================================== */ + + if (!knobs) + { + colamd_set_defaults (default_knobs) ; + knobs = default_knobs ; + } + + /* === Allocate count and mark ========================================== */ + + count = (int *) ((*allocate) (n+1, sizeof (int))) ; + if (!count) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; + DEBUG0 (("symamd: allocate count (size %d) failed\n", n+1)) ; + return (FALSE) ; + } + + mark = (int *) ((*allocate) (n+1, sizeof (int))) ; + if (!mark) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; + (*release) ((void *) count) ; + DEBUG0 (("symamd: allocate mark (size %d) failed\n", n+1)) ; + return (FALSE) ; + } + + /* === Compute column counts of M, check if A is valid ================== */ + + stats [COLAMD_INFO3] = 0 ; /* number of duplicate or unsorted row indices*/ + + for (i = 0 ; i < n ; i++) + { + mark [i] = -1 ; + } + + for (j = 0 ; j < n ; j++) + { + last_row = -1 ; + + length = p [j+1] - p [j] ; + if (length < 0) + { + /* column pointers must be non-decreasing */ + stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; + stats [COLAMD_INFO1] = j ; + stats [COLAMD_INFO2] = length ; + (*release) ((void *) count) ; + (*release) ((void *) mark) ; + DEBUG0 (("symamd: col %d negative length %d\n", j, length)) ; + return (FALSE) ; + } + + for (pp = p [j] ; pp < p [j+1] ; pp++) + { + i = A [pp] ; + if (i < 0 || i >= n) + { + /* row index i, in column j, is out of bounds */ + stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; + stats [COLAMD_INFO1] = j ; + stats [COLAMD_INFO2] = i ; + stats [COLAMD_INFO3] = n ; + (*release) ((void *) count) ; + (*release) ((void *) mark) ; + DEBUG0 (("symamd: row %d col %d out of bounds\n", i, j)) ; + return (FALSE) ; + } + + if (i <= last_row || mark [i] == j) + { + /* row index is unsorted or repeated (or both), thus col */ + /* is jumbled. This is a notice, not an error condition. */ + stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; + stats [COLAMD_INFO1] = j ; + stats [COLAMD_INFO2] = i ; + (stats [COLAMD_INFO3]) ++ ; + DEBUG1 (("symamd: row %d col %d unsorted/duplicate\n", i, j)) ; + } + + if (i > j && mark [i] != j) + { + /* row k of M will contain column indices i and j */ + count [i]++ ; + count [j]++ ; + } + + /* mark the row as having been seen in this column */ + mark [i] = j ; + + last_row = i ; + } + } + + if (stats [COLAMD_STATUS] == COLAMD_OK) + { + /* if there are no duplicate entries, then mark is no longer needed */ + (*release) ((void *) mark) ; + } + + /* === Compute column pointers of M ===================================== */ + + /* use output permutation, perm, for column pointers of M */ + perm [0] = 0 ; + for (j = 1 ; j <= n ; j++) + { + perm [j] = perm [j-1] + count [j-1] ; + } + for (j = 0 ; j < n ; j++) + { + count [j] = perm [j] ; + } + + /* === Construct M ====================================================== */ + + mnz = perm [n] ; + n_row = mnz / 2 ; + Mlen = colamd_recommended (mnz, n_row, n) ; + M = (int *) ((*allocate) (Mlen, sizeof (int))) ; + DEBUG0 (("symamd: M is %d-by-%d with %d entries, Mlen = %d\n", + n_row, n, mnz, Mlen)) ; + + if (!M) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ; + (*release) ((void *) count) ; + (*release) ((void *) mark) ; + DEBUG0 (("symamd: allocate M (size %d) failed\n", Mlen)) ; + return (FALSE) ; + } + + k = 0 ; + + if (stats [COLAMD_STATUS] == COLAMD_OK) + { + /* Matrix is OK */ + for (j = 0 ; j < n ; j++) + { + ASSERT (p [j+1] - p [j] >= 0) ; + for (pp = p [j] ; pp < p [j+1] ; pp++) + { + i = A [pp] ; + ASSERT (i >= 0 && i < n) ; + if (i > j) + { + /* row k of M contains column indices i and j */ + M [count [i]++] = k ; + M [count [j]++] = k ; + k++ ; + } + } + } + } + else + { + /* Matrix is jumbled. Do not add duplicates to M. Unsorted cols OK. */ + DEBUG0 (("symamd: Duplicates in A.\n")) ; + for (i = 0 ; i < n ; i++) + { + mark [i] = -1 ; + } + for (j = 0 ; j < n ; j++) + { + ASSERT (p [j+1] - p [j] >= 0) ; + for (pp = p [j] ; pp < p [j+1] ; pp++) + { + i = A [pp] ; + ASSERT (i >= 0 && i < n) ; + if (i > j && mark [i] != j) + { + /* row k of M contains column indices i and j */ + M [count [i]++] = k ; + M [count [j]++] = k ; + k++ ; + mark [i] = j ; + } + } + } + (*release) ((void *) mark) ; + } + + /* count and mark no longer needed */ + (*release) ((void *) count) ; + ASSERT (k == n_row) ; + + /* === Adjust the knobs for M =========================================== */ + + for (i = 0 ; i < COLAMD_KNOBS ; i++) + { + cknobs [i] = knobs [i] ; + } + + /* there are no dense rows in M */ + cknobs [COLAMD_DENSE_ROW] = 1.0 ; + + if (n_row != 0 && n < n_row) + { + /* On input, the knob is a fraction of 1..n, the number of rows of A. */ + /* Convert it to a fraction of 1..n_row, of the number of rows of M. */ + cknobs [COLAMD_DENSE_COL] = (knobs [COLAMD_DENSE_ROW] * n) / n_row ; + } + else + { + /* no dense columns in M */ + cknobs [COLAMD_DENSE_COL] = 1.0 ; + } + + DEBUG0 (("symamd: dense col knob for M: %g\n", cknobs [COLAMD_DENSE_COL])) ; + + /* === Order the columns of M =========================================== */ + + if (!colamd (n_row, n, Mlen, M, perm, cknobs, cstats)) + { + /* This "cannot" happen, unless there is a bug in the code. */ + stats [COLAMD_STATUS] = COLAMD_ERROR_internal_error ; + (*release) ((void *) M) ; + DEBUG0 (("symamd: internal error!\n")) ; + return (FALSE) ; + } + + /* Note that the output permutation is now in perm */ + + /* === get the statistics for symamd from colamd ======================== */ + + /* note that a dense column in colamd means a dense row and col in symamd */ + stats [COLAMD_DENSE_ROW] = cstats [COLAMD_DENSE_COL] ; + stats [COLAMD_DENSE_COL] = cstats [COLAMD_DENSE_COL] ; + stats [COLAMD_DEFRAG_COUNT] = cstats [COLAMD_DEFRAG_COUNT] ; + + /* === Free M =========================================================== */ + + (*release) ((void *) M) ; + DEBUG0 (("symamd: done.\n")) ; + return (TRUE) ; + +} + +/* ========================================================================== */ +/* === colamd =============================================================== */ +/* ========================================================================== */ + +/* + The colamd routine computes a column ordering Q of a sparse matrix + A such that the LU factorization P(AQ) = LU remains sparse, where P is + selected via partial pivoting. The routine can also be viewed as + providing a permutation Q such that the Cholesky factorization + (AQ)'(AQ) = LL' remains sparse. +*/ + +PUBLIC int colamd /* returns TRUE if successful, FALSE otherwise*/ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows in A */ + int n_col, /* number of columns in A */ + int Alen, /* length of A */ + int A [], /* row indices of A */ + int p [], /* pointers to columns in A */ + double knobs [COLAMD_KNOBS],/* parameters (uses defaults if NULL) */ + int stats [COLAMD_STATS] /* output statistics and error codes */ +) +{ + /* === Local variables ================================================== */ + + int i ; /* loop index */ + int nnz ; /* nonzeros in A */ + int Row_size ; /* size of Row [], in integers */ + int Col_size ; /* size of Col [], in integers */ + int need ; /* minimum required length of A */ + Colamd_Row *Row ; /* pointer into A of Row [0..n_row] array */ + Colamd_Col *Col ; /* pointer into A of Col [0..n_col] array */ + int n_col2 ; /* number of non-dense, non-empty columns */ + int n_row2 ; /* number of non-dense, non-empty rows */ + int ngarbage ; /* number of garbage collections performed */ + int max_deg ; /* maximum row degree */ + double default_knobs [COLAMD_KNOBS] ; /* default knobs array */ + +#ifndef NDEBUG + colamd_get_debug ("colamd") ; +#endif /* NDEBUG */ + + /* === Check the input arguments ======================================== */ + + if (!stats) + { + DEBUG0 (("colamd: stats not present\n")) ; + return (FALSE) ; + } + for (i = 0 ; i < COLAMD_STATS ; i++) + { + stats [i] = 0 ; + } + stats [COLAMD_STATUS] = COLAMD_OK ; + stats [COLAMD_INFO1] = -1 ; + stats [COLAMD_INFO2] = -1 ; + + if (!A) /* A is not present */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ; + DEBUG0 (("colamd: A not present\n")) ; + return (FALSE) ; + } + + if (!p) /* p is not present */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ; + DEBUG0 (("colamd: p not present\n")) ; + return (FALSE) ; + } + + if (n_row < 0) /* n_row must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ; + stats [COLAMD_INFO1] = n_row ; + DEBUG0 (("colamd: nrow negative %d\n", n_row)) ; + return (FALSE) ; + } + + if (n_col < 0) /* n_col must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ; + stats [COLAMD_INFO1] = n_col ; + DEBUG0 (("colamd: ncol negative %d\n", n_col)) ; + return (FALSE) ; + } + + nnz = p [n_col] ; + if (nnz < 0) /* nnz must be >= 0 */ + { + stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ; + stats [COLAMD_INFO1] = nnz ; + DEBUG0 (("colamd: number of entries negative %d\n", nnz)) ; + return (FALSE) ; + } + + if (p [0] != 0) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ; + stats [COLAMD_INFO1] = p [0] ; + DEBUG0 (("colamd: p[0] not zero %d\n", p [0])) ; + return (FALSE) ; + } + + /* === If no knobs, set default knobs =================================== */ + + if (!knobs) + { + colamd_set_defaults (default_knobs) ; + knobs = default_knobs ; + } + + /* === Allocate the Row and Col arrays from array A ===================== */ + + Col_size = COLAMD_C (n_col) ; + Row_size = COLAMD_R (n_row) ; + need = 2*nnz + n_col + Col_size + Row_size ; + + if (need > Alen) + { + /* not enough space in array A to perform the ordering */ + stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ; + stats [COLAMD_INFO1] = need ; + stats [COLAMD_INFO2] = Alen ; + DEBUG0 (("colamd: Need Alen >= %d, given only Alen = %d\n", need,Alen)); + return (FALSE) ; + } + + Alen -= Col_size + Row_size ; + Col = (Colamd_Col *) &A [Alen] ; + Row = (Colamd_Row *) &A [Alen + Col_size] ; + + /* === Construct the row and column data structures ===================== */ + + if (!init_rows_cols (n_row, n_col, Row, Col, A, p, stats)) + { + /* input matrix is invalid */ + DEBUG0 (("colamd: Matrix invalid\n")) ; + return (FALSE) ; + } + + /* === Initialize scores, kill dense rows/columns ======================= */ + + init_scoring (n_row, n_col, Row, Col, A, p, knobs, + &n_row2, &n_col2, &max_deg) ; + + /* === Order the supercolumns =========================================== */ + + ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p, + n_col2, max_deg, 2*nnz) ; + + /* === Order the non-principal columns ================================== */ + + order_children (n_col, Col, p) ; + + /* === Return statistics in stats ======================================= */ + + stats [COLAMD_DENSE_ROW] = n_row - n_row2 ; + stats [COLAMD_DENSE_COL] = n_col - n_col2 ; + stats [COLAMD_DEFRAG_COUNT] = ngarbage ; + DEBUG0 (("colamd: done.\n")) ; + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === colamd_report ======================================================== */ +/* ========================================================================== */ + +PUBLIC void colamd_report +( + int stats [COLAMD_STATS] +) +{ + print_report ("colamd", stats) ; +} + + +/* ========================================================================== */ +/* === symamd_report ======================================================== */ +/* ========================================================================== */ + +PUBLIC void symamd_report +( + int stats [COLAMD_STATS] +) +{ + print_report ("symamd", stats) ; +} + + + +/* ========================================================================== */ +/* === NON-USER-CALLABLE ROUTINES: ========================================== */ +/* ========================================================================== */ + +/* There are no user-callable routines beyond this point in the file */ + + +/* ========================================================================== */ +/* === init_rows_cols ======================================================= */ +/* ========================================================================== */ + +/* + Takes the column form of the matrix in A and creates the row form of the + matrix. Also, row and column attributes are stored in the Col and Row + structs. If the columns are un-sorted or contain duplicate row indices, + this routine will also sort and remove duplicate row indices from the + column form of the matrix. Returns FALSE if the matrix is invalid, + TRUE otherwise. Not user-callable. +*/ + +PRIVATE int init_rows_cols /* returns TRUE if OK, or FALSE otherwise */ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows of A */ + int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + int A [], /* row indices of A, of size Alen */ + int p [], /* pointers to columns in A, of size n_col+1 */ + int stats [COLAMD_STATS] /* colamd statistics */ +) +{ + /* === Local variables ================================================== */ + + int col ; /* a column index */ + int row ; /* a row index */ + int *cp ; /* a column pointer */ + int *cp_end ; /* a pointer to the end of a column */ + int *rp ; /* a row pointer */ + int *rp_end ; /* a pointer to the end of a row */ + int last_row ; /* previous row */ + + /* === Initialize columns, and check column pointers ==================== */ + + for (col = 0 ; col < n_col ; col++) + { + Col [col].start = p [col] ; + Col [col].length = p [col+1] - p [col] ; + + if (Col [col].length < 0) + { + /* column pointers must be non-decreasing */ + stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = Col [col].length ; + DEBUG0 (("colamd: col %d length %d < 0\n", col, Col [col].length)) ; + return (FALSE) ; + } + + Col [col].shared1.thickness = 1 ; + Col [col].shared2.score = 0 ; + Col [col].shared3.prev = EMPTY ; + Col [col].shared4.degree_next = EMPTY ; + } + + /* p [0..n_col] no longer needed, used as "head" in subsequent routines */ + + /* === Scan columns, compute row degrees, and check row indices ========= */ + + stats [COLAMD_INFO3] = 0 ; /* number of duplicate or unsorted row indices*/ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].length = 0 ; + Row [row].shared2.mark = -1 ; + } + + for (col = 0 ; col < n_col ; col++) + { + last_row = -1 ; + + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + + while (cp < cp_end) + { + row = *cp++ ; + + /* make sure row indices within range */ + if (row < 0 || row >= n_row) + { + stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + stats [COLAMD_INFO3] = n_row ; + DEBUG0 (("colamd: row %d col %d out of bounds\n", row, col)) ; + return (FALSE) ; + } + + if (row <= last_row || Row [row].shared2.mark == col) + { + /* row index are unsorted or repeated (or both), thus col */ + /* is jumbled. This is a notice, not an error condition. */ + stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ; + stats [COLAMD_INFO1] = col ; + stats [COLAMD_INFO2] = row ; + (stats [COLAMD_INFO3]) ++ ; + DEBUG1 (("colamd: row %d col %d unsorted/duplicate\n",row,col)); + } + + if (Row [row].shared2.mark != col) + { + Row [row].length++ ; + } + else + { + /* this is a repeated entry in the column, */ + /* it will be removed */ + Col [col].length-- ; + } + + /* mark the row as having been seen in this column */ + Row [row].shared2.mark = col ; + + last_row = row ; + } + } + + /* === Compute row pointers ============================================= */ + + /* row form of the matrix starts directly after the column */ + /* form of matrix in A */ + Row [0].start = p [n_col] ; + Row [0].shared1.p = Row [0].start ; + Row [0].shared2.mark = -1 ; + for (row = 1 ; row < n_row ; row++) + { + Row [row].start = Row [row-1].start + Row [row-1].length ; + Row [row].shared1.p = Row [row].start ; + Row [row].shared2.mark = -1 ; + } + + /* === Create row form ================================================== */ + + if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) + { + /* if cols jumbled, watch for repeated row indices */ + for (col = 0 ; col < n_col ; col++) + { + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + while (cp < cp_end) + { + row = *cp++ ; + if (Row [row].shared2.mark != col) + { + A [(Row [row].shared1.p)++] = col ; + Row [row].shared2.mark = col ; + } + } + } + } + else + { + /* if cols not jumbled, we don't need the mark (this is faster) */ + for (col = 0 ; col < n_col ; col++) + { + cp = &A [p [col]] ; + cp_end = &A [p [col+1]] ; + while (cp < cp_end) + { + A [(Row [*cp++].shared1.p)++] = col ; + } + } + } + + /* === Clear the row marks and set row degrees ========================== */ + + for (row = 0 ; row < n_row ; row++) + { + Row [row].shared2.mark = 0 ; + Row [row].shared1.degree = Row [row].length ; + } + + /* === See if we need to re-create columns ============================== */ + + if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED) + { + DEBUG0 (("colamd: reconstructing column form, matrix jumbled\n")) ; + +#ifndef NDEBUG + /* make sure column lengths are correct */ + for (col = 0 ; col < n_col ; col++) + { + p [col] = Col [col].length ; + } + for (row = 0 ; row < n_row ; row++) + { + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + p [*rp++]-- ; + } + } + for (col = 0 ; col < n_col ; col++) + { + ASSERT (p [col] == 0) ; + } + /* now p is all zero (different than when debugging is turned off) */ +#endif /* NDEBUG */ + + /* === Compute col pointers ========================================= */ + + /* col form of the matrix starts at A [0]. */ + /* Note, we may have a gap between the col form and the row */ + /* form if there were duplicate entries, if so, it will be */ + /* removed upon the first garbage collection */ + Col [0].start = 0 ; + p [0] = Col [0].start ; + for (col = 1 ; col < n_col ; col++) + { + /* note that the lengths here are for pruned columns, i.e. */ + /* no duplicate row indices will exist for these columns */ + Col [col].start = Col [col-1].start + Col [col-1].length ; + p [col] = Col [col].start ; + } + + /* === Re-create col form =========================================== */ + + for (row = 0 ; row < n_row ; row++) + { + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + A [(p [*rp++])++] = row ; + } + } + } + + /* === Done. Matrix is not (or no longer) jumbled ====================== */ + + return (TRUE) ; +} + + +/* ========================================================================== */ +/* === init_scoring ========================================================= */ +/* ========================================================================== */ + +/* + Kills dense or empty columns and rows, calculates an initial score for + each column, and places all columns in the degree lists. Not user-callable. +*/ + +PRIVATE void init_scoring +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows of A */ + int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + int A [], /* column form and row form of A */ + int head [], /* of size n_col+1 */ + double knobs [COLAMD_KNOBS],/* parameters */ + int *p_n_row2, /* number of non-dense, non-empty rows */ + int *p_n_col2, /* number of non-dense, non-empty columns */ + int *p_max_deg /* maximum row degree */ +) +{ + /* === Local variables ================================================== */ + + int c ; /* a column index */ + int r, row ; /* a row index */ + int *cp ; /* a column pointer */ + int deg ; /* degree of a row or column */ + int *cp_end ; /* a pointer to the end of a column */ + int *new_cp ; /* new column pointer */ + int col_length ; /* length of pruned column */ + int score ; /* current column score */ + int n_col2 ; /* number of non-dense, non-empty columns */ + int n_row2 ; /* number of non-dense, non-empty rows */ + int dense_row_count ; /* remove rows with more entries than this */ + int dense_col_count ; /* remove cols with more entries than this */ + int min_score ; /* smallest column score */ + int max_deg ; /* maximum row degree */ + int next_col ; /* Used to add to degree list.*/ + +#ifndef NDEBUG + int debug_count ; /* debug only. */ +#endif /* NDEBUG */ + + /* === Extract knobs ==================================================== */ + + dense_row_count = MAX (0, MIN (knobs [COLAMD_DENSE_ROW] * n_col, n_col)) ; + dense_col_count = MAX (0, MIN (knobs [COLAMD_DENSE_COL] * n_row, n_row)) ; + DEBUG1 (("colamd: densecount: %d %d\n", dense_row_count, dense_col_count)) ; + max_deg = 0 ; + n_col2 = n_col ; + n_row2 = n_row ; + + /* === Kill empty columns =============================================== */ + + /* Put the empty columns at the end in their natural order, so that LU */ + /* factorization can proceed as far as possible. */ + for (c = n_col-1 ; c >= 0 ; c--) + { + deg = Col [c].length ; + if (deg == 0) + { + /* this is a empty column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + } + } + DEBUG1 (("colamd: null columns killed: %d\n", n_col - n_col2)) ; + + /* === Kill dense columns =============================================== */ + + /* Put the dense columns at the end, in their natural order */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* skip any dead columns */ + if (COL_IS_DEAD (c)) + { + continue ; + } + deg = Col [c].length ; + if (deg > dense_col_count) + { + /* this is a dense column, kill and order it last */ + Col [c].shared2.order = --n_col2 ; + /* decrement the row degrees */ + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + Row [*cp++].shared1.degree-- ; + } + KILL_PRINCIPAL_COL (c) ; + } + } + DEBUG1 (("colamd: Dense and null columns killed: %d\n", n_col - n_col2)) ; + + /* === Kill dense and empty rows ======================================== */ + + for (r = 0 ; r < n_row ; r++) + { + deg = Row [r].shared1.degree ; + ASSERT (deg >= 0 && deg <= n_col) ; + if (deg > dense_row_count || deg == 0) + { + /* kill a dense or empty row */ + KILL_ROW (r) ; + --n_row2 ; + } + else + { + /* keep track of max degree of remaining rows */ + max_deg = MAX (max_deg, deg) ; + } + } + DEBUG1 (("colamd: Dense and null rows killed: %d\n", n_row - n_row2)) ; + + /* === Compute initial column scores ==================================== */ + + /* At this point the row degrees are accurate. They reflect the number */ + /* of "live" (non-dense) columns in each row. No empty rows exist. */ + /* Some "live" columns may contain only dead rows, however. These are */ + /* pruned in the code below. */ + + /* now find the initial matlab score for each column */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* skip dead column */ + if (COL_IS_DEAD (c)) + { + continue ; + } + score = 0 ; + cp = &A [Col [c].start] ; + new_cp = cp ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + /* skip if dead */ + if (ROW_IS_DEAD (row)) + { + continue ; + } + /* compact the column */ + *new_cp++ = row ; + /* add row's external degree */ + score += Row [row].shared1.degree - 1 ; + /* guard against integer overflow */ + score = MIN (score, n_col) ; + } + /* determine pruned column length */ + col_length = (int) (new_cp - &A [Col [c].start]) ; + if (col_length == 0) + { + /* a newly-made null column (all rows in this col are "dense" */ + /* and have already been killed) */ + DEBUG2 (("Newly null killed: %d\n", c)) ; + Col [c].shared2.order = --n_col2 ; + KILL_PRINCIPAL_COL (c) ; + } + else + { + /* set column length and set score */ + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + Col [c].length = col_length ; + Col [c].shared2.score = score ; + } + } + DEBUG1 (("colamd: Dense, null, and newly-null columns killed: %d\n", + n_col-n_col2)) ; + + /* At this point, all empty rows and columns are dead. All live columns */ + /* are "clean" (containing no dead rows) and simplicial (no supercolumns */ + /* yet). Rows may contain dead columns, but all live rows contain at */ + /* least one live column. */ + +#ifndef NDEBUG + debug_structures (n_row, n_col, Row, Col, A, n_col2) ; +#endif /* NDEBUG */ + + /* === Initialize degree lists ========================================== */ + +#ifndef NDEBUG + debug_count = 0 ; +#endif /* NDEBUG */ + + /* clear the hash buckets */ + for (c = 0 ; c <= n_col ; c++) + { + head [c] = EMPTY ; + } + min_score = n_col ; + /* place in reverse order, so low column indices are at the front */ + /* of the lists. This is to encourage natural tie-breaking */ + for (c = n_col-1 ; c >= 0 ; c--) + { + /* only add principal columns to degree lists */ + if (COL_IS_ALIVE (c)) + { + DEBUG4 (("place %d score %d minscore %d ncol %d\n", + c, Col [c].shared2.score, min_score, n_col)) ; + + /* === Add columns score to DList =============================== */ + + score = Col [c].shared2.score ; + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (score >= 0) ; + ASSERT (score <= n_col) ; + ASSERT (head [score] >= EMPTY) ; + + /* now add this column to dList at proper score location */ + next_col = head [score] ; + Col [c].shared3.prev = EMPTY ; + Col [c].shared4.degree_next = next_col ; + + /* if there already was a column with the same score, set its */ + /* previous pointer to this new column */ + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = c ; + } + head [score] = c ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, score) ; + +#ifndef NDEBUG + debug_count++ ; +#endif /* NDEBUG */ + + } + } + +#ifndef NDEBUG + DEBUG1 (("colamd: Live cols %d out of %d, non-princ: %d\n", + debug_count, n_col, n_col-debug_count)) ; + ASSERT (debug_count == n_col2) ; + debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2, max_deg) ; +#endif /* NDEBUG */ + + /* === Return number of remaining columns, and max row degree =========== */ + + *p_n_col2 = n_col2 ; + *p_n_row2 = n_row2 ; + *p_max_deg = max_deg ; +} + + +/* ========================================================================== */ +/* === find_ordering ======================================================== */ +/* ========================================================================== */ + +/* + Order the principal columns of the supercolumn form of the matrix + (no supercolumns on input). Uses a minimum approximate column minimum + degree ordering method. Not user-callable. +*/ + +PRIVATE int find_ordering /* return the number of garbage collections */ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows of A */ + int n_col, /* number of columns of A */ + int Alen, /* size of A, 2*nnz + n_col or larger */ + Colamd_Row Row [], /* of size n_row+1 */ + Colamd_Col Col [], /* of size n_col+1 */ + int A [], /* column form and row form of A */ + int head [], /* of size n_col+1 */ + int n_col2, /* Remaining columns to order */ + int max_deg, /* Maximum row degree */ + int pfree /* index of first free slot (2*nnz on entry) */ +) +{ + /* === Local variables ================================================== */ + + int k ; /* current pivot ordering step */ + int pivot_col ; /* current pivot column */ + int *cp ; /* a column pointer */ + int *rp ; /* a row pointer */ + int pivot_row ; /* current pivot row */ + int *new_cp ; /* modified column pointer */ + int *new_rp ; /* modified row pointer */ + int pivot_row_start ; /* pointer to start of pivot row */ + int pivot_row_degree ; /* number of columns in pivot row */ + int pivot_row_length ; /* number of supercolumns in pivot row */ + int pivot_col_score ; /* score of pivot column */ + int needed_memory ; /* free space needed for pivot row */ + int *cp_end ; /* pointer to the end of a column */ + int *rp_end ; /* pointer to the end of a row */ + int row ; /* a row index */ + int col ; /* a column index */ + int max_score ; /* maximum possible score */ + int cur_score ; /* score of current column */ + unsigned int hash ; /* hash value for supernode detection */ + int head_column ; /* head of hash bucket */ + int first_col ; /* first column in hash bucket */ + int tag_mark ; /* marker value for mark array */ + int row_mark ; /* Row [row].shared2.mark */ + int set_difference ; /* set difference size of row with pivot row */ + int min_score ; /* smallest column score */ + int col_thickness ; /* "thickness" (no. of columns in a supercol) */ + int max_mark ; /* maximum value of tag_mark */ + int pivot_col_thickness ; /* number of columns represented by pivot col */ + int prev_col ; /* Used by Dlist operations. */ + int next_col ; /* Used by Dlist operations. */ + int ngarbage ; /* number of garbage collections performed */ + +#ifndef NDEBUG + int debug_d ; /* debug loop counter */ + int debug_step = 0 ; /* debug loop counter */ +#endif /* NDEBUG */ + + /* === Initialization and clear mark ==================================== */ + + max_mark = INT_MAX - n_col ; /* INT_MAX defined in */ + tag_mark = clear_mark (n_row, Row) ; + min_score = 0 ; + ngarbage = 0 ; + DEBUG1 (("colamd: Ordering, n_col2=%d\n", n_col2)) ; + + /* === Order the columns ================================================ */ + + for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */) + { + +#ifndef NDEBUG + if (debug_step % 100 == 0) + { + DEBUG2 (("\n... Step k: %d out of n_col2: %d\n", k, n_col2)) ; + } + else + { + DEBUG3 (("\n----------Step k: %d out of n_col2: %d\n", k, n_col2)) ; + } + debug_step++ ; + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + + /* === Select pivot column, and order it ============================ */ + + /* make sure degree list isn't empty */ + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (head [min_score] >= EMPTY) ; + +#ifndef NDEBUG + for (debug_d = 0 ; debug_d < min_score ; debug_d++) + { + ASSERT (head [debug_d] == EMPTY) ; + } +#endif /* NDEBUG */ + + /* get pivot column from head of minimum degree list */ + while (head [min_score] == EMPTY && min_score < n_col) + { + min_score++ ; + } + pivot_col = head [min_score] ; + ASSERT (pivot_col >= 0 && pivot_col <= n_col) ; + next_col = Col [pivot_col].shared4.degree_next ; + head [min_score] = next_col ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = EMPTY ; + } + + ASSERT (COL_IS_ALIVE (pivot_col)) ; + DEBUG3 (("Pivot col: %d\n", pivot_col)) ; + + /* remember score for defrag check */ + pivot_col_score = Col [pivot_col].shared2.score ; + + /* the pivot column is the kth column in the pivot order */ + Col [pivot_col].shared2.order = k ; + + /* increment order count by column thickness */ + pivot_col_thickness = Col [pivot_col].shared1.thickness ; + k += pivot_col_thickness ; + ASSERT (pivot_col_thickness > 0) ; + + /* === Garbage_collection, if necessary ============================= */ + + needed_memory = MIN (pivot_col_score, n_col - k) ; + if (pfree + needed_memory >= Alen) + { + pfree = garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ; + ngarbage++ ; + /* after garbage collection we will have enough */ + ASSERT (pfree + needed_memory < Alen) ; + /* garbage collection has wiped out the Row[].shared2.mark array */ + tag_mark = clear_mark (n_row, Row) ; + +#ifndef NDEBUG + debug_matrix (n_row, n_col, Row, Col, A) ; +#endif /* NDEBUG */ + } + + /* === Compute pivot row pattern ==================================== */ + + /* get starting location for this new merged row */ + pivot_row_start = pfree ; + + /* initialize new row counts to zero */ + pivot_row_degree = 0 ; + + /* tag pivot column as having been visited so it isn't included */ + /* in merged pivot row */ + Col [pivot_col].shared1.thickness = -pivot_col_thickness ; + + /* pivot row is the union of all rows in the pivot column pattern */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + DEBUG4 (("Pivot col pattern %d %d\n", ROW_IS_ALIVE (row), row)) ; + /* skip if row is dead */ + if (ROW_IS_DEAD (row)) + { + continue ; + } + rp = &A [Row [row].start] ; + rp_end = rp + Row [row].length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + /* add the column, if alive and untagged */ + col_thickness = Col [col].shared1.thickness ; + if (col_thickness > 0 && COL_IS_ALIVE (col)) + { + /* tag column in pivot row */ + Col [col].shared1.thickness = -col_thickness ; + ASSERT (pfree < Alen) ; + /* place column in pivot row */ + A [pfree++] = col ; + pivot_row_degree += col_thickness ; + } + } + } + + /* clear tag on pivot column */ + Col [pivot_col].shared1.thickness = pivot_col_thickness ; + max_deg = MAX (max_deg, pivot_row_degree) ; + +#ifndef NDEBUG + DEBUG3 (("check2\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Kill all rows used to construct pivot row ==================== */ + + /* also kill pivot row, temporarily */ + cp = &A [Col [pivot_col].start] ; + cp_end = cp + Col [pivot_col].length ; + while (cp < cp_end) + { + /* may be killing an already dead row */ + row = *cp++ ; + DEBUG3 (("Kill row in pivot col: %d\n", row)) ; + KILL_ROW (row) ; + } + + /* === Select a row index to use as the new pivot row =============== */ + + pivot_row_length = pfree - pivot_row_start ; + if (pivot_row_length > 0) + { + /* pick the "pivot" row arbitrarily (first row in col) */ + pivot_row = A [Col [pivot_col].start] ; + DEBUG3 (("Pivotal row is %d\n", pivot_row)) ; + } + else + { + /* there is no pivot row, since it is of zero length */ + pivot_row = EMPTY ; + ASSERT (pivot_row_length == 0) ; + } + ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ; + + /* === Approximate degree computation =============================== */ + + /* Here begins the computation of the approximate degree. The column */ + /* score is the sum of the pivot row "length", plus the size of the */ + /* set differences of each row in the column minus the pattern of the */ + /* pivot row itself. The column ("thickness") itself is also */ + /* excluded from the column score (we thus use an approximate */ + /* external degree). */ + + /* The time taken by the following code (compute set differences, and */ + /* add them up) is proportional to the size of the data structure */ + /* being scanned - that is, the sum of the sizes of each column in */ + /* the pivot row. Thus, the amortized time to compute a column score */ + /* is proportional to the size of that column (where size, in this */ + /* context, is the column "length", or the number of row indices */ + /* in that column). The number of row indices in a column is */ + /* monotonically non-decreasing, from the length of the original */ + /* column on input to colamd. */ + + /* === Compute set differences ====================================== */ + + DEBUG3 (("** Computing set differences phase. **\n")) ; + + /* pivot row is currently dead - it will be revived later. */ + + DEBUG3 (("Pivot row: ")) ; + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + DEBUG3 (("Col: %d\n", col)) ; + + /* clear tags used to construct pivot row pattern */ + col_thickness = -Col [col].shared1.thickness ; + ASSERT (col_thickness > 0) ; + Col [col].shared1.thickness = col_thickness ; + + /* === Remove column from degree list =========================== */ + + cur_score = Col [col].shared2.score ; + prev_col = Col [col].shared3.prev ; + next_col = Col [col].shared4.degree_next ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (cur_score >= EMPTY) ; + if (prev_col == EMPTY) + { + head [cur_score] = next_col ; + } + else + { + Col [prev_col].shared4.degree_next = next_col ; + } + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = prev_col ; + } + + /* === Scan the column ========================================== */ + + cp = &A [Col [col].start] ; + cp_end = cp + Col [col].length ; + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + continue ; + } + ASSERT (row != pivot_row) ; + set_difference = row_mark - tag_mark ; + /* check if the row has been seen yet */ + if (set_difference < 0) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + set_difference = Row [row].shared1.degree ; + } + /* subtract column thickness from this row's set difference */ + set_difference -= col_thickness ; + ASSERT (set_difference >= 0) ; + /* absorb this row if the set difference becomes zero */ + if (set_difference == 0) + { + DEBUG3 (("aggressive absorption. Row: %d\n", row)) ; + KILL_ROW (row) ; + } + else + { + /* save the new mark */ + Row [row].shared2.mark = set_difference + tag_mark ; + } + } + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k-pivot_row_degree, max_deg) ; +#endif /* NDEBUG */ + + /* === Add up set differences for each column ======================= */ + + DEBUG3 (("** Adding set differences phase. **\n")) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + /* get a column */ + col = *rp++ ; + ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ; + hash = 0 ; + cur_score = 0 ; + cp = &A [Col [col].start] ; + /* compact the column */ + new_cp = cp ; + cp_end = cp + Col [col].length ; + + DEBUG4 (("Adding set diffs for Col: %d.\n", col)) ; + + while (cp < cp_end) + { + /* get a row */ + row = *cp++ ; + ASSERT(row >= 0 && row < n_row) ; + row_mark = Row [row].shared2.mark ; + /* skip if dead */ + if (ROW_IS_MARKED_DEAD (row_mark)) + { + continue ; + } + ASSERT (row_mark > tag_mark) ; + /* compact the column */ + *new_cp++ = row ; + /* compute hash function */ + hash += row ; + /* add set difference */ + cur_score += row_mark - tag_mark ; + /* integer overflow... */ + cur_score = MIN (cur_score, n_col) ; + } + + /* recompute the column's length */ + Col [col].length = (int) (new_cp - &A [Col [col].start]) ; + + /* === Further mass elimination ================================= */ + + if (Col [col].length == 0) + { + DEBUG4 (("further mass elimination. Col: %d\n", col)) ; + /* nothing left but the pivot row in this column */ + KILL_PRINCIPAL_COL (col) ; + pivot_row_degree -= Col [col].shared1.thickness ; + ASSERT (pivot_row_degree >= 0) ; + /* order it */ + Col [col].shared2.order = k ; + /* increment order count by column thickness */ + k += Col [col].shared1.thickness ; + } + else + { + /* === Prepare for supercolumn detection ==================== */ + + DEBUG4 (("Preparing supercol detection for Col: %d.\n", col)) ; + + /* save score so far */ + Col [col].shared2.score = cur_score ; + + /* add column to hash table, for supercolumn detection */ + hash %= n_col + 1 ; + + DEBUG4 ((" Hash = %d, n_col = %d.\n", hash, n_col)) ; + ASSERT (hash <= n_col) ; + + head_column = head [hash] ; + if (head_column > EMPTY) + { + /* degree list "hash" is non-empty, use prev (shared3) of */ + /* first column in degree list as head of hash bucket */ + first_col = Col [head_column].shared3.headhash ; + Col [head_column].shared3.headhash = col ; + } + else + { + /* degree list "hash" is empty, use head as hash bucket */ + first_col = - (head_column + 2) ; + head [hash] = - (col + 2) ; + } + Col [col].shared4.hash_next = first_col ; + + /* save hash function in Col [col].shared3.hash */ + Col [col].shared3.hash = (int) hash ; + ASSERT (COL_IS_ALIVE (col)) ; + } + } + + /* The approximate external column degree is now computed. */ + + /* === Supercolumn detection ======================================== */ + + DEBUG3 (("** Supercolumn detection phase. **\n")) ; + + detect_super_cols ( + +#ifndef NDEBUG + n_col, Row, +#endif /* NDEBUG */ + + Col, A, head, pivot_row_start, pivot_row_length) ; + + /* === Kill the pivotal column ====================================== */ + + KILL_PRINCIPAL_COL (pivot_col) ; + + /* === Clear mark =================================================== */ + + tag_mark += (max_deg + 1) ; + if (tag_mark >= max_mark) + { + DEBUG2 (("clearing tag_mark\n")) ; + tag_mark = clear_mark (n_row, Row) ; + } + +#ifndef NDEBUG + DEBUG3 (("check3\n")) ; + debug_mark (n_row, Row, tag_mark, max_mark) ; +#endif /* NDEBUG */ + + /* === Finalize the new pivot row, and column scores ================ */ + + DEBUG3 (("** Finalize scores phase. **\n")) ; + + /* for each column in pivot row */ + rp = &A [pivot_row_start] ; + /* compact the pivot row */ + new_rp = rp ; + rp_end = rp + pivot_row_length ; + while (rp < rp_end) + { + col = *rp++ ; + /* skip dead columns */ + if (COL_IS_DEAD (col)) + { + continue ; + } + *new_rp++ = col ; + /* add new pivot row to column */ + A [Col [col].start + (Col [col].length++)] = pivot_row ; + + /* retrieve score so far and add on pivot row's degree. */ + /* (we wait until here for this in case the pivot */ + /* row's degree was reduced due to mass elimination). */ + cur_score = Col [col].shared2.score + pivot_row_degree ; + + /* calculate the max possible score as the number of */ + /* external columns minus the 'k' value minus the */ + /* columns thickness */ + max_score = n_col - k - Col [col].shared1.thickness ; + + /* make the score the external degree of the union-of-rows */ + cur_score -= Col [col].shared1.thickness ; + + /* make sure score is less or equal than the max score */ + cur_score = MIN (cur_score, max_score) ; + ASSERT (cur_score >= 0) ; + + /* store updated score */ + Col [col].shared2.score = cur_score ; + + /* === Place column back in degree list ========================= */ + + ASSERT (min_score >= 0) ; + ASSERT (min_score <= n_col) ; + ASSERT (cur_score >= 0) ; + ASSERT (cur_score <= n_col) ; + ASSERT (head [cur_score] >= EMPTY) ; + next_col = head [cur_score] ; + Col [col].shared4.degree_next = next_col ; + Col [col].shared3.prev = EMPTY ; + if (next_col != EMPTY) + { + Col [next_col].shared3.prev = col ; + } + head [cur_score] = col ; + + /* see if this score is less than current min */ + min_score = MIN (min_score, cur_score) ; + + } + +#ifndef NDEBUG + debug_deg_lists (n_row, n_col, Row, Col, head, + min_score, n_col2-k, max_deg) ; +#endif /* NDEBUG */ + + /* === Resurrect the new pivot row ================================== */ + + if (pivot_row_degree > 0) + { + /* update pivot row length to reflect any cols that were killed */ + /* during super-col detection and mass elimination */ + Row [pivot_row].start = pivot_row_start ; + Row [pivot_row].length = (int) (new_rp - &A[pivot_row_start]) ; + Row [pivot_row].shared1.degree = pivot_row_degree ; + Row [pivot_row].shared2.mark = 0 ; + /* pivot row is no longer dead */ + } + } + + /* === All principal columns have now been ordered ====================== */ + + return (ngarbage) ; +} + + +/* ========================================================================== */ +/* === order_children ======================================================= */ +/* ========================================================================== */ + +/* + The find_ordering routine has ordered all of the principal columns (the + representatives of the supercolumns). The non-principal columns have not + yet been ordered. This routine orders those columns by walking up the + parent tree (a column is a child of the column which absorbed it). The + final permutation vector is then placed in p [0 ... n_col-1], with p [0] + being the first column, and p [n_col-1] being the last. It doesn't look + like it at first glance, but be assured that this routine takes time linear + in the number of columns. Although not immediately obvious, the time + taken by this routine is O (n_col), that is, linear in the number of + columns. Not user-callable. +*/ + +PRIVATE void order_children +( + /* === Parameters ======================================================= */ + + int n_col, /* number of columns of A */ + Colamd_Col Col [], /* of size n_col+1 */ + int p [] /* p [0 ... n_col-1] is the column permutation*/ +) +{ + /* === Local variables ================================================== */ + + int i ; /* loop counter for all columns */ + int c ; /* column index */ + int parent ; /* index of column's parent */ + int order ; /* column's order */ + + /* === Order each non-principal column ================================== */ + + for (i = 0 ; i < n_col ; i++) + { + /* find an un-ordered non-principal column */ + ASSERT (COL_IS_DEAD (i)) ; + if (!COL_IS_DEAD_PRINCIPAL (i) && Col [i].shared2.order == EMPTY) + { + parent = i ; + /* once found, find its principal parent */ + do + { + parent = Col [parent].shared1.parent ; + } while (!COL_IS_DEAD_PRINCIPAL (parent)) ; + + /* now, order all un-ordered non-principal columns along path */ + /* to this parent. collapse tree at the same time */ + c = i ; + /* get order of parent */ + order = Col [parent].shared2.order ; + + do + { + ASSERT (Col [c].shared2.order == EMPTY) ; + + /* order this column */ + Col [c].shared2.order = order++ ; + /* collaps tree */ + Col [c].shared1.parent = parent ; + + /* get immediate parent of this column */ + c = Col [c].shared1.parent ; + + /* continue until we hit an ordered column. There are */ + /* guarranteed not to be anymore unordered columns */ + /* above an ordered column */ + } while (Col [c].shared2.order == EMPTY) ; + + /* re-order the super_col parent to largest order for this group */ + Col [parent].shared2.order = order ; + } + } + + /* === Generate the permutation ========================================= */ + + for (c = 0 ; c < n_col ; c++) + { + p [Col [c].shared2.order] = c ; + } +} + + +/* ========================================================================== */ +/* === detect_super_cols ==================================================== */ +/* ========================================================================== */ + +/* + Detects supercolumns by finding matches between columns in the hash buckets. + Check amongst columns in the set A [row_start ... row_start + row_length-1]. + The columns under consideration are currently *not* in the degree lists, + and have already been placed in the hash buckets. + + The hash bucket for columns whose hash function is equal to h is stored + as follows: + + if head [h] is >= 0, then head [h] contains a degree list, so: + + head [h] is the first column in degree bucket h. + Col [head [h]].headhash gives the first column in hash bucket h. + + otherwise, the degree list is empty, and: + + -(head [h] + 2) is the first column in hash bucket h. + + For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous + column" pointer. Col [c].shared3.hash is used instead as the hash number + for that column. The value of Col [c].shared4.hash_next is the next column + in the same hash bucket. + + Assuming no, or "few" hash collisions, the time taken by this routine is + linear in the sum of the sizes (lengths) of each column whose score has + just been computed in the approximate degree computation. + Not user-callable. +*/ + +PRIVATE void detect_super_cols +( + /* === Parameters ======================================================= */ + +#ifndef NDEBUG + /* these two parameters are only needed when debugging is enabled: */ + int n_col, /* number of columns of A */ + Colamd_Row Row [], /* of size n_row+1 */ +#endif /* NDEBUG */ + + Colamd_Col Col [], /* of size n_col+1 */ + int A [], /* row indices of A */ + int head [], /* head of degree lists and hash buckets */ + int row_start, /* pointer to set of columns to check */ + int row_length /* number of columns to check */ +) +{ + /* === Local variables ================================================== */ + + int hash ; /* hash value for a column */ + int *rp ; /* pointer to a row */ + int c ; /* a column index */ + int super_c ; /* column index of the column to absorb into */ + int *cp1 ; /* column pointer for column super_c */ + int *cp2 ; /* column pointer for column c */ + int length ; /* length of column super_c */ + int prev_c ; /* column preceding c in hash bucket */ + int i ; /* loop counter */ + int *rp_end ; /* pointer to the end of the row */ + int col ; /* a column index in the row to check */ + int head_column ; /* first column in hash bucket or degree list */ + int first_col ; /* first column in hash bucket */ + + /* === Consider each column in the row ================================== */ + + rp = &A [row_start] ; + rp_end = rp + row_length ; + while (rp < rp_end) + { + col = *rp++ ; + if (COL_IS_DEAD (col)) + { + continue ; + } + + /* get hash number for this column */ + hash = Col [col].shared3.hash ; + ASSERT (hash <= n_col) ; + + /* === Get the first column in this hash bucket ===================== */ + + head_column = head [hash] ; + if (head_column > EMPTY) + { + first_col = Col [head_column].shared3.headhash ; + } + else + { + first_col = - (head_column + 2) ; + } + + /* === Consider each column in the hash bucket ====================== */ + + for (super_c = first_col ; super_c != EMPTY ; + super_c = Col [super_c].shared4.hash_next) + { + ASSERT (COL_IS_ALIVE (super_c)) ; + ASSERT (Col [super_c].shared3.hash == hash) ; + length = Col [super_c].length ; + + /* prev_c is the column preceding column c in the hash bucket */ + prev_c = super_c ; + + /* === Compare super_c with all columns after it ================ */ + + for (c = Col [super_c].shared4.hash_next ; + c != EMPTY ; c = Col [c].shared4.hash_next) + { + ASSERT (c != super_c) ; + ASSERT (COL_IS_ALIVE (c)) ; + ASSERT (Col [c].shared3.hash == hash) ; + + /* not identical if lengths or scores are different */ + if (Col [c].length != length || + Col [c].shared2.score != Col [super_c].shared2.score) + { + prev_c = c ; + continue ; + } + + /* compare the two columns */ + cp1 = &A [Col [super_c].start] ; + cp2 = &A [Col [c].start] ; + + for (i = 0 ; i < length ; i++) + { + /* the columns are "clean" (no dead rows) */ + ASSERT (ROW_IS_ALIVE (*cp1)) ; + ASSERT (ROW_IS_ALIVE (*cp2)) ; + /* row indices will same order for both supercols, */ + /* no gather scatter nessasary */ + if (*cp1++ != *cp2++) + { + break ; + } + } + + /* the two columns are different if the for-loop "broke" */ + if (i != length) + { + prev_c = c ; + continue ; + } + + /* === Got it! two columns are identical =================== */ + + ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ; + + Col [super_c].shared1.thickness += Col [c].shared1.thickness ; + Col [c].shared1.parent = super_c ; + KILL_NON_PRINCIPAL_COL (c) ; + /* order c later, in order_children() */ + Col [c].shared2.order = EMPTY ; + /* remove c from hash bucket */ + Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ; + } + } + + /* === Empty this hash bucket ======================================= */ + + if (head_column > EMPTY) + { + /* corresponding degree list "hash" is not empty */ + Col [head_column].shared3.headhash = EMPTY ; + } + else + { + /* corresponding degree list "hash" is empty */ + head [hash] = EMPTY ; + } + } +} + + +/* ========================================================================== */ +/* === garbage_collection =================================================== */ +/* ========================================================================== */ + +/* + Defragments and compacts columns and rows in the workspace A. Used when + all avaliable memory has been used while performing row merging. Returns + the index of the first free position in A, after garbage collection. The + time taken by this routine is linear is the size of the array A, which is + itself linear in the number of nonzeros in the input matrix. + Not user-callable. +*/ + +PRIVATE int garbage_collection /* returns the new value of pfree */ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows */ + int n_col, /* number of columns */ + Colamd_Row Row [], /* row info */ + Colamd_Col Col [], /* column info */ + int A [], /* A [0 ... Alen-1] holds the matrix */ + int *pfree /* &A [0] ... pfree is in use */ +) +{ + /* === Local variables ================================================== */ + + int *psrc ; /* source pointer */ + int *pdest ; /* destination pointer */ + int j ; /* counter */ + int r ; /* a row index */ + int c ; /* a column index */ + int length ; /* length of a row or column */ + +#ifndef NDEBUG + int debug_rows ; + DEBUG2 (("Defrag..\n")) ; + for (psrc = &A[0] ; psrc < pfree ; psrc++) ASSERT (*psrc >= 0) ; + debug_rows = 0 ; +#endif /* NDEBUG */ + + /* === Defragment the columns =========================================== */ + + pdest = &A[0] ; + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + psrc = &A [Col [c].start] ; + + /* move and compact the column */ + ASSERT (pdest <= psrc) ; + Col [c].start = (int) (pdest - &A [0]) ; + length = Col [c].length ; + for (j = 0 ; j < length ; j++) + { + r = *psrc++ ; + if (ROW_IS_ALIVE (r)) + { + *pdest++ = r ; + } + } + Col [c].length = (int) (pdest - &A [Col [c].start]) ; + } + } + + /* === Prepare to defragment the rows =================================== */ + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + if (Row [r].length == 0) + { + /* this row is of zero length. cannot compact it, so kill it */ + DEBUG3 (("Defrag row kill\n")) ; + KILL_ROW (r) ; + } + else + { + /* save first column index in Row [r].shared2.first_column */ + psrc = &A [Row [r].start] ; + Row [r].shared2.first_column = *psrc ; + ASSERT (ROW_IS_ALIVE (r)) ; + /* flag the start of the row with the one's complement of row */ + *psrc = ONES_COMPLEMENT (r) ; + +#ifndef NDEBUG + debug_rows++ ; +#endif /* NDEBUG */ + + } + } + } + + /* === Defragment the rows ============================================== */ + + psrc = pdest ; + while (psrc < pfree) + { + /* find a negative number ... the start of a row */ + if (*psrc++ < 0) + { + psrc-- ; + /* get the row index */ + r = ONES_COMPLEMENT (*psrc) ; + ASSERT (r >= 0 && r < n_row) ; + /* restore first column index */ + *psrc = Row [r].shared2.first_column ; + ASSERT (ROW_IS_ALIVE (r)) ; + + /* move and compact the row */ + ASSERT (pdest <= psrc) ; + Row [r].start = (int) (pdest - &A [0]) ; + length = Row [r].length ; + for (j = 0 ; j < length ; j++) + { + c = *psrc++ ; + if (COL_IS_ALIVE (c)) + { + *pdest++ = c ; + } + } + Row [r].length = (int) (pdest - &A [Row [r].start]) ; + +#ifndef NDEBUG + debug_rows-- ; +#endif /* NDEBUG */ + + } + } + /* ensure we found all the rows */ + ASSERT (debug_rows == 0) ; + + /* === Return the new value of pfree ==================================== */ + + return ((int) (pdest - &A [0])) ; +} + + +/* ========================================================================== */ +/* === clear_mark =========================================================== */ +/* ========================================================================== */ + +/* + Clears the Row [].shared2.mark array, and returns the new tag_mark. + Return value is the new tag_mark. Not user-callable. +*/ + +PRIVATE int clear_mark /* return the new value for tag_mark */ +( + /* === Parameters ======================================================= */ + + int n_row, /* number of rows in A */ + Colamd_Row Row [] /* Row [0 ... n_row-1].shared2.mark is set to zero */ +) +{ + /* === Local variables ================================================== */ + + int r ; + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + Row [r].shared2.mark = 0 ; + } + } + return (1) ; +} + + +/* ========================================================================== */ +/* === print_report ========================================================= */ +/* ========================================================================== */ + +PRIVATE void print_report +( + char *method, + int stats [COLAMD_STATS] +) +{ + + int i1, i2, i3 ; + + if (!stats) + { + PRINTF ("%s: No statistics available.\n", method) ; + return ; + } + + i1 = stats [COLAMD_INFO1] ; + i2 = stats [COLAMD_INFO2] ; + i3 = stats [COLAMD_INFO3] ; + + if (stats [COLAMD_STATUS] >= 0) + { + PRINTF ("%s: OK. ", method) ; + } + else + { + PRINTF ("%s: ERROR. ", method) ; + } + + switch (stats [COLAMD_STATUS]) + { + + case COLAMD_OK_BUT_JUMBLED: + + PRINTF ("Matrix has unsorted or duplicate row indices.\n") ; + + PRINTF ("%s: number of duplicate or out-of-order row indices: %d\n", + method, i3) ; + + PRINTF ("%s: last seen duplicate or out-of-order row index: %d\n", + method, INDEX (i2)) ; + + PRINTF ("%s: last seen in column: %d", + method, INDEX (i1)) ; + + /* no break - fall through to next case instead */ + + case COLAMD_OK: + + PRINTF ("\n") ; + + PRINTF ("%s: number of dense or empty rows ignored: %d\n", + method, stats [COLAMD_DENSE_ROW]) ; + + PRINTF ("%s: number of dense or empty columns ignored: %d\n", + method, stats [COLAMD_DENSE_COL]) ; + + PRINTF ("%s: number of garbage collections performed: %d\n", + method, stats [COLAMD_DEFRAG_COUNT]) ; + break ; + + case COLAMD_ERROR_A_not_present: + + PRINTF ("Array A (row indices of matrix) not present.\n") ; + break ; + + case COLAMD_ERROR_p_not_present: + + PRINTF ("Array p (column pointers for matrix) not present.\n") ; + break ; + + case COLAMD_ERROR_nrow_negative: + + PRINTF ("Invalid number of rows (%d).\n", i1) ; + break ; + + case COLAMD_ERROR_ncol_negative: + + PRINTF ("Invalid number of columns (%d).\n", i1) ; + break ; + + case COLAMD_ERROR_nnz_negative: + + PRINTF ("Invalid number of nonzero entries (%d).\n", i1) ; + break ; + + case COLAMD_ERROR_p0_nonzero: + + PRINTF ("Invalid column pointer, p [0] = %d, must be zero.\n", i1) ; + break ; + + case COLAMD_ERROR_A_too_small: + + PRINTF ("Array A too small.\n") ; + PRINTF (" Need Alen >= %d, but given only Alen = %d.\n", + i1, i2) ; + break ; + + case COLAMD_ERROR_col_length_negative: + + PRINTF + ("Column %d has a negative number of nonzero entries (%d).\n", + INDEX (i1), i2) ; + break ; + + case COLAMD_ERROR_row_index_out_of_bounds: + + PRINTF + ("Row index (row %d) out of bounds (%d to %d) in column %d.\n", + INDEX (i2), INDEX (0), INDEX (i3-1), INDEX (i1)) ; + break ; + + case COLAMD_ERROR_out_of_memory: + + PRINTF ("Out of memory.\n") ; + break ; + + case COLAMD_ERROR_internal_error: + + /* if this happens, there is a bug in the code */ + PRINTF + ("Internal error! Please contact authors (davis@cise.ufl.edu).\n") ; + break ; + } +} + + + + +/* ========================================================================== */ +/* === colamd debugging routines ============================================ */ +/* ========================================================================== */ + +/* When debugging is disabled, the remainder of this file is ignored. */ + +#ifndef NDEBUG + + +/* ========================================================================== */ +/* === debug_structures ===================================================== */ +/* ========================================================================== */ + +/* + At this point, all empty rows and columns are dead. All live columns + are "clean" (containing no dead rows) and simplicial (no supercolumns + yet). Rows may contain dead columns, but all live rows contain at + least one live column. +*/ + +PRIVATE void debug_structures +( + /* === Parameters ======================================================= */ + + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [], + int n_col2 +) +{ + /* === Local variables ================================================== */ + + int i ; + int c ; + int *cp ; + int *cp_end ; + int len ; + int score ; + int r ; + int *rp ; + int *rp_end ; + int deg ; + + /* === Check A, Row, and Col ============================================ */ + + for (c = 0 ; c < n_col ; c++) + { + if (COL_IS_ALIVE (c)) + { + len = Col [c].length ; + score = Col [c].shared2.score ; + DEBUG4 (("initial live col %5d %5d %5d\n", c, len, score)) ; + ASSERT (len > 0) ; + ASSERT (score >= 0) ; + ASSERT (Col [c].shared1.thickness == 1) ; + cp = &A [Col [c].start] ; + cp_end = cp + len ; + while (cp < cp_end) + { + r = *cp++ ; + ASSERT (ROW_IS_ALIVE (r)) ; + } + } + else + { + i = Col [c].shared2.order ; + ASSERT (i >= n_col2 && i < n_col) ; + } + } + + for (r = 0 ; r < n_row ; r++) + { + if (ROW_IS_ALIVE (r)) + { + i = 0 ; + len = Row [r].length ; + deg = Row [r].shared1.degree ; + ASSERT (len > 0) ; + ASSERT (deg > 0) ; + rp = &A [Row [r].start] ; + rp_end = rp + len ; + while (rp < rp_end) + { + c = *rp++ ; + if (COL_IS_ALIVE (c)) + { + i++ ; + } + } + ASSERT (i > 0) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_deg_lists ====================================================== */ +/* ========================================================================== */ + +/* + Prints the contents of the degree lists. Counts the number of columns + in the degree list and compares it to the total it should have. Also + checks the row degrees. +*/ + +PRIVATE void debug_deg_lists +( + /* === Parameters ======================================================= */ + + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int head [], + int min_score, + int should, + int max_deg +) +{ + /* === Local variables ================================================== */ + + int deg ; + int col ; + int have ; + int row ; + + /* === Check the degree lists =========================================== */ + + if (n_col > 10000 && colamd_debug <= 0) + { + return ; + } + have = 0 ; + DEBUG4 (("Degree lists: %d\n", min_score)) ; + for (deg = 0 ; deg <= n_col ; deg++) + { + col = head [deg] ; + if (col == EMPTY) + { + continue ; + } + DEBUG4 (("%d:", deg)) ; + while (col != EMPTY) + { + DEBUG4 ((" %d", col)) ; + have += Col [col].shared1.thickness ; + ASSERT (COL_IS_ALIVE (col)) ; + col = Col [col].shared4.degree_next ; + } + DEBUG4 (("\n")) ; + } + DEBUG4 (("should %d have %d\n", should, have)) ; + ASSERT (should == have) ; + + /* === Check the row degrees ============================================ */ + + if (n_row > 10000 && colamd_debug <= 0) + { + return ; + } + for (row = 0 ; row < n_row ; row++) + { + if (ROW_IS_ALIVE (row)) + { + ASSERT (Row [row].shared1.degree <= max_deg) ; + } + } +} + + +/* ========================================================================== */ +/* === debug_mark =========================================================== */ +/* ========================================================================== */ + +/* + Ensures that the tag_mark is less that the maximum and also ensures that + each entry in the mark array is less than the tag mark. +*/ + +PRIVATE void debug_mark +( + /* === Parameters ======================================================= */ + + int n_row, + Colamd_Row Row [], + int tag_mark, + int max_mark +) +{ + /* === Local variables ================================================== */ + + int r ; + + /* === Check the Row marks ============================================== */ + + ASSERT (tag_mark > 0 && tag_mark <= max_mark) ; + if (n_row > 10000 && colamd_debug <= 0) + { + return ; + } + for (r = 0 ; r < n_row ; r++) + { + ASSERT (Row [r].shared2.mark < tag_mark) ; + } +} + + +/* ========================================================================== */ +/* === debug_matrix ========================================================= */ +/* ========================================================================== */ + +/* + Prints out the contents of the columns and the rows. +*/ + +PRIVATE void debug_matrix +( + /* === Parameters ======================================================= */ + + int n_row, + int n_col, + Colamd_Row Row [], + Colamd_Col Col [], + int A [] +) +{ + /* === Local variables ================================================== */ + + int r ; + int c ; + int *rp ; + int *rp_end ; + int *cp ; + int *cp_end ; + + /* === Dump the rows and columns of the matrix ========================== */ + + if (colamd_debug < 3) + { + return ; + } + DEBUG3 (("DUMP MATRIX:\n")) ; + for (r = 0 ; r < n_row ; r++) + { + DEBUG3 (("Row %d alive? %d\n", r, ROW_IS_ALIVE (r))) ; + if (ROW_IS_DEAD (r)) + { + continue ; + } + DEBUG3 (("start %d length %d degree %d\n", + Row [r].start, Row [r].length, Row [r].shared1.degree)) ; + rp = &A [Row [r].start] ; + rp_end = rp + Row [r].length ; + while (rp < rp_end) + { + c = *rp++ ; + DEBUG4 ((" %d col %d\n", COL_IS_ALIVE (c), c)) ; + } + } + + for (c = 0 ; c < n_col ; c++) + { + DEBUG3 (("Col %d alive? %d\n", c, COL_IS_ALIVE (c))) ; + if (COL_IS_DEAD (c)) + { + continue ; + } + DEBUG3 (("start %d length %d shared1 %d shared2 %d\n", + Col [c].start, Col [c].length, + Col [c].shared1.thickness, Col [c].shared2.score)) ; + cp = &A [Col [c].start] ; + cp_end = cp + Col [c].length ; + while (cp < cp_end) + { + r = *cp++ ; + DEBUG4 ((" %d row %d\n", ROW_IS_ALIVE (r), r)) ; + } + } +} + +PRIVATE void colamd_get_debug +( + char *method +) +{ + colamd_debug = 0 ; /* no debug printing */ + + /* get "D" environment variable, which gives the debug printing level */ + if (getenv ("D")) + { + colamd_debug = atoi (getenv ("D")) ; + } + + DEBUG0 (("%s: debug version, D = %d (THIS WILL BE SLOW!)\n", + method, colamd_debug)) ; +} + +#endif /* NDEBUG */ + Index: src/tools/solver/lp_solve/colamd.h =================================================================== RCS file: src/tools/solver/lp_solve/colamd.h diff -N src/tools/solver/lp_solve/colamd.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/colamd.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,270 @@ +/* ========================================================================== */ +/* === colamd/symamd prototypes and definitions ============================= */ +/* ========================================================================== */ + +/* + You must include this file (colamd.h) in any routine that uses colamd, + symamd, or the related macros and definitions. + + Authors: + + The authors of the code itself are Stefan I. Larimore and Timothy A. + Davis (davis@cise.ufl.edu), University of Florida. The algorithm was + developed in collaboration with John Gilbert, Xerox PARC, and Esmond + Ng, Oak Ridge National Laboratory. + + Date: + + May 4, 2001. Version 2.1. + + Acknowledgements: + + This work was supported by the National Science Foundation, under + grants DMS-9504974 and DMS-9803599. + + Notice: + + Copyright (c) 1998-2001 by the University of Florida. + All Rights Reserved. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY + EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to use or copy this program for any + purpose, provided the above notices are retained on all copies. + User documentation of any code that uses this code must cite the + Authors, the Copyright, and "Used by permission." If this code is + accessible from within Matlab, then typing "help colamd" and "help + symamd" must cite the Authors. Permission to modify the code and to + distribute modified code is granted, provided the above notices are + retained, and a notice that the code was modified is included with the + above copyright notice. You must also retain the Availability + information below, of the original version. + + This software is provided free of charge. + + Availability: + + The colamd/symamd library is available at + + http://www.cise.ufl.edu/research/sparse/colamd + + This is the http://www.cise.ufl.edu/research/sparse/colamd/colamd.h + file. It is required by the colamd.c, colamdmex.c, and symamdmex.c + files, and by any C code that calls the routines whose prototypes are + listed below, or that uses the colamd/symamd definitions listed below. + +*/ + +#ifndef COLAMD_H +#define COLAMD_H + +/* ========================================================================== */ +/* === Include files ======================================================== */ +/* ========================================================================== */ + +#include + +/* ========================================================================== */ +/* === Knob and statistics definitions ====================================== */ +/* ========================================================================== */ + +/* size of the knobs [ ] array. Only knobs [0..1] are currently used. */ +#define COLAMD_KNOBS 20 + +/* number of output statistics. Only stats [0..6] are currently used. */ +#define COLAMD_STATS 20 + +/* knobs [0] and stats [0]: dense row knob and output statistic. */ +#define COLAMD_DENSE_ROW 0 + +/* knobs [1] and stats [1]: dense column knob and output statistic. */ +#define COLAMD_DENSE_COL 1 + +/* stats [2]: memory defragmentation count output statistic */ +#define COLAMD_DEFRAG_COUNT 2 + +/* stats [3]: colamd status: zero OK, > 0 warning or notice, < 0 error */ +#define COLAMD_STATUS 3 + +/* stats [4..6]: error info, or info on jumbled columns */ +#define COLAMD_INFO1 4 +#define COLAMD_INFO2 5 +#define COLAMD_INFO3 6 + +/* error codes returned in stats [3]: */ +#define COLAMD_OK (0) +#define COLAMD_OK_BUT_JUMBLED (1) +#define COLAMD_ERROR_A_not_present (-1) +#define COLAMD_ERROR_p_not_present (-2) +#define COLAMD_ERROR_nrow_negative (-3) +#define COLAMD_ERROR_ncol_negative (-4) +#define COLAMD_ERROR_nnz_negative (-5) +#define COLAMD_ERROR_p0_nonzero (-6) +#define COLAMD_ERROR_A_too_small (-7) +#define COLAMD_ERROR_col_length_negative (-8) +#define COLAMD_ERROR_row_index_out_of_bounds (-9) +#define COLAMD_ERROR_out_of_memory (-10) +#define COLAMD_ERROR_internal_error (-999) + + + +/* ========================================================================== */ +/* === Row and Column structures ============================================ */ +/* ========================================================================== */ + +/* User code that makes use of the colamd/symamd routines need not directly */ +/* reference these structures. They are used only for the COLAMD_RECOMMENDED */ +/* macro. */ + +typedef struct Colamd_Col_struct +{ + int start ; /* index for A of first row in this column, or DEAD */ + /* if column is dead */ + int length ; /* number of rows in this column */ + union + { + int thickness ; /* number of original columns represented by this */ + /* col, if the column is alive */ + int parent ; /* parent in parent tree super-column structure, if */ + /* the column is dead */ + } shared1 ; + union + { + int score ; /* the score used to maintain heap, if col is alive */ + int order ; /* pivot ordering of this column, if col is dead */ + } shared2 ; + union + { + int headhash ; /* head of a hash bucket, if col is at the head of */ + /* a degree list */ + int hash ; /* hash value, if col is not in a degree list */ + int prev ; /* previous column in degree list, if col is in a */ + /* degree list (but not at the head of a degree list) */ + } shared3 ; + union + { + int degree_next ; /* next column, if col is in a degree list */ + int hash_next ; /* next column, if col is in a hash list */ + } shared4 ; + +} Colamd_Col ; + +typedef struct Colamd_Row_struct +{ + int start ; /* index for A of first col in this row */ + int length ; /* number of principal columns in this row */ + union + { + int degree ; /* number of principal & non-principal columns in row */ + int p ; /* used as a row pointer in init_rows_cols () */ + } shared1 ; + union + { + int mark ; /* for computing set differences and marking dead rows*/ + int first_column ;/* first column in row (used in garbage collection) */ + } shared2 ; + +} Colamd_Row ; + +/* ========================================================================== */ +/* === Colamd recommended memory size ======================================= */ +/* ========================================================================== */ + +/* + The recommended length Alen of the array A passed to colamd is given by + the COLAMD_RECOMMENDED (nnz, n_row, n_col) macro. It returns -1 if any + argument is negative. 2*nnz space is required for the row and column + indices of the matrix. COLAMD_C (n_col) + COLAMD_R (n_row) space is + required for the Col and Row arrays, respectively, which are internal to + colamd. An additional n_col space is the minimal amount of "elbow room", + and nnz/5 more space is recommended for run time efficiency. + + This macro is not needed when using symamd. +*/ + +#define COLAMD_C(n_col) (((n_col) + 1) * sizeof (Colamd_Col) / sizeof (int)) +#define COLAMD_R(n_row) (((n_row) + 1) * sizeof (Colamd_Row) / sizeof (int)) + +#define COLAMD_RECOMMENDED(nnz, n_row, n_col) \ +( \ +((nnz) < 0 || (n_row) < 0 || (n_col) < 0) \ +? \ + (-1) \ +: \ + (2 * (nnz) + COLAMD_C (n_col) + COLAMD_R (n_row) + (n_col) + ((nnz) / 5)) \ +) + +/* ========================================================================== */ +/* === Prototypes of user-callable routines ================================= */ +/* ========================================================================== */ + +#ifdef __cplusplus + #define __EXTERN_C extern "C" +#else + #define __EXTERN_C +#endif + +#ifdef __cplusplus +__EXTERN_C { +#endif + + + +int colamd_recommended /* returns recommended value of Alen, */ + /* or (-1) if input arguments are erroneous */ +( + int nnz, /* nonzeros in A */ + int n_row, /* number of rows in A */ + int n_col /* number of columns in A */ +) ; + +void colamd_set_defaults /* sets default parameters */ +( /* knobs argument is modified on output */ + double knobs [COLAMD_KNOBS] /* parameter settings for colamd */ +) ; + +int colamd /* returns (1) if successful, (0) otherwise*/ +( /* A and p arguments are modified on output */ + int n_row, /* number of rows in A */ + int n_col, /* number of columns in A */ + int Alen, /* size of the array A */ + int A [], /* row indices of A, of size Alen */ + int p [], /* column pointers of A, of size n_col+1 */ + double knobs [COLAMD_KNOBS],/* parameter settings for colamd */ + int stats [COLAMD_STATS] /* colamd output statistics and error codes */ +) ; + +int symamd /* return (1) if OK, (0) otherwise */ +( + int n, /* number of rows and columns of A */ + int A [], /* row indices of A */ + int p [], /* column pointers of A */ + int perm [], /* output permutation, size n_col+1 */ + double knobs [COLAMD_KNOBS], /* parameters (uses defaults if NULL) */ + int stats [COLAMD_STATS], /* output statistics and error codes */ + void * (*allocate) (size_t, size_t), + /* pointer to calloc (ANSI C) or */ + /* mxCalloc (for Matlab mexFunction) */ + void (*release) (void *) + /* pointer to free (ANSI C) or */ + /* mxFree (for Matlab mexFunction) */ +) ; + +void colamd_report +( + int stats [COLAMD_STATS] +) ; + +void symamd_report +( + int stats [COLAMD_STATS] +) ; + +#endif /* COLAMD_H */ + + +#ifdef __cplusplus +} +#endif + Index: src/tools/solver/lp_solve/commonlib.c =================================================================== RCS file: src/tools/solver/lp_solve/commonlib.c diff -N src/tools/solver/lp_solve/commonlib.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/commonlib.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,429 @@ + +#include + +#ifdef INTEGERTIME +# include +#else +# include +#endif + +#include +#include +#ifdef WIN32 +# include /* Used in file search functions */ +#endif +#include +#include +#include "commonlib.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +/* Test code - Intel compiler only */ + +#if 0 +void initMemoryByMMX(unsigned char *memoryBuffer, unsigned long length, unsigned char value) +{ + Iu8vec8 initValue; + unsigned long i, size; + + size = sizeof( Iu8vec8 ); + for(i = 0; i < size; i++) + initValue[ i ] = value; + size = length / sizeof( Iu8vec8 ); + for(i = i; i < size; i++) + ((Iu8vec8 *) memoryBuffer)[ i ] = initValue; + empty(); +} +#endif + +/* Math functions */ +int mod(int n, int d) +{ + return(n % d); +} + +/* Array search functions */ +int findIndex(int target, int *attributes, int count, int offset) +{ + int focusPos, beginPos, endPos; + int focusAttrib, beginAttrib, endAttrib; + + /* Set starting and ending index offsets */ + beginPos = offset; + endPos = beginPos + count - 1; + if(endPos < beginPos) + return(-1); + + /* Do binary search logic based on a sorted attribute vector */ + focusPos = (beginPos + endPos) / 2; + beginAttrib = attributes[beginPos]; + focusAttrib = attributes[focusPos]; + endAttrib = attributes[endPos]; + + while(endPos - beginPos > LINEARSEARCH) { + if(beginAttrib == target) { + focusAttrib = beginAttrib; + endPos = beginPos; + } + else if(endAttrib == target) { + focusAttrib = endAttrib; + beginPos = endPos; + } + else if(focusAttrib < target) { + beginPos = focusPos + 1; + beginAttrib = attributes[beginPos]; + focusPos = (beginPos + endPos) / 2; + focusAttrib = attributes[focusPos]; + } + else if(focusAttrib > target) { + endPos = focusPos - 1; + endAttrib = attributes[endPos]; + focusPos = (beginPos + endPos) / 2; + focusAttrib = attributes[focusPos]; + } + else { + beginPos = focusPos; + endPos = focusPos; + } + } + + /* Do linear (unsorted) search logic */ + if(endPos - beginPos <= LINEARSEARCH) { + + /* CPU intensive loop; provide alternative evaluation models */ +#if defined DOFASTMATH + /* Do fast pointer arithmetic */ + int *attptr = attributes + beginPos; + while((beginPos < endPos) && ((*attptr) < target)) { + beginPos++; + attptr++; + } + focusAttrib = (*attptr); +#else + /* Do traditional indexed access */ + focusAttrib = attributes[beginPos]; + while((beginPos < endPos) && (focusAttrib < target)) { + beginPos++; + focusAttrib = attributes[beginPos]; + } +#endif + } + + /* Return the index if a match was found, or signal failure with a -1 */ + if(focusAttrib == target) /* Found; return retrieval index */ + return(beginPos); + else if(focusAttrib > target) /* Not found; last item */ + return(-beginPos); + else if(beginPos > offset+count-1) + return(-(endPos+1)); /* Not found; end of list */ + else + return(-(beginPos+1)); /* Not found; intermediate point */ + +} +int findIndexEx(void *target, void *attributes, int count, int offset, int recsize, findCompare_func findCompare) +{ + int focusPos, beginPos, endPos, compare; + void *focusAttrib, *beginAttrib, *endAttrib; + + /* Set starting and ending index offsets */ + beginPos = offset; + endPos = beginPos + count - 1; + if(endPos < beginPos) + return(-1); + + /* Do binary search logic based on a sorted attribute vector */ + focusPos = (beginPos + endPos) / 2; + beginAttrib = CMP_ATTRIBUTES(beginPos); + focusAttrib = CMP_ATTRIBUTES(focusPos); + endAttrib = CMP_ATTRIBUTES(endPos); + + while(endPos - beginPos > LINEARSEARCH) { + if(findCompare(target, beginAttrib) == 0) { + focusAttrib = beginAttrib; + endPos = beginPos; + } + else if(findCompare(target, endAttrib) == 0) { + focusAttrib = endAttrib; + beginPos = endPos; + } + else { + compare = findCompare(target, focusAttrib); + if(compare < 0) { + beginPos = focusPos + 1; + beginAttrib = CMP_ATTRIBUTES(beginPos); + focusPos = (beginPos + endPos) / 2; + focusAttrib = CMP_ATTRIBUTES(focusPos); + } + else if(compare > 0) { + endPos = focusPos - 1; + endAttrib = CMP_ATTRIBUTES(endPos); + focusPos = (beginPos + endPos) / 2; + focusAttrib = CMP_ATTRIBUTES(focusPos); + } + else { + beginPos = focusPos; + endPos = focusPos; + } + } + } + + /* Do linear (unsorted) search logic */ + if(endPos - beginPos <= LINEARSEARCH) { + + /* Do traditional indexed access */ + focusAttrib = CMP_ATTRIBUTES(beginPos); + if(beginPos == endPos) + compare = findCompare(target, focusAttrib); + else + while((beginPos < endPos) && + (compare = findCompare(target, focusAttrib) < 0)) { + beginPos++; + focusAttrib = CMP_ATTRIBUTES(beginPos); + } + } + + /* Return the index if a match was found, or signal failure with a -1 */ + if(compare == 0) /* Found; return retrieval index */ + return(beginPos); + else if(compare > 0) /* Not found; last item */ + return(-beginPos); + else if(beginPos > offset+count-1) + return(-(endPos+1)); /* Not found; end of list */ + else + return(-(beginPos+1)); /* Not found; intermediate point */ + +} + +/* Simple specialized bubble sort functions */ +int sortByREAL(int *item, REAL *weight, int size, int offset, MYBOOL unique) +{ + int i, ii, saveI; + REAL saveW; + + for(i = 1; i < size; i++) { + ii = i+offset-1; + while ((ii >= offset) && (weight[ii] >= weight[ii+1])) { + if(weight[ii] == weight[ii+1]) { + if(unique) + return(item[ii]); + } + else { + saveI = item[ii]; + saveW = weight[ii]; + item[ii] = item[ii+1]; + weight[ii] = weight[ii+1]; + item[ii+1] = saveI; + weight[ii+1] = saveW; + } + ii--; + } + } + return(0); +} +int sortByINT(int *item, int *weight, int size, int offset, MYBOOL unique) +{ + int i, ii, saveI; + int saveW; + + for(i = 1; i < size; i++) { + ii = i+offset-1; + while ((ii >= offset) && (weight[ii] >= weight[ii+1])) { + if(weight[ii] == weight[ii+1]) { + if(unique) + return(item[ii]); + } + else { + saveI = item[ii]; + saveW = weight[ii]; + item[ii] = item[ii+1]; + weight[ii] = weight[ii+1]; + item[ii+1] = saveI; + weight[ii+1] = saveW; + } + ii--; + } + } + return(0); +} +REAL sortREALByINT(REAL *item, int *weight, int size, int offset, MYBOOL unique) +{ + int i, ii, saveW; + REAL saveI; + + for(i = 1; i < size; i++) { + ii = i+offset-1; + while ((ii >= offset) && (weight[ii] >= weight[ii+1])) { + if(weight[ii] == weight[ii+1]) { + if(unique) + return(item[ii]); + } + else { + saveI = item[ii]; + saveW = weight[ii]; + item[ii] = item[ii+1]; + weight[ii] = weight[ii+1]; + item[ii+1] = saveI; + weight[ii+1] = saveW; + } + ii--; + } + } + return(0); +} + + +/* Time and message functions */ +double timeNow() +{ +#ifdef INTEGERTIME + return((double)time(NULL)); +#elif defined CLOCKTIME + return((double)clock()/CLOCKS_PER_SEC /* CLK_TCK */); +#else + struct timeb buf; + + ftime(&buf); + return((double)buf.time+((double) buf.millitm)/1000.0); +#endif +} + + +/* Miscellaneous reporting functions */ + +/* List a vector of INT values for the given index range */ +void blockWriteINT(FILE *output, char *label, int *vector, int first, int last) +{ + int i, k = 0; + + fprintf(output, label); + fprintf(output, "\n"); + for(i = first; i <= last; i++) { + fprintf(output, " %5d", vector[i]); + k++; + if(k % 12 == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(k % 12 != 0) + fprintf(output, "\n"); +} + +/* List a vector of MYBOOL values for the given index range */ +void blockWriteBOOL(FILE *output, char *label, MYBOOL *vector, int first, int last, MYBOOL asRaw) +{ + int i, k = 0; + + fprintf(output, label); + fprintf(output, "\n"); + for(i = first; i <= last; i++) { + if(asRaw) + fprintf(output, " %1d", vector[i]); + else + fprintf(output, " %5s", my_boolstr(vector[i])); + k++; + if(k % 36 == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(k % 36 != 0) + fprintf(output, "\n"); +} + +/* List a vector of REAL values for the given index range */ +void blockWriteREAL(FILE *output, char *label, REAL *vector, int first, int last) +{ + int i, k = 0; + + fprintf(output, label); + fprintf(output, "\n"); + for(i = first; i <= last; i++) { + fprintf(output, " %18g", vector[i]); + k++; + if(k % 4 == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(k % 4 != 0) + fprintf(output, "\n"); +} + + +/* CONSOLE vector and matrix printing routines */ +void printvec( int n, REAL *x, int modulo ) +{ + int i; + + if (modulo <= 0) modulo = 5; + for (i = 1; i<=n; i++) { + if(mod(i, modulo) == 1) + printf("\n%2d:%12g", i, x[i]); + else + printf(" %2d:%12g", i, x[i]); + } + if(i % modulo != 0) printf("\n"); +} + + +void printmatUT( int size, int n, REAL *U, int modulo) +{ + int i, ll; + ll = 0; + for(i = 1; i<=n; i++) { + printvec(n-i+1, &U[ll], modulo); + ll += size-i+1; + } +} + + +void printmatSQ( int size, int n, REAL *X, int modulo) +{ + int i, ll; + ll = 0; + for(i = 1; i<=n; i++) { + printvec(n, &X[ll], modulo); + ll += size; + } +} + +/* Miscellaneous file functions */ +#ifdef _MSC_VER +#if _MSC_VER < 1300 +# define intptr_t long +#endif + +int fileCount( char *filemask ) +{ + struct _finddata_t c_file; + intptr_t hFile; + int count = 0; + + /* Find first .c file in current directory */ + if( (hFile = _findfirst( filemask, &c_file )) == -1L ) + ; + /* Iterate over all matching names */ + else { + while( _findnext( hFile, &c_file ) == 0 ) + count++; + _findclose( hFile ); + } + return( count ); +} +MYBOOL fileSearchPath( char *envvar, char *searchfile, char *foundpath ) +{ + char pathbuffer[_MAX_PATH]; + + _searchenv( searchfile, envvar, pathbuffer ); + if(pathbuffer[0] == '\0') + return( FALSE ); + else { + if(foundpath != NULL) + strcpy(foundpath, pathbuffer); + return( TRUE ); + } +} +#endif Index: src/tools/solver/lp_solve/commonlib.h =================================================================== RCS file: src/tools/solver/lp_solve/commonlib.h diff -N src/tools/solver/lp_solve/commonlib.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/commonlib.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,158 @@ +#ifndef HEADER_commonlib +#define HEADER_commonlib + +#include +#include +#include + +#define BIGNUMBER 1.0e+30 +#define TINYNUMBER 1.0e-4 +#define MACHINEPREC 2.22e-16 +#define MATHPREC 1.0e-16 +#define ERRLIMIT 1.0e-6 + +#ifndef LINEARSEARCH + #define LINEARSEARCH 5 +#endif + +#if 0 + #define INTEGERTIME +#endif + +#ifndef MYBOOL +/* #define MYBOOL unsigned int */ + #define MYBOOL unsigned char +#endif + +#ifndef REAL + #define REAL double +#endif + +#ifndef my_boolstr + #define my_boolstr(x) (!(x) ? "FALSE" : "TRUE") +#endif + +#ifndef NULL + #define NULL 0 +#endif + +#ifndef FALSE + #define FALSE 0 + #define TRUE 1 +#endif + +#ifndef DOFASTMATH + #define DOFASTMATH +#endif + + +#ifndef CALLOC +#define CALLOC(ptr, nr)\ + if(!((void *) ptr = calloc((size_t)(nr), sizeof(*ptr))) && nr) {\ + printf("calloc of %d bytes failed on line %d of file %s\n",\ + (size_t) nr * sizeof(*ptr), __LINE__, __FILE__);\ + } +#endif + +#ifndef MALLOC +#define MALLOC(ptr, nr)\ + if(!((void *) ptr = malloc((size_t)((size_t) (nr) * sizeof(*ptr)))) && nr) {\ + printf("malloc of %d bytes failed on line %d of file %s\n",\ + (size_t) nr * sizeof(*ptr), __LINE__, __FILE__);\ + } +#endif + +#ifndef REALLOC +#define REALLOC(ptr, nr)\ + if(!((void *) ptr = realloc(ptr, (size_t)((size_t) (nr) * sizeof(*ptr)))) && nr) {\ + printf("realloc of %d bytes failed on line %d of file %s\n",\ + (size_t) nr * sizeof(*ptr), __LINE__, __FILE__);\ + } +#endif + +#ifndef FREE +#define FREE(ptr)\ + if((void *) ptr != NULL) {\ + free(ptr);\ + ptr = NULL; \ + } +#endif + +#ifndef MEMCOPY +#define MEMCOPY(nptr, optr, nr)\ + memcpy((nptr), (optr), (size_t)((size_t)(nr) * sizeof(*(optr)))) +#endif + +#ifndef MEMMOVE +#define MEMMOVE(nptr, optr, nr)\ + memmove((nptr), (optr), (size_t)((size_t)(nr) * sizeof(*(optr)))) +#endif + +#ifndef MALLOCCCPY +#define MALLOCCPY(nptr, optr, nr)\ + {MALLOC(nptr, (size_t)(nr))\ + MEMCPY(nptr, optr, (size_t)(nr))} +#endif + +#ifndef MEMCLEAR +/*#define useMMX*/ +#ifdef useMMX + #define MEMCLEAR(ptr, nr)\ + mem_set((ptr), '\0', (size_t)((size_t)(nr) * sizeof(*(ptr)))) +#else + #define MEMCLEAR(ptr, nr)\ + memset((ptr), '\0', (size_t)((size_t)(nr) * sizeof(*(ptr)))) +#endif +#endif + + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define IF(t, x, y) ((t) ? (x) : (y)) +#define SIGN(x) ((x) < 0 ? -1 : 1) + +#ifndef CMP_CALLMODEL + #ifdef WIN32 + # define CMP_CALLMODEL _cdecl + #else + # define CMP_CALLMODEL + #endif +#endif + +#define CMP_ATTRIBUTES(item) (((char *) attributes)+item*recsize) +typedef int (CMP_CALLMODEL findCompare_func)(const void *current, const void *candidate); + + +#ifdef __cplusplus + extern "C" { +#endif + +int mod( int n, int d ); + +int findIndex(int target, int *attributes, int count, int offset); +int findIndexEx(void *target, void *attributes, int count, int offset, int recsize, findCompare_func findCompare); + +int sortByREAL(int *item, REAL *weight, int size, int offset, MYBOOL unique); +int sortByINT(int *item, int *weight, int size, int offset, MYBOOL unique); +REAL sortREALByINT(REAL *item, int *weight, int size, int offset, MYBOOL unique); + +double timeNow(); + +void blockWriteBOOL(FILE *output, char *label, MYBOOL *vector, int first, int last, MYBOOL asRaw); +void blockWriteINT(FILE *output, char *label, int *vector, int first, int last); +void blockWriteREAL(FILE *output, char *label, REAL *vector, int first, int last); + +void printvec( int n, REAL *x, int modulo ); +void printmatSQ( int size, int n, REAL *X, int modulo ); +void printmatUT( int size, int n, REAL *U, int modulo ); + +#ifdef WIN32 +int fileCount( char *filemask ); +MYBOOL fileSearchPath( char *envvar, char *searchfile, char *foundpath ); +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_commonlib */ Index: src/tools/solver/lp_solve/fortify.c =================================================================== RCS file: src/tools/solver/lp_solve/fortify.c diff -N src/tools/solver/lp_solve/fortify.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/fortify.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1527 @@ +/* + * FILE: + * fortify.c + * + * DESCRIPTION: + * A fortified shell for malloc, realloc, calloc, strdup, getcwd, tempnam + * and free. + * To use Fortify, each source file will need to #include "fortify.h". To + * enable Fortify, define the symbol FORTIFY. If FORTIFY is not defined, it + * will compile away to nothing. If you do not have stderr available, you may + * wish to set an alternate output function. See _Fortify_SetOutputFunc(), + * below. + * You will also need to link in fortify.o + * + * None of the functions in this file should really be called + * directly; they really should be called through the macros + * defined in fortify.h + * + */ + +#include +#include +#include + +#if defined FORTIFY + +#if defined MSDOS || defined __BORLANDC__ || defined WIN32 || defined __HIGHC__ +# include +#endif + +extern int EndOfPgr(int); + +#define __FORTIFY_C__ /* So fortify.h knows to not define the fortify macros */ +#include "fortify.h" + +#include "ufortify.h" /* the user's options */ + +char *_Fortify_file=NULL; +int _Fortify_line=0; + +#ifndef FORTIFY_TRANSPARENT + +#include +#include +#include +#include + +#if defined MSDOS || defined __BORLANDC__ || defined WIN32 || defined __HIGHC__ +# if !defined WIN32 +# undef MSDOS +# define MSDOS +# endif +# include +#else +# include +# include +# define getch() getchar() +#endif + +#if defined _WINDOWS +# include "windows.h" +# if !defined WIN32 +# include "toolhelp.h" +# endif +#endif + +#if defined LONGNAME +# include "longname.h" +#endif + +struct Header +{ + int Checksum; /* For validating the Header structure; see ChecksumHeader() */ + char *File; /* The sourcefile of the caller */ + unsigned short Line; /* The sourceline of the caller */ + size_t Size; /* The size of the malloc'd block */ + struct Header *Prev, /* List pointers */ + *Next; + unsigned char Scope; + int dummy; /* to be sure that all bytes are considered */ +}; + +#if defined AViiON || defined __GNUC__ +# define _static static +#else +# define _static +#endif + +_static char *address __OF((void *)); +_static int TimeToCheck __OF((void)); +_static int CheckBlock __OF((struct Header *h, char *file, unsigned long line)); +_static int CheckFortification __OF((unsigned char *ptr, unsigned char value, size_t size)); +_static void SetFortification __OF((unsigned char *ptr, unsigned char value, size_t size)); +_static void OutputFortification __OF((unsigned char *ptr, unsigned char value, size_t size)); +_static int IsHeaderValid __OF((struct Header *h)); +_static void MakeHeaderValid __OF((struct Header *h)); +_static int ChecksumHeader __OF((struct Header *h)); +_static int IsOnList __OF((struct Header *h)); +_static void OutputHeader __OF((struct Header *h)); +_static void OutputMemory __OF((struct Header *h)); +_static void st_DefaultOutput __OF((char *String)); +_static void WaitIfstdOutput __OF((void)); + +static char stdOutput = 0; /* If true, did some stderr output */ +static OutputFuncPtr st_Output = st_DefaultOutput; /* Output function for errors */ + +#if !defined MSDOS && !defined WIN32 +static int strnicmp(s1,s2,maxlen) + char *s1,*s2; + size_t maxlen; + { + while ((maxlen) && (*s1) && (*s2) && (toupper(*s1)==toupper(*s2))) { + maxlen--; + s1++; + s2++; + } + return((maxlen) ? toupper(*s1)-toupper(*s2) : 0); + } + +static int stricmp(s1,s2) + char *s1,*s2; + { + return(strnicmp(s1,s2,strlen(s1)+1)); + } +#endif + +static char *address(void *addr) +{ + static char str[80]; + +#if defined KNOWS_POINTER_TYPE + sprintf(str,"%p",addr); +#else + sprintf(str,"%lx",(unsigned long) addr); +#endif + return(str); +} + +#ifdef FORTIFY_CheckInterval +int TimeToCheck() +{ + static time_t lastcheck=0L; + time_t t; + int ret = 0; + + time(&t); + if ((lastcheck==0L) || (t-lastcheck>=FORTIFY_CheckInterval)) + { + lastcheck = t; + ret = 1; + } + return(ret); +} +#endif + +static FILE *gfile=NULL; +static int Nchars=0,Nlines=0; +static char flag=0; + +static void _Fortify_NoOutput() + { + } + +static void st_DefaultOutput(char *String) +{ + static FILE *file; + static char first=1; + + if (first) { + file=stderr; + first=0; + } + + if (stdOutput==0) { + Nchars=Nlines=0; + if (gfile!=NULL) rewind(gfile); + } + + if (flag==0) + { + char *ptr; + + file=stderr; + flag = 1; + if ((ptr=getenv("FORTIFY_OUTPUT"))!=NULL) + { + if ((stricmp(ptr,"null")==0) || (stricmp(ptr,"nul")==0)) + file=NULL; + else if (stricmp(ptr,"stderr")==0) + file=stderr; + else if (stricmp(ptr,"stdout")==0) + file=stdout; +#if defined MSDOS && !defined _WINDOWS && !defined WIN32 + else if (stricmp(ptr,"stdprn")==0) + file=stdprn; +#endif + else if ((file=fopen(ptr,"w"))==NULL) + { +#if !defined _WINDOWS + fprintf(stderr,"\r\nFortify: Unable to create logfile %s\r\n",ptr); + EndOfPgr(4); +#else + { + char str[255]; + + sprintf(str,"Unable to create logfile\n \"%s\"",ptr); + MessageBox((HWND) NULL,(LPCSTR) str,(LPCSTR) "Fortify",(UINT) MB_ICONSTOP); +#if 0 +#if defined WIN32 + /* TerminateProcess(GetCurrentProcess(),65535); */ + ExitProcess(65535); +#else + TerminateApp((HTASK) NULL,(WORD) NO_UAE_BOX); +#endif +#else + EndOfPgr(1); +#endif + } +#endif + } + } + if ((file!=NULL) && (file!=stderr) && (file!=stdout)) + { + time_t t; + + time(&t); + fprintf(file,"Generated on: %s%s\n", + ctime(&t), + (file==stdout) || (file==stderr) ? "\r" : "" + ); + } + } + if (file!=NULL) + { +#if defined _WINDOWS + if ((file==stdout) || (file==stderr)) { +#if defined LINE_BY_LINE + if (MessageBox((HWND) NULL,(LPCSTR) String,(LPCSTR) "Fortify",(UINT) MB_OKCANCEL /* |MB_ICONINFORMATION */)==IDCANCEL) +#if 0 +#if defined WIN32 + /* TerminateProcess(GetCurrentProcess(),65535); */ + ExitProcess(65535); +#else + TerminateApp((HTASK) NULL,(WORD) NO_UAE_BOX); +#endif +#else + EndOfPgr(1); +#endif +#else + { + char *ptr; + + ptr="fortify.tmp"; + if ((ptr==NULL) || ((file=gfile=fopen(ptr,"w+"))==NULL)) + { + char str[255]; + + sprintf(str,"Unable to create temporary file\n \"%s\"",(ptr==NULL) ? "(NULL)" : ptr); + MessageBox((HWND) NULL,(LPCSTR) str,(LPCSTR) "Fortify",(UINT) MB_ICONSTOP); +#if 0 +#if defined WIN32 + /* TerminateProcess(GetCurrentProcess(),65535); */ + ExitProcess(65535); +#else + TerminateApp((HTASK) NULL,(WORD) NO_UAE_BOX); +#endif +#else + EndOfPgr(1); +#endif + } + } +#endif + } + if ((file!=stdout) && (file!=stderr)) +#endif + { + int i,ch=-1; + + for (i=0;(String[i]) && (Nlines<30);i++) + if (String[i]=='\n') Nlines++; + if ((String[i]) && (String[i+1])) { + ch=String[i+1]; + String[i+1]=0; + } + if ((file==stdout) || (file==stderr)) { + char *ptr=String; + int i; + + do { + for (i=0;(ptr[i]) && (ptr[i]!='\r') && (ptr[i]!='\n');i++); + Nchars+=fprintf(file,"%-*.*s%s", + i,i, + ptr, + (ptr[i]) ? "\r\n" : "" + ); + ptr+=i; + if (ptr[0]=='\r') ptr++; + if (ptr[0]=='\n') ptr++; + } while (*ptr); + } + else Nchars+=fprintf(file,String); + if (ch>=0) String[i+1]=ch; + if (Nlines>=30) { + WaitIfstdOutput(); + Nchars=Nlines=0; + stdOutput = 0; + if ((String[i]) && (String[i+1])) { + if ((file==stderr) || (file==stdout) || ((gfile!=NULL) && (Nchars))) + stdOutput = 1; + st_DefaultOutput(String+i); + } + } + } + if ((file==stderr) || (file==stdout) || ((gfile!=NULL) && (Nchars))) + stdOutput = 1; + } +} + +static void WaitIfstdOutput() +{ +#if !defined _WINDOWS + if((stdOutput) && (st_Output != (OutputFuncPtr) _Fortify_NoOutput)) + { +#ifdef FORTIFY_WAIT_FOR_KEY + static signed char wait_on_key=-1; + + if(wait_on_key<0) + { + char *ptr; + + if (((ptr=getenv("FORTIFY_WAIT_FOR_KEY"))!=NULL) && + (tolower(*ptr)=='n')) wait_on_key = 0; + else wait_on_key = 1; + + } + if(wait_on_key) + { + char c; + +#if !defined MSDOS && !defined WIN32 + struct termio tio,tiobak; + char flag; + + if ((flag=ioctl(0,TCGETA,&tio))==0) /* handle 0 is stdin */ + { + tiobak=tio; + tio.c_lflag&=~ICANON; + tio.c_lflag&=~ECHO; + tio.c_cc[VMIN]=1; + ioctl(0,TCSETA,&tio); + } +#endif /* !MSDOS */ + c = getch(); + +#if !defined MSDOS && !defined WIN32 + if (flag==0) + ioctl(0,TCSETA,&tiobak); +#endif /* !MSDOS */ + + if ((c == 3) || (c == 0x1b)) EndOfPgr(3); + } +#endif /* FORTIFY_WAIT_FOR_KEY */ + + } +#else +# if !defined LINE_BY_LINE + if ((stdOutput) && (gfile!=NULL) && (Nchars)) + { + char *ptr; + + ptr=(char *) malloc(Nchars+1); + if (ptr!=NULL) + { + int n=0,l=0; + + rewind(gfile); + while ((n 0) + { + if(rand() % 100 < st_MallocFailRate) + { +#ifdef WARN_ON_FALSE_FAIL + sprintf(st_Buffer, + "\nFortify: %s.%ld\n malloc(%ld) \"false\" failed\n", + file, line, (unsigned long)size); + st_Output(st_Buffer); +#endif + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(0); + } + } + + /* + * malloc the memory, including the space for the header and fortification + * buffers + */ +#ifdef WARN_ON_SIZE_T_OVERFLOW + { + size_t private_size = sizeof(struct Header) + + FORTIFY_BEFORE_SIZE + size + FORTIFY_AFTER_SIZE; + + if(private_size < size) /* Check to see if the added baggage is larger than size_t */ + { + sprintf(st_Buffer, + "\nFortify: %s.%ld\n malloc(%ld) has overflowed size_t.\n", + file, line, (unsigned long)size); + st_Output(st_Buffer); + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(0); + } + } +#endif + + ptr = (unsigned char *) malloc(sizeof(struct Header) + + FORTIFY_BEFORE_SIZE + size + FORTIFY_AFTER_SIZE); + if(!ptr) + { +#ifdef WARN_ON_MALLOC_FAIL + sprintf(st_Buffer, "\nFortify: %s.%ld\n malloc(%ld) failed\n", + file, line, (unsigned long)size); + st_Output(st_Buffer); +#endif + + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(0); + } + + /* + * Initialize and validate the header + */ + h = (struct Header *)ptr; + + h->Size = size; + + h->File = file; + h->Line = (unsigned short) line; + + h->Next = st_Head; + h->Prev = 0; + + h->Scope = st_Scope; + + if(st_Head) + { + st_Head->Prev = h; + MakeHeaderValid(st_Head); + } + + st_Head = h; + + MakeHeaderValid(h); + + + /* + * Initialize the fortifications + */ + SetFortification(ptr + sizeof(struct Header), + FORTIFY_BEFORE_VALUE, FORTIFY_BEFORE_SIZE); + SetFortification(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE + size, + FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE); + +#ifdef FILL_ON_MALLOC + /* + * Fill the actual user memory + */ + SetFortification(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE, + FILL_ON_MALLOC_VALUE, size); +#endif + + /* + * We return the address of the user's memory, not the start of the block, + * which points to our magic cookies + */ + + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return((void *) (ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE)); +} + +/* + * _Fortify_free() - This free must be used for all memory allocated with + * _Fortify_malloc(). + * + * Features: + * + Pointers are validated before attempting a free - the pointer + * must point to a valid malloc'd bit of memory. + * + Detects attempts at freeing the same block of memory twice + * + Can clear out memory as it is free'd, to prevent code from using + * the memory after it's been freed. + * + Checks the sentinals of the memory being freed. + * + Can check the sentinals of all memory. + */ + +void FORTIFY_STORAGE +_Fortify_free(void * uptr,char * file,unsigned long line) +{ + unsigned char *ptr = (unsigned char *)uptr - sizeof(struct Header) - FORTIFY_BEFORE_SIZE; + struct Header *h = (struct Header *)ptr; + + stdOutput = 0; + + FORTIFY_LOCK(); + + if(st_Disabled) + { + free(uptr); + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return; + } + +#ifdef CHECK_ALL_MEMORY_ON_FREE +#ifdef FORTIFY_CheckInterval + if (TimeToCheck()) +#endif + _Fortify_CheckAllMemory(file, line); +#endif + +#ifdef PARANOID_FREE + if(!IsOnList(h)) + { + sprintf(st_Buffer, + "\nFortify: %s.%ld\n Invalid pointer, corrupted header, or possible free twice\n", + file, line); + st_Output(st_Buffer); + OutputLastVerifiedPoint(); + goto fail; + } +#endif + + if(!CheckBlock(h, file, line)) + goto fail; + + /* + * Remove the block from the list + */ + if(h->Prev) + { + if(!CheckBlock(h->Prev, file, line)) + goto fail; + + h->Prev->Next = h->Next; + MakeHeaderValid(h->Prev); + } + else + st_Head = h->Next; + + if(h->Next) + { + if(!CheckBlock(h->Next, file, line)) + goto fail; + + h->Next->Prev = h->Prev; + MakeHeaderValid(h->Next); + } + +#ifdef FILL_ON_FREE + /* + * Nuke out all memory that is about to be freed + */ + SetFortification(ptr, FILL_ON_FREE_VALUE, + sizeof(struct Header) + FORTIFY_BEFORE_SIZE + h->Size + FORTIFY_AFTER_SIZE); +#endif + + /* + * And do the actual free + */ + free(ptr); + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return; + +fail: + sprintf(st_Buffer, " free(%s) failed\n", address(uptr)); + st_Output(st_Buffer); + FORTIFY_UNLOCK(); + WaitIfstdOutput(); +} + +/* + * _Fortify_realloc() - Uses _Fortify_malloc() and _Fortify_free() to implement + * realloc(). + * + * Features: + * + The realloc'd block is ALWAYS moved. + * + The pointer passed to realloc() is verified in the same way that + * _Fortify_free() verifies pointers before it frees them. + * + All the _Fortify_malloc() and _Fortify_free() protection + */ +void *FORTIFY_STORAGE +_Fortify_realloc(void *ptr,size_t new_size,char *file,unsigned long line) +{ + void *new_ptr; + struct Header *h = (struct Header *) + ((unsigned char *)ptr - sizeof(struct Header) - FORTIFY_BEFORE_SIZE); + + stdOutput = 0; + + if(st_Disabled) + { + FORTIFY_LOCK(); + new_ptr = (void *) realloc(ptr, new_size); + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(new_ptr); + } + + if(!ptr) + { + void *FORTIFY_STORAGE ret = _Fortify_malloc(new_size, file, line); + + WaitIfstdOutput(); + return(ret); + } + + FORTIFY_LOCK(); + + if(!IsOnList(h)) + { + sprintf(st_Buffer, + "\nFortify: %s.%ld\n Invalid pointer or corrupted header passed to realloc\n", + file, line); + st_Output(st_Buffer); + goto fail; + } + + if(!CheckBlock(h, file, line)) + goto fail; + + new_ptr = _Fortify_malloc(new_size, file, line); + if(!new_ptr) + { + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(0); + } + + if(h->Size < new_size) + memcpy(new_ptr, ptr, h->Size); + else + memcpy(new_ptr, ptr, new_size); + + _Fortify_free(ptr, file, line); + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(new_ptr); + +fail: + sprintf(st_Buffer, " realloc(%s, %ld) failed\n", address(ptr), (unsigned long)new_size); + st_Output(st_Buffer); + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(NULL); +} + + +/* + * _Fortify_CheckPointer() - Returns true if the uptr points to a valid + * piece of _Fortify_malloc()'d memory. The memory must be on the malloc'd + * list, and it's sentinals must be in tact. + * If anything is wrong, an error message is issued. + * + * (Note - if fortify is disabled, this function always returns true). + */ +int FORTIFY_STORAGE +_Fortify_CheckPointer(void *uptr,char *file,unsigned long line) +{ + unsigned char *ptr = (unsigned char *)uptr - sizeof(struct Header) - FORTIFY_BEFORE_SIZE; + int r; + + stdOutput = 0; + + if(st_Disabled) + { + WaitIfstdOutput(); + return(1); + } + + FORTIFY_LOCK(); + + if(!IsOnList((struct Header *)ptr)) + { + sprintf(st_Buffer, + "\nFortify: %s.%ld\n Invalid pointer or corrupted header detected (%s)\n", + file, line, address(uptr)); + st_Output(st_Buffer); + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(0); + } + + r = CheckBlock((struct Header *)ptr, file, line); + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(r); +} + +/* + * Fortify_SetOutputFunc(OutputFuncPtr Output) - Sets the function used to + * output all error and diagnostic messages by fortify. The output function + * takes a single unsigned char * argument, and must be able to handle newlines. + * The function returns the old pointer. + */ +Fortify_OutputFuncPtr FORTIFY_STORAGE +_Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output) +{ + OutputFuncPtr Old = st_Output; + + st_Output = (OutputFuncPtr) Output; + + return((Fortify_OutputFuncPtr FORTIFY_STORAGE) Old); +} + +/* + * _Fortify_SetMallocFailRate(int Percent) - _Fortify_malloc() will make the + * malloc attempt fail this Percent of the time, even if the memory is + * available. Useful to "stress-test" an application. Returns the old + * value. The fail rate defaults to 0. + */ +int FORTIFY_STORAGE +_Fortify_SetMallocFailRate(int Percent) +{ + int Old = st_MallocFailRate; + + st_MallocFailRate = Percent; + + return(Old); +} + + +/* + * _Fortify_CheckAllMemory() - Checks the sentinals of all malloc'd memory. + * Returns the number of blocks that failed. + * + * (If Fortify is disabled, this function always returns 0). + */ +int FORTIFY_STORAGE +_Fortify_CheckAllMemory(char *file,unsigned long line) +{ + struct Header *curr = st_Head; + int count = 0; + + stdOutput = 0; + + if(st_Disabled) + { + WaitIfstdOutput(); + return(0); + } + + FORTIFY_LOCK(); + + while(curr) + { + if(!CheckBlock(curr, file, line)) + count++; + + curr = curr->Next; + } + + if(file) + { + st_LastVerifiedFile = file; + st_LastVerifiedLine = (short) line; + } + + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(count); +} + +/* _Fortify_EnterScope - enters a new Fortify scope level. + * returns the new scope level. + */ +int FORTIFY_STORAGE +_Fortify_EnterScope(char *file,unsigned long line) +{ + return((int) ++st_Scope); +} + +/* _Fortify_LeaveScope - leaves a Fortify scope level, + * also prints a memory dump of all non-freed memory that was allocated + * during the scope being exited. + */ +int FORTIFY_STORAGE +_Fortify_LeaveScope(char *file,unsigned long line) +{ + struct Header *curr = st_Head; + int count = 0; + size_t size = 0; + + stdOutput = 0; + + if(st_Disabled) + { + WaitIfstdOutput(); + return(0); + } + + FORTIFY_LOCK(); + + st_Scope--; + while(curr) + { + if(curr->Scope > st_Scope) + { + if(count == 0) + { + sprintf(st_Buffer, "\nFortify: Memory Dump at %s.%ld\n", file, line); + st_Output(st_Buffer); + OutputLastVerifiedPoint(); + sprintf(st_Buffer, "%11s %8s %s\n", "Address", "Size", "Allocator"); + st_Output(st_Buffer); + } + + OutputHeader(curr); + count++; + size += curr->Size; + } + + curr = curr->Next; + } + + if(count) + { + sprintf(st_Buffer, "%11s %8ld bytes overhead\n", "and", + (unsigned long)(count * (sizeof(struct Header) + FORTIFY_BEFORE_SIZE + FORTIFY_AFTER_SIZE))); + st_Output(st_Buffer); + + sprintf(st_Buffer,"%11s %8ld bytes in %d blocks\n", "total", size, count); + st_Output(st_Buffer); + } + + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(count); +} + +/* + * _Fortify_OutputAllMemory() - Outputs the entire list of currently + * malloc'd memory. For each malloc'd block is output it's Address, + * Size, and the SourceFile and Line that allocated it. + * + * If there is no memory on the list, this function outputs nothing. + * + * It returns the number of blocks on the list, unless fortify has been + * disabled, in which case it always returns 0. + */ +int FORTIFY_STORAGE +_Fortify_OutputAllMemory(char *file,unsigned long line) +{ + struct Header *curr = st_Head; + int count = 0; + size_t size = 0; + + stdOutput = 0; + + if(st_Disabled) + { + WaitIfstdOutput(); + return(0); + } + + FORTIFY_LOCK(); + + if(curr) + { + sprintf(st_Buffer, "\nFortify: Memory Dump at %s.%ld\n", file, line); + st_Output(st_Buffer); + OutputLastVerifiedPoint(); + sprintf(st_Buffer, "%11s %8s %s\n", "Address", "Size", "Allocator"); + st_Output(st_Buffer); + + while(curr) + { + OutputHeader(curr); + count++; + size += curr->Size; + curr = curr->Next; + } + + sprintf(st_Buffer, "%11s %8ld bytes overhead\n", "and", + (unsigned long)(count * (sizeof(struct Header) + FORTIFY_BEFORE_SIZE + FORTIFY_AFTER_SIZE))); + st_Output(st_Buffer); + + sprintf(st_Buffer,"%11s %8ld bytes in %d blocks\n", "total", size, count); + st_Output(st_Buffer); + } + + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(count); +} + +/* _Fortify_DumpAllMemory(Scope) - Outputs the entire list of currently + * new'd memory within the specified scope. For each new'd block is output + * it's Address, Size, the SourceFile and Line that allocated it, a hex dump + * of the contents of the memory and an ascii dump of printable characters. + * + * If there is no memory on the list, this function outputs nothing. + * + * It returns the number of blocks on the list, unless Fortify has been + * disabled, in which case it always returns 0. + */ +int FORTIFY_STORAGE +_Fortify_DumpAllMemory(int scope,char *file,unsigned long line) +{ + struct Header *curr = st_Head; + int count = 0; + size_t size = 0; + + stdOutput = 0; + + if(st_Disabled) + { + WaitIfstdOutput(); + return(0); + } + + FORTIFY_LOCK(); + + while(curr) + { + if(curr->Scope >= scope) + { + if(count == 0) + { + sprintf(st_Buffer, "\nFortify: Memory Dump at %s.%ld\n", file, line); + st_Output(st_Buffer); + OutputLastVerifiedPoint(); + sprintf(st_Buffer, "%11s %8s %s\n", "Address", "Size", "Allocator"); + st_Output(st_Buffer); + } + + OutputHeader(curr); + OutputMemory(curr); + st_Output("\n"); + count++; + size += curr->Size; + } + + curr = curr->Next; + } + + if(count) + { + sprintf(st_Buffer, "%11s %8ld bytes overhead\n", "and", + (unsigned long)(count * (sizeof(struct Header) + FORTIFY_BEFORE_SIZE + FORTIFY_AFTER_SIZE))); + st_Output(st_Buffer); + + sprintf(st_Buffer,"%11s %8ld bytes in %d blocks\n", "total", size, count); + st_Output(st_Buffer); + } + + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + return(count); +} + +/* + * _Fortify_Disable() - This function provides a mechanism to disable Fortify + * without recompiling all the sourcecode. + * If 'how' is zero then it can only be called when there is no memory on the + * Fortify malloc'd list. (Ideally, at the start of the program before any + * memory has been allocated). If you call this function when there IS + * memory on the Fortify malloc'd list, it will issue an error, and fortify + * will not be disabled. + * If 'how' is nonzero then output will only be disabled. This can always be + * done. + */ + +int FORTIFY_STORAGE +_Fortify_Disable(char *file,unsigned long line,int how) +{ + int result; + + if (how == 0) + { + stdOutput = 0; + + FORTIFY_LOCK(); + + if(st_Head) + { + sprintf(st_Buffer, "Fortify: %s.%d\n", file, line); + st_Output(st_Buffer); + st_Output(" Fortify_Disable failed\n"); + st_Output(" (because there is memory on the Fortify memory list)\n"); + + _Fortify_OutputAllMemory(file, line); + result = 0; + } + else + { + st_Disabled = 1; + result = 1; + } + + FORTIFY_UNLOCK(); + WaitIfstdOutput(); + } + else + { + _Fortify_SetOutputFunc((Fortify_OutputFuncPtr) _Fortify_NoOutput); + result = 1; + } + return(result); +} + +/* + * Check a block's header and fortifications. + */ +static int CheckBlock(struct Header *h,char *file,unsigned long line) +{ + unsigned char *ptr = (unsigned char *)h; + int result = 1; + + stdOutput = 0; + + if(!IsHeaderValid(h)) + { + sprintf(st_Buffer, + "\nFortify: %s.%ld\n Invalid pointer or corrupted header detected (%s)\n", + file, line, address(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE)); + st_Output(st_Buffer); + OutputLastVerifiedPoint(); + WaitIfstdOutput(); + return(0); + } + + if(!CheckFortification(ptr + sizeof(struct Header), + FORTIFY_BEFORE_VALUE, FORTIFY_BEFORE_SIZE)) + { + sprintf(st_Buffer, + "\nFortify: %s.%ld\n Memory overrun detected before block\n", + file, line); + st_Output(st_Buffer); + + sprintf(st_Buffer," (%s,%ld,%s.%u)\n", + address(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE), + (unsigned long)h->Size, h->File, h->Line); + st_Output(st_Buffer); + + OutputFortification(ptr + sizeof(struct Header), + FORTIFY_BEFORE_VALUE, FORTIFY_BEFORE_SIZE); + OutputLastVerifiedPoint(); + result = 0; + } + + if(!CheckFortification(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE + h->Size, + FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE)) + { + sprintf(st_Buffer, "\nFortify: %s.%ld\n Memory overrun detected after block\n", + file, line); + st_Output(st_Buffer); + + sprintf(st_Buffer," (%s,%ld,%s.%u)\n", + address(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE), + (unsigned long)h->Size, h->File, h->Line); + st_Output(st_Buffer); + + OutputFortification(ptr + sizeof(struct Header) + FORTIFY_BEFORE_SIZE + h->Size, + FORTIFY_AFTER_VALUE, FORTIFY_AFTER_SIZE); + OutputLastVerifiedPoint(); + result = 0; + } + + WaitIfstdOutput(); + return(result); +} + +/* + * Checks if the _size_ bytes from _ptr_ are all set to _value_ + */ +static int CheckFortification(unsigned char *ptr,unsigned char value,size_t size) +{ + while(size--) + if(*ptr++ != value) + return(0); + + return(1); +} + +/* + * Set the _size_ bytes from _ptr_ to _value_. + */ +static void SetFortification(unsigned char *ptr,unsigned char value,size_t size) +{ + memset(ptr, value, size); +} + +/* + * Output the corrupted section of the fortification + */ +/* Output the corrupted section of the fortification */ +static void +OutputFortification(unsigned char *ptr,unsigned char value,size_t size) +{ + unsigned long offset, column; + char ascii[17]; + + st_Output("Address Offset Data"); + + offset = 0; + column = 0; + + while(offset < size) + { + if(column == 0) + { + sprintf(st_Buffer, "\n%8s %8d ", address(ptr), offset); + st_Output(st_Buffer); + } + + sprintf(st_Buffer, "%02x ", *ptr); + st_Output(st_Buffer); + + ascii[ (int) column ] = isprint( *ptr ) ? (char)(*ptr) : (char)(' '); + ascii[ (int) (column + 1) ] = '\0'; + + ptr++; + offset++; + column++; + + if(column == 16) + { + st_Output( " \"" ); + st_Output( ascii ); + st_Output( "\"" ); + column = 0; + } + } + + if ( column != 0 ) + { + while ( column ++ < 16 ) + { + st_Output( " " ); + } + st_Output( " \"" ); + st_Output( ascii ); + st_Output( "\"" ); + } + + st_Output("\n"); +} + +/* + * Returns true if the supplied pointer does indeed point to a real Header + */ +static int IsHeaderValid(struct Header *h) +{ + return(!ChecksumHeader(h)); +} + +/* + * Updates the checksum to make the header valid + */ +static void MakeHeaderValid(struct Header *h) +{ + h->dummy = 0; + h->Checksum = 0; + h->Checksum = -ChecksumHeader(h); +} + +/* + * Calculate (and return) the checksum of the header. (Including the Checksum + * variable itself. If all is well, the checksum returned by this function should + * be 0. + */ +static int ChecksumHeader(struct Header *h) +{ + register int c, checksum, *p; + + for(c = 0, checksum = 0, p = (int *)h; c < sizeof(struct Header)/sizeof(int); c++) + checksum += *p++; + + return(checksum); +} + +/* + * Examines the malloc'd list to see if the given header is on it. + */ +static int IsOnList(struct Header *h) +{ + struct Header *curr; + + curr = st_Head; + while(curr) + { + if(curr == h) + return(1); + + curr = curr->Next; + } + + return(0); +} + +/* + * Hex and ascii dump the memory + */ +static void +OutputMemory(struct Header *h) +{ + OutputFortification((unsigned char*)h + sizeof(struct Header) + FORTIFY_BEFORE_SIZE, + 0, h->Size); +} + + +/* + * Output the header... + */ +static void OutputHeader(struct Header *h) +{ + sprintf(st_Buffer, "%11s %8ld %s.%u (%d)\n", + address((unsigned char*)h + sizeof(struct Header) + FORTIFY_BEFORE_SIZE), + (unsigned long)h->Size, + h->File, h->Line, (int) h->Scope); + st_Output(st_Buffer); +} + +static void OutputLastVerifiedPoint() +{ + sprintf(st_Buffer, "\nLast Verified point: %s.%u\n", + st_LastVerifiedFile, + st_LastVerifiedLine); + st_Output(st_Buffer); +} + +#else /* FORTIFY_TRANSPARENT */ + +void *FORTIFY_STORAGE +_Fortify_malloc(size,file,line) + size_t size; + char *file; + unsigned long line; +{ + return(malloc(size)); +} + +void FORTIFY_STORAGE +_Fortify_free(uptr,file,line) + void *uptr; + char *file; + unsigned long line; +{ + free(uptr); +} + +void *FORTIFY_STORAGE +_Fortify_realloc(ptr,new_size,file,line) + void *ptr; + size_t new_size; + char *file; + unsigned long line; +{ + return(realloc(ptr, new_size)); +} + +int FORTIFY_STORAGE +_Fortify_CheckPointer(uptr,file,line) + void *uptr; + char *file; + unsigned long line; +{ + return(1); +} + +Fortify_OutputFuncPtr FORTIFY_STORAGE +_Fortify_SetOutputFunc(Output) + Fortify_OutputFuncPtr Output; +{ + return(0); +} + +int FORTIFY_STORAGE +_Fortify_SetMallocFailRate(Percent) + int Percent; +{ + return(0); +} + +int FORTIFY_STORAGE +_Fortify_CheckAllMemory(file,line) + char *file; + unsigned long line; +{ + return(0); +} + +int FORTIFY_STORAGE +_Fortify_EnterScope(file,line) + char *file; + unsigned long line; +{ + return(0); +} + +int FORTIFY_STORAGE +_Fortify_LeaveScope(file,line) + char *file; + unsigned long line; +{ + return(0); +} + +int FORTIFY_STORAGE +_Fortify_OutputAllMemory(file,line) + char *file; + unsigned long line; +{ + return(0); +} + +int FORTIFY_STORAGE +_Fortify_DumpAllMemory(scope,file,line) + int scope; + char *file; + unsigned long line; +{ + return(0); +} + +int FORTIFY_STORAGE +_Fortify_Disable(file,line) + char *file; + unsigned long line; +{ + return(1); +} + +#endif /* !FORTIFY_TRANSPARENT */ + +/* function that use _Fortify_malloc(), _Fortify_realloc(), _Fortify_free() */ + +/* + * Fortifty_calloc() - Uses _Fortify_malloc() to implement calloc(). Much + * the same protection as _Fortify_malloc(). + */ +void *FORTIFY_STORAGE +_Fortify_calloc(size_t nitems,size_t size,char *file,unsigned long line) +{ + void *ptr; + + ptr = _Fortify_malloc(nitems * size, file, line); + + if(ptr) + memset(ptr, 0, nitems * size); + + return(ptr); +} + +/* + * Fortifty_strdup() - Uses _Fortify_malloc() to implement strdup(). Much + * the same protection as _Fortify_malloc(). + * The library function is not used because it is not certain that getpwd + * uses the library malloc function (if linked with an alternate library) + * and if the memory is freed then strange things can happen + */ +char *FORTIFY_STORAGE +_Fortify_strdup(char *str,char *file,unsigned long line) +{ + char *ptr; + + ptr = (char *) _Fortify_malloc(strlen(str) + 1, file, line); + + if(ptr) + strcpy(ptr, str); + + return(ptr); +} + +/* + * Fortifty_getpwd() - Uses _Fortify_malloc() to implement getpwd(). Much + * the same protection as _Fortify_malloc(). + * Memory is not allocated bu getcwd but by our routine for the same reason + * as for strdup + */ +char *FORTIFY_STORAGE +_Fortify_getcwd(char *buf,int size,char *file,unsigned long line) +{ + char *ptr; + + if(buf!=NULL) + ptr = buf; + else + ptr = (char *) _Fortify_malloc(size + 1, file, line); + + if(ptr) + ptr = getcwd(ptr, size); + + return(ptr); +} + +/* + * Fortifty_tempnam() - Uses _Fortify_strdup() to implement tempnam(). Much + * the same protection as _Fortify_malloc(). + */ +char *FORTIFY_STORAGE +_Fortify_tempnam(char *dir,char *pfx,char *file,unsigned long line) +{ + char *ptr1, *ptr2; + + ptr1 = tempnam(dir,pfx); + + if(ptr1) + { + ptr2=_Fortify_strdup(ptr1,file,line); + free(ptr1); + ptr1=ptr2; + } + + return(ptr1); +} + +#endif /* FORTIFY */ Index: src/tools/solver/lp_solve/lp-solve-debug.c =================================================================== RCS file: src/tools/solver/lp_solve/lp-solve-debug.c diff -N src/tools/solver/lp_solve/lp-solve-debug.c --- src/tools/solver/lp_solve/lp-solve-debug.c 5 Dec 2002 18:26:32 -0000 1.7 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,112 +0,0 @@ -#include -#include "lpkit.h" -#include "lpglob.h" -#include - -void -lp_solve_debug_print_solution (lprec *lp); - -void -lp_solve_debug_print_bounds (lprec *lp, gnm_float *upbo, gnm_float *lowbo); - -void -lp_solve_debug_print (lprec *lp, const char *format, ...) -#ifdef __GNUC__ -__attribute__ ((format (printf, 2, 3))) -#endif -; - - -static void -print_indent (void) -{ - int i; - - fprintf (stderr, "%2d", lp_solve_Level); - if (lp_solve_Level < 50) /* useless otherwise */ - for (i = lp_solve_Level; i > 0; i--) - fprintf (stderr, "--"); - else - fprintf (stderr, " *** too deep ***"); - fprintf (stderr, "> "); -} - - -void -lp_solve_debug_print_solution (lprec *lp) -{ - int i; - - if (lp->debug) - for (i = lp->rows + 1; i <= lp->sum; i++) { - print_indent (); - if (lp->names_used) - fprintf (stderr, "%s %g\n", - lp->col_name[i - lp->rows], - (double)lp->solution[i]); - else - fprintf (stderr, "Var [%d] %g\n", i - lp->rows, - (double)lp->solution[i]); - } -} - - -void -lp_solve_debug_print_bounds (lprec *lp, gnm_float *upbo, gnm_float *lowbo) -{ - int i; - - if (lp->debug) - for (i = lp->rows + 1; i <= lp->sum; i++) { - if (lowbo[i] == upbo[i]) { - print_indent (); - if (lp->names_used) - fprintf (stderr, "%s = %g\n", - lp->col_name[i - lp->rows], - (double) lowbo[i]); - else - fprintf (stderr, "Var [%d] = %g\n", - i - lp->rows, - (double) lowbo[i]); - } else { - if (lowbo[i] != 0) { - print_indent (); - if (lp->names_used) - fprintf (stderr, "%s > %g\n", - lp->col_name[i - lp->rows], - (double)lowbo[i]); - else - fprintf (stderr, - "Var [%d] > %g\n", - i - lp->rows, - (double) lowbo[i]); - } - if (upbo[i] != lp->infinite) { - print_indent (); - if (lp->names_used) - fprintf (stderr, "%s < %g\n", - lp->col_name[i - lp->rows], - (double) upbo[i]); - else - fprintf (stderr, - "Var [%d] < %g\n", - i - lp->rows, - (double) upbo[i]); - } - } - } -} - -void -lp_solve_debug_print (lprec *lp, const char *format, ...) -{ - va_list ap; - - if (lp->debug) { - va_start (ap, format); - print_indent (); - vfprintf (stderr, format, ap); - fputc ('\n', stderr); - va_end (ap); - } -} Index: src/tools/solver/lp_solve/lp-solve-debug.h =================================================================== RCS file: src/tools/solver/lp_solve/lp-solve-debug.h diff -N src/tools/solver/lp_solve/lp-solve-debug.h --- src/tools/solver/lp_solve/lp-solve-debug.h 5 Dec 2002 18:26:32 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,6 +0,0 @@ -/* prototypes for debug printing by other files */ - -void lp_solve_debug_print (lprec *lp, const char *format, ...); -void lp_solve_debug_print_solution (lprec *lp); -void lp_solve_debug_print_bounds (lprec *lp, gnm_float *upbo, - gnm_float *lowbo); Index: src/tools/solver/lp_solve/lp_BFP.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_BFP.h diff -N src/tools/solver/lp_solve/lp_BFP.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_BFP.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,73 @@ + +/* ---------------------------------------------------------------------------------- */ +/* lp_solve v5+ headers for basis inversion / factorization libraries */ +/* ---------------------------------------------------------------------------------- */ +#define BFP_STATUS_RANKLOSS -1 +#define BFP_STATUS_SUCCESS 0 +#define BFP_STATUS_SINGULAR 1 +#define BFP_STATUS_UNSTABLE 2 +#define BFP_STATUS_NOPIVOT 3 +#define BFP_STATUS_DIMERROR 4 +#define BFP_STATUS_DUPLICATE 5 +#define BFP_STATUS_NOMEMORY 6 +#define BFP_STATUS_ERROR 7 /* Unspecified, command-related error */ +#define BFP_STATUS_FATAL 8 + +#ifndef BFP_CALLMODEL + #ifdef WIN32 + #define BFP_CALLMODEL __stdcall /* "Standard" call model */ + #else + #define BFP_CALLMODEL + #endif +#endif + +#ifdef RoleIsExternalInvEngine + #define __BFP_EXPORT_TYPE __EXPORT_TYPE +#else + #define __BFP_EXPORT_TYPE +#endif + + +/* Routines with UNIQUE implementations for each inversion engine */ +/* ---------------------------------------------------------------------------------- */ +char __BFP_EXPORT_TYPE *(BFP_CALLMODEL bfp_name)(void); +void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_free)(lprec *lp); +void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_resize)(lprec *lp, int newsize); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_nonzeros)(lprec *lp, MYBOOL maximum); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_memallocated)(lprec *lp); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_preparefactorization)(lprec *lp); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_factorize)(lprec *lp, int uservars, int Bsize, MYBOOL *usedpos); +MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_finishupdate)(lprec *lp, MYBOOL changesign); +void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_ftran_normal)(lprec *lp, REAL *pcol, int *nzidx); +void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_ftran_prepare)(lprec *lp, REAL *pcol, int *nzidx); +void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_btran_normal)(lprec *lp, REAL *prow, int *nzidx); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_status)(lprec *lp); + + +/* Routines SHARED for all inverse implementations; located in lp_BFP1.c */ +/* ---------------------------------------------------------------------------------- */ +MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_compatible)(lprec *lp, int bfpversion, int lpversion); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_indexbase)(lprec *lp); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_rowoffset)(lprec *lp); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_pivotmax)(lprec *lp); +REAL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_efficiency)(lprec *lp); +REAL __BFP_EXPORT_TYPE *(BFP_CALLMODEL bfp_pivotvector)(lprec *lp); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_pivotcount)(lprec *lp); +MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_mustrefactorize)(lprec *lp); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_refactcount)(lprec *lp); +MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_isSetI)(lprec *lp); +int *bfp_createMDO(lprec *lp, MYBOOL *usedpos, int count, MYBOOL doMDO); + +/* Routines with OPTIONAL SHARED code; template routines suitable for canned */ +/* inverse engines are located in lp_BFP2.c */ +/* ---------------------------------------------------------------------------------- */ +MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_init)(lprec *lp, int size, int deltasize); +MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_restart)(lprec *lp); +void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_pivotalloc)(lprec *lp, int newsize); +int __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_colcount)(lprec *lp); +MYBOOL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_canresetbasis)(lprec *lp); +void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_finishfactorization)(lprec *lp); +LREAL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_prepareupdate)(lprec *lp, int row_nr, int col_nr, REAL *pcol); +REAL __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_pivotRHS)(lprec *lp, LREAL theta, REAL *pcol); +void __BFP_EXPORT_TYPE (BFP_CALLMODEL bfp_btran_double)(lprec *lp, REAL *prow, int *pnzidx, REAL *drow, int *dnzidx); + Index: src/tools/solver/lp_solve/lp_BFP1.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_BFP1.c diff -N src/tools/solver/lp_solve/lp_BFP1.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_BFP1.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,150 @@ + +/* Routines located in lp_BFP1.cpp; common for all factorization engines */ +/* Cfr. lp_BFP.h for definitions */ +/* ---------------------------------------------------------------------------------- */ +/* Changes: */ +/* 29 May 2004 Corrected calculation of bfp_efficiency(), which required */ +/* modifying the max_Bsize to include slack variables. KE. */ +/* 16 June 2004 Make the symbolic minimum degree ordering routine available */ +/* to BFPs as a routine internal to the library. KE */ +/* 1 July 2004 Change due to change in MDO naming. */ +/* ---------------------------------------------------------------------------------- */ + +#include "lp_MDO.h" + + +/* MUST MODIFY */ +MYBOOL BFP_CALLMODEL bfp_compatible(lprec *lp, int bfpversion, int lpversion) +{ + MYBOOL status = FALSE; + + if((lp != NULL) && (bfpversion == BFPVERSION)) { +#if 0 + if(lpversion == MAJORVERSION) /* Forces BFP renewal at lp_solve major version changes */ +#endif + status = TRUE; + } + return( status ); +} + +/* DON'T MODIFY */ +int BFP_CALLMODEL bfp_status(lprec *lp) +{ + return(lp->invB->status); +} + +/* DON'T MODIFY */ +int BFP_CALLMODEL bfp_indexbase(lprec *lp) +{ + return( MATINDEXBASE ); +} + +/* DON'T MODIFY */ +int BFP_CALLMODEL bfp_rowoffset(lprec *lp) +{ + return( INVDELTAROWS ); +} + +/* DON'T MODIFY */ +int BFP_CALLMODEL bfp_pivotmax(lprec *lp) +{ + if(lp->max_pivots > 0) + return( lp->max_pivots ); + else + return( DEF_MAXPIVOT ); +} + +/* DON'T MODIFY */ +REAL * BFP_CALLMODEL bfp_pivotvector(lprec *lp) +{ + return( lp->invB->pcol ); +} + +/* DON'T MODIFY */ +REAL BFP_CALLMODEL bfp_efficiency(lprec *lp) +{ + REAL hold; + + hold = bfp_nonzeros(lp, AUTOMATIC); + if(hold == 0) + hold = 1 + lp->rows; + hold = bfp_nonzeros(lp, TRUE)/hold; + + return(hold); +} + +/* DON'T MODIFY */ +int BFP_CALLMODEL bfp_pivotcount(lprec *lp) +{ + return(lp->invB->num_pivots); +} + + +/* DON'T MODIFY */ +int BFP_CALLMODEL bfp_refactcount(lprec *lp) +{ + return(lp->invB->num_refact); +} + +/* DON'T MODIFY */ +MYBOOL BFP_CALLMODEL bfp_mustrefactorize(lprec *lp) +{ + if(!lp->doInvert) { + REAL f; + INVrec *lu = lp->invB; + + if(bfp_pivotcount(lp) > 0) + f = (timeNow()-lu->time_refactstart) / (REAL) bfp_pivotcount(lp); + else + f = 0; + + if(lu->force_refact || + (bfp_pivotcount(lp) >= bfp_pivotmax(lp)) || + ((bfp_pivotcount(lp) > 1) && (f >= MIN_TIMEPIVOT) && + (f >= lu->time_refactnext))) + lp->doInvert = TRUE; + else + lu->time_refactnext = f; + } + + return(lp->doInvert); +} + +/* DON'T MODIFY */ +MYBOOL BFP_CALLMODEL bfp_isSetI(lprec *lp) +{ + return( (MYBOOL) lp->invB->set_Bidentity ); +} + +/* DON'T MODIFY */ +int *bfp_createMDO(lprec *lp, MYBOOL *usedpos, int count, MYBOOL doMDO) +{ + int *mdo, i, j, kk; + + mdo = (int *) malloc((count + 1)*sizeof(*mdo)); +/* allocINT(lp, &mdo, count + 1, FALSE); */ + + /* Fill the mdo[] array with remaining full-pivot basic user variables */ + kk = 0; + for(j = 1; j <= lp->columns; j++) { + i = lp->rows + j; + if(usedpos[i] == TRUE) { + kk++; + mdo[kk] = i; + } + } + mdo[0] = kk; + if(kk == 0) + goto Process; + + /* Calculate the approximate minimum degree column ordering */ + if(doMDO) { + i = getMDO(lp, usedpos, mdo, NULL, FALSE); + if(i != 0) { + lp->report(lp, CRITICAL, "bfp_createMDO: Internal error %d in minimum degree ordering routine", i); + FREE(mdo); + } + } +Process: + return( mdo ); +} Index: src/tools/solver/lp_solve/lp_Hash.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_Hash.c diff -N src/tools/solver/lp_solve/lp_Hash.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_Hash.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,187 @@ + +#include +#include +#include "lp_lib.h" +#include "lp_utils.h" +#include "lp_Hash.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +#define HASH_START_SIZE 5000 /* Hash table size for row and column name storage */ +#define NUMHASHPRIMES 31 + +STATIC hashtable *create_hash_table(int size, int base) +{ + int i; + int HashPrimes[ ] = { + 29, 229, 883, 1671, 2791, 4801, 8629, 10007, + 15289, 25303, 34843, 65269, 99709, 129403, 147673, 166669, + 201403, 222163, 242729, 261431, 303491, 320237, 402761, 501131, + 602309, 701507, 800999, 900551, 1000619, 1100837, 1200359 }; + hashtable *ht; + + /* Find a good size for the hash table */ + if(size < HASH_START_SIZE) + size = HASH_START_SIZE; + for(i = 0; i < NUMHASHPRIMES-1; i++) + if(HashPrimes[i] > size) + break; + size = HashPrimes[i]; + + /* Then allocate and initialize memory */ + ht = (hashtable *) calloc(1 , sizeof(*ht)); + ht->table = (hashelem **) calloc(size, sizeof(*(ht->table))); + ht->size = size; + ht->base = base; + ht->count = base-1; + + return(ht); +} + +STATIC void free_hash_item(hashelem **hp) +{ + free((*hp)->name); + free(*hp); + *hp = NULL; +} + +STATIC void free_hash_table(hashtable *ht) +{ + hashelem *hp, *thp; + + hp = ht->first; + while(hp != NULL) { + thp = hp; + hp = hp->nextelem; + free_hash_item(&thp); + } + free(ht->table); + free(ht); +} + + +/* make a good hash function for any int size */ +/* inspired by Aho, Sethi and Ullman, Compilers ..., p436 */ +#define HASH_1 sizeof(unsigned int) +#define HASH_2 (sizeof(unsigned int) * 6) +#define HASH_3 (((unsigned int)0xF0) << ((sizeof(unsigned int) - 1) * CHAR_BIT)) + +STATIC int hashval(const char *string, int size) +{ + unsigned int result = 0, tmp; + + for(; *string; string++) { + result = (result << HASH_1) + *string; + if((tmp = result & HASH_3) != 0) { + /* if any of the most significant bits is on */ + result ^= tmp >> HASH_2; /* xor them in in a less significant part */ + result ^= tmp; /* and reset the most significant bits to 0 */ + } + } + return(result % size); +} /* hashval */ + + +STATIC hashelem *findhash(const char *name, hashtable *ht) +{ + hashelem *h_tab_p; + for(h_tab_p = ht->table[hashval(name, ht->size)]; + h_tab_p != NULL; + h_tab_p = h_tab_p->next) + if(strcmp(name, h_tab_p->name) == 0) /* got it! */ + break; + return(h_tab_p); +} /* findhash */ + + +STATIC hashelem *puthash(const char *name, int index, hashelem **list, hashtable *ht) +{ + hashelem *hp = NULL; + int hashindex; + + if(list != NULL) { + hp = list[index]; + if(hp != NULL) + list[index] = NULL; + } + + if((hp = findhash(name, ht)) == NULL) { + + hashindex = hashval(name, ht->size); + hp = (hashelem *) calloc(1, sizeof(*hp)); + allocCHAR(NULL, &hp->name, (int) (strlen(name) + 1), FALSE); + strcpy(hp->name, name); + hp->index = index; + ht->count++; + if(list != NULL) + list[index] = hp; + + hp->next = ht->table[hashindex]; + ht->table[hashindex] = hp; + if(ht->first == NULL) + ht->first = hp; + if(ht->last != NULL) + ht->last->nextelem = hp; + ht->last = hp; + + } + return(hp); +} + +STATIC hashtable *copy_hash_table(hashtable *ht, hashelem **list, int newsize) +{ + hashtable *copy; + hashelem *elem, *new_elem; + + if(newsize < ht->size) + newsize = ht->size; + + copy = create_hash_table(newsize, ht->base); + if (copy != NULL) { + elem = ht->first; + while (elem != NULL) { + if((new_elem = puthash(elem->name, elem->index, list, copy)) == NULL) { + free_hash_table(copy); + return(NULL); + } + elem = elem ->nextelem; + } + } + + return(copy); +} + +STATIC int find_row(lprec *lp, char *name, MYBOOL Unconstrained_rows_found) +{ + hashelem *hp; + + hp = findhash(name, lp->rowname_hashtab); + + if (hp == NULL) { + if(Unconstrained_rows_found) { /* just ignore them in this case */ + return(-1); + } + else { + return(-1); + } + } + return(hp->index); +} + +STATIC int find_var(lprec *lp, char *name, MYBOOL verbose) +{ + hashelem *hp; + + hp = findhash(name, lp->colname_hashtab); + + if (hp == NULL) { + if(verbose) + report(lp, SEVERE, "find_var: Unknown variable name '%s'\n", name); + return(-1); + } + return(hp->index); +} + Index: src/tools/solver/lp_solve/lp_Hash.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_Hash.h diff -N src/tools/solver/lp_solve/lp_Hash.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_Hash.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,41 @@ +#ifndef HEADER_lp_hash +#define HEADER_lp_hash + +/* For row and column name hash tables */ + +typedef struct _hashelem +{ + char *name; + int index; + struct _hashelem *next; + struct _hashelem *nextelem; +} hashelem; + +typedef struct _hashtable +{ + hashelem **table; + int size; + int base; + int count; + struct _hashelem *first; + struct _hashelem *last; +} hashtable; + +#ifdef __cplusplus +extern "C" { +#endif + +STATIC hashtable *create_hash_table(int size, int base); +STATIC void free_hash_table(hashtable *ht); +STATIC hashelem *findhash(const char *name, hashtable *ht); +STATIC hashelem *puthash(const char *name, int index, hashelem **list, hashtable *ht); +STATIC void free_hash_item(hashelem **hp); +STATIC hashtable *copy_hash_table(hashtable *ht, hashelem **list, int newsize); +STATIC int find_var(lprec *lp, char *name, MYBOOL verbose); +STATIC int find_row(lprec *lp, char *name, MYBOOL Unconstrained_rows_found); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_hash */ Index: src/tools/solver/lp_solve/lp_MDO.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_MDO.c diff -N src/tools/solver/lp_solve/lp_MDO.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_MDO.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,233 @@ +/* + Minimum matrix inverse fill-in modules - interface for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: string.h, colamd.h, lp_lib.h + + Release notes: + v1.0 1 September 2003 Preprocessing routines for minimum fill-in column + ordering for inverse factorization using the open + source COLAMD library. Suitable for the dense parts + of both the product form and LU factorization inverse + methods. + v1.1 1 July 2004 Renamed from lp_colamdMDO to lp_MDO. + + ---------------------------------------------------------------------------------- +*/ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "colamd.h" +#include "lp_MDO.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +STATIC MYBOOL includeMDO(MYBOOL *usedpos, int item) +{ +/* Legend: TRUE => A basic slack variable already in the basis + FALSE => A column free for being pivoted in + AUTOMATIC+TRUE => A row-singleton user column pivoted into the basis + AUTOMATIC+FALSE => A column-singleton user column pivoted into the basis +*/ + MYBOOL test = usedpos[item]; + +#if 1 + return( test != TRUE ); +#else + test = test & TRUE; + return( test == FALSE ); +#endif +} + +STATIC int prepareMDO(lprec *lp, MYBOOL *usedpos, int *colorder, int *data, int *rowmap) +/* This routine prepares data structures for colamd(). It is called twice, the first + time to count applicable non-zero elements by column, and the second time to fill in + the row indexes of the non-zero values from the first call. Note that the colamd() + row index base is 0 (which suits lp_solve fine). */ +{ + int i, ii, j, k, kk, rn; + int nrows = lp->rows+1, ncols = colorder[0]; + int offset = 0, Bnz = 0, Tnz; + MYBOOL dotally = (MYBOOL) (rowmap == NULL); + MATitem *matelem; + REAL hold; + + if(dotally) + data[0] = 0; + + Tnz = nrows - ncols; + for(j = 1; j <= ncols; j++) { + kk = colorder[j]; + + /* Process slacks */ + if(kk <= lp->rows) { + if(includeMDO(usedpos, kk)) { + if(!dotally) + data[Bnz] = rowmap[kk]+offset; + Bnz++; + } + Tnz++; + } + /* Process user columns */ + else { + k = kk - lp->rows; + i = lp->matA->col_end[k-1]; + ii= lp->matA->col_end[k]; + Tnz += ii-i; +#ifdef Paranoia + if(i >= ii) + lp->report(lp, SEVERE, "prepareMDO: Encountered empty basic column %d\n", k); +#endif + + /* Detect if we need to do phase 1 adjustments of zero-valued OF variable */ + matelem = lp->matA->col_mat + i; + rn = (*matelem).row_nr; + hold = 0; + if((rn > 0) && includeMDO(usedpos, 0) && lp->modifyOF1(lp, kk, &hold, 1.0)) { + if(!dotally) + data[Bnz] = offset; + Bnz++; + } + /* Loop over all NZ-variables */ + for(; i < ii; i++, matelem++) { + rn = (*matelem).row_nr; + if(!includeMDO(usedpos, rn)) + continue; + /* See if we need to change phase 1 OF value */ + if(rn == 0) { + hold = (*matelem).value; + if(!lp->modifyOF1(lp, kk, &hold, 1.0)) + continue; + } + /* Tally uneliminated constraint row values */ + if(!dotally) + data[Bnz] = rowmap[rn]+offset; + Bnz++; + } + } + if(dotally) + data[j] = Bnz; + } + return( Tnz ); +} + +STATIC MYBOOL verifyMDO(lprec *lp, int *col_end, int *row_nr, int rowmax, int colmax) +{ + int i, j, n, err = 0; + + for(i = 1; i <= colmax; i++) { + n = 0; + for(j = col_end[i-1]; (j < col_end[i]) && (err == 0); j++, n++) { + if(row_nr[j] < 0 || row_nr[j] > rowmax) + err = 1; + if(n > 0 && row_nr[j] <= row_nr[j-1]) + err = 2; + n++; + } + } + if(err != 0) + lp->report(lp, SEVERE, "verifyMDO: Invalid MDO input structure generated (error %d)\n", err); + return( (MYBOOL) (err == 0) ); +} + +STATIC void *mdo_calloc(size_t size, size_t count) +{ + return ( calloc(size, count) ); +} +STATIC void mdo_free(void *mem) +{ + free( mem ); +} + + +STATIC int getMDO(lprec *lp, MYBOOL *usedpos, int *colorder, int *size, MYBOOL symmetric) +{ + int error = FALSE; + int nrows = lp->rows+1, ncols = colorder[0]; + int i, j, kk, n; + int *col_end, *row_map = NULL; + int Bnz, Blen, *Brows = NULL; + int stats[COLAMD_STATS]; + double knobs[COLAMD_KNOBS]; + + /* Tally the non-zero counts of the unused columns/rows of the + basis matrix and store corresponding "net" starting positions */ + allocINT(lp, &col_end, ncols+1, FALSE); + n = prepareMDO(lp, usedpos, colorder, col_end, NULL); + Bnz = col_end[ncols]; + + /* Check that we have unused basic columns, otherwise skip analysis */ + if(ncols == 0 || Bnz == 0) + goto Transfer; + + /* Get net number of rows and fill mapper */ + allocINT(lp, &row_map, nrows, FALSE); + nrows = 0; + for(i = 0; i <= lp->rows; i++) { + row_map[i] = i-nrows; + /* Increment eliminated row counter if necessary */ + if(!includeMDO(usedpos, i)) + nrows++; + } + nrows = lp->rows+1 - nrows; + + /* Store row indeces of non-zero values in the basic columns */ + Blen = colamd_recommended(Bnz, nrows, ncols); + allocINT(lp, &Brows, Blen, FALSE); + prepareMDO(lp, usedpos, colorder, Brows, row_map); +#ifdef Paranoia + verifyMDO(lp, col_end, Brows, nrows, ncols); +#endif + + /* Compute the MDO */ +#if 1 + colamd_set_defaults(knobs); + knobs [COLAMD_DENSE_ROW] = 0.2+0.2 ; /* default changed for UMFPACK */ + knobs [COLAMD_DENSE_COL] = knobs [COLAMD_DENSE_ROW]; + if(symmetric && (nrows == ncols)) { + MEMCOPY(colorder, Brows, ncols + 1); + error = !symamd(nrows, colorder, col_end, Brows, knobs, stats, mdo_calloc, mdo_free); + } + else + error = !colamd(nrows, ncols, Blen, Brows, col_end, knobs, stats); +#else + if(symmetric && (nrows == ncols)) { + MEMCOPY(colorder, Brows, ncols + 1); + error = !symamd(nrows, colorder, col_end, Brows, knobs, stats, mdo_calloc, mdo_free); + } + else + error = !colamd(nrows, ncols, Blen, Brows, col_end, (double *) NULL, stats); +#endif + + /* Transfer the estimated optimal ordering, adjusting for index offsets */ +Transfer: + if(error) + error = stats[COLAMD_STATUS]; + else { + MEMCOPY(Brows, colorder, ncols + 1); + for(j = 0; j < ncols; j++) { + kk = col_end[j]; + n = Brows[kk+1]; + colorder[j+1] = n; + } + } + + /* Free temporary vectors */ + FREE(col_end); + if(row_map != NULL) + FREE(row_map); + if(Brows != NULL) + FREE(Brows); + + if(size != NULL) + *size = ncols; + return( error ); +} + + Index: src/tools/solver/lp_solve/lp_MDO.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_MDO.h diff -N src/tools/solver/lp_solve/lp_MDO.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_MDO.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,17 @@ +#ifndef HEADER_MDO +#define HEADER_MDO + +#include "lp_types.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +STATIC int getMDO(lprec *lp, MYBOOL *usedpos, int *colorder, int *size, MYBOOL symmetric); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_MDO */ Index: src/tools/solver/lp_solve/lp_MPS.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_MPS.c diff -N src/tools/solver/lp_solve/lp_MPS.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_MPS.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1367 @@ + +#include +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_MPS.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* MPS file input and output routines for lp_solve */ +/* ------------------------------------------------------------------------- */ + +/* +A: MPS format was named after an early IBM LP product and has emerged +as a de facto standard ASCII medium among most of the commercial LP +codes. Essentially all commercial LP codes accept this format, but if +you are using public domain software and have MPS files, you may need +to write your own reader routine for this. It's not too hard. The +main things to know about MPS format are that it is column oriented (as +opposed to entering the model as equations), and everything (variables, +rows, etc.) gets a name. MPS format is described in more detail in +Murtagh's book, referenced in another section. Also, + +ftp://softlib.cs.rice.edu/pub/miplib/mps_format + +is a nice short introduction. exports + +MPS is an old format, so it is set up as though you were using punch +cards, and is not free format. Fields start in column 1, 5, 15, 25, 40 +and 50. Sections of an MPS file are marked by so-called header cards, +which are distinguished by their starting in column 1. Although it is +typical to use upper-case throughout the file (like I said, MPS has +long historical roots), many MPS-readers will accept mixed-case for +anything except the header cards, and some allow mixed-case anywhere. +The names that you choose for the individual entities (constraints or +variables) are not important to the solver; you should pick names that +are meaningful to you, or will be easy for a post-processing code to +read. + +Here is a little sample model written in MPS format (explained in more +detail below): + +NAME TESTPROB +ROWS + N COST + L LIM1 + G LIM2 + E MYEQN +COLUMNS + XONE COST 1 LIM1 1 + XONE LIM2 1 + YTWO COST 4 LIM1 1 + YTWO MYEQN -1 + ZTHREE COST 9 LIM2 1 + ZTHREE MYEQN 1 +RHS + RHS1 LIM1 5 LIM2 10 + RHS1 MYEQN 7 +BOUNDS + UP BND1 XONE 4 + LO BND1 YTWO -1 + UP BND1 YTWO 1 +ENDATA + +means: + +Optimize + COST: XONE + 4 YTWO + 9 ZTHREE +Subject To + LIM1: XONE + YTWO <= 5 + LIM2: XONE + ZTHREE >= 10 + MYEQN: - YTWO + ZTHREE = 7 +Bounds + 0 <= XONE <= 4 +-1 <= YTWO <= 1 +End + +*/ + +/* copy a MPS name, only trailing spaces are removed. In MPS, names can have + embedded spaces! */ +STATIC void namecpy(char *into, char *from) +{ + int i; + + /* copy at most 8 characters of from, stop at end of string or newline */ + for(i = 0; (from[i] != '\0') && (from[i] != '\n') && (from[i] != '\r') && (i < 8); i++) + into[i] = from[i]; + + /* end with end of string */ + into[i] = '\0'; + + /* remove trailing spaces, if any */ + for(i--; (i >= 0) && (into[i] == ' '); i--) + into[i] = '\0'; +} + +/* scan an MPS line, and pick up the information in the fields that are + present */ + +/* scan_line for fixed MPS format */ +STATIC int scan_lineFIXED(int section, char* line, char *field1, char *field2, char *field3, + double *field4, char *field5, double *field6) +{ + int items = 0, line_len; + char buf[16], *ptr1, *ptr2; + + line_len = (int) strlen(line); + while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' '))) + line_len--; + + if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */ + strncpy(buf, line, 4); + buf[4] = '\0'; + sscanf(buf, "%s", field1); + items++; + } + else + field1[0] = '\0'; + + line += 4; + + if(line_len >= 5) { /* name */ + namecpy(field2, line); + items++; + } + else + field2[0] = '\0'; + + line += 10; + + if(line_len >= 14) { /* name */ + namecpy(field3, line); + items++; + } + else + field3[0] = '\0'; + + line += 10; + + if(line_len >= 25) { /* number */ + strncpy(buf, line, 15); + buf[15] = '\0'; + for(ptr1 = ptr2 = buf; ; ptr1++) + if(!isspace((unsigned char) *ptr1)) + if((*(ptr2++) = *ptr1) == 0) + break; + /* *field4 = atof(buf); */ + *field4 = strtod(buf, &ptr1); + if(*ptr1) + return(-1); + items++; + } + else + *field4 = 0; + + line += 15; + + if(line_len >= 40) { /* name */ + namecpy(field5, line); + items++; + } + else + field5[0] = '\0'; + line += 10; + + if(line_len >= 50) { /* number */ + strncpy(buf, line, 15); + buf[15] = '\0'; + for(ptr1 = ptr2 = buf; ; ptr1++) + if(!isspace((unsigned char) *ptr1)) + if((*(ptr2++) = *ptr1) == 0) + break; + /* *field6 = atof(buf); */ + *field6 = strtod(buf, &ptr1); + if(*ptr1) + return(-1); + items++; + } + else + *field6 = 0; + + return(items); +} + +STATIC int spaces(char *line, int line_len) +{ + int l; + char *line1 = line; + + while (*line1 == ' ') + line1++; + l = (int) (line1 - line); + if (line_len < l) + l = line_len; + return(l); +} + +STATIC int lenfield(char *line, int line_len) +{ + int l; + char *line1 = line; + + while ((*line1) && (*line1 != ' ')) + line1++; + l = (int) (line1 - line); + if (line_len < l) + l = line_len; + return(l); +} + +/* scan_line for fixed MPS format */ +STATIC int scan_lineFREE(int section, char* line, char *field1, char *field2, char *field3, + double *field4, char *field5, double *field6) +{ + int items = 0, line_len, len; + char buf[256], *ptr1, *ptr2; + + line_len = (int) strlen(line); + while ((line_len) && ((line[line_len-1] == '\n') || (line[line_len-1] == '\r') || (line[line_len-1] == ' '))) + line_len--; + + len = spaces(line, line_len); + line += len; + line_len -= len; + + if ((section == MPSCOLUMNS) || (section == MPSRHS) || (section == MPSRANGES)) { + field1[0] = '\0'; + items++; + } + else { + len = lenfield(line, line_len); + if(line_len >= 1) { /* spaces or N/L/G/E or UP/LO */ + strncpy(buf, line, len); + buf[len] = '\0'; + sscanf(buf, "%s", field1); + items++; + } + else + field1[0] = '\0'; + + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + } + + len = lenfield(line, line_len); + if(line_len >= 1) { /* name */ + strncpy(field2, line, len); + field2[len] = '\0'; + items++; + } + else + field2[0] = '\0'; + + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + + len = lenfield(line, line_len); + if(line_len >= 1) { /* name */ + strncpy(field3, line, len); + field3[len] = '\0'; + items++; + } + else + field3[0] = '\0'; + + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + + if (*field3) { + if((section == MPSCOLUMNS) && (strcmp(field3, "'MARKER'") == 0)) { + *field4 = 0; + items++; + ptr1 = field3; + } + else { + *field4 = strtod(field3, &ptr1); + if(*ptr1 == 0) { + strcpy(field3, field2); + if ((section == MPSROWS) || (section == MPSBOUNDS) /* || (section == MPSSOS) */) + *field2 = 0; + else { + strcpy(field2, field1); + *field1 = 0; + } + items++; + } + else + ptr1 = NULL; + } + } + else ptr1 = NULL; + + if(ptr1 == NULL) { + len = lenfield(line, line_len); + if(line_len >= 1) { /* number */ + strncpy(buf, line, len); + buf[len] = '\0'; + for(ptr1 = ptr2 = buf; ; ptr1++) + if(!isspace((unsigned char) *ptr1)) + if((*(ptr2++) = *ptr1) == 0) + break; + /* *field4 = atof(buf); */ + *field4 = strtod(buf, &ptr1); + if(*ptr1) + return(-1); + items++; + } + else + *field4 = 0; + + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + } + + len = lenfield(line, line_len); + if(line_len >= 1) { /* name */ + strncpy(field5, line, len); + field5[len] = '\0'; + items++; + } + else + field5[0] = '\0'; + line += len; + line_len -= len; + + len = spaces(line, line_len); + line += len; + line_len -= len; + + len = lenfield(line, line_len); + if(line_len >= 1) { /* number */ + strncpy(buf, line, len); + buf[len] = '\0'; + for(ptr1 = ptr2 = buf; ; ptr1++) + if(!isspace((unsigned char) *ptr1)) + if((*(ptr2++) = *ptr1) == 0) + break; + /* *field6 = atof(buf); */ + *field6 = strtod(buf, &ptr1); + if(*ptr1) + return(-1); + items++; + } + else + *field6 = 0; + + if((section == MPSSOS) && (items == 2)) { + strcpy(field3, field2); + strcpy(field2, field1); + *field1 = 0; + } + + for(ptr1 = field1; *ptr1; ptr1++) + *ptr1=toupper(*ptr1); + + return(items); +} + +STATIC int addmpscolumn(lprec *lp, MYBOOL Int_section, MYBOOL *Column_ready, + int *count, REAL *Last_column, int *Last_columnno, char *Last_col_name) +{ + int ok = TRUE; + + if (*Column_ready) { + ok = add_columnex(lp, *count, Last_column, Last_columnno); + if (ok) { + ok = set_col_name(lp, lp->columns, Last_col_name); + } + if (ok) + lp_solve_set_int(lp, lp->columns, Int_section); + } + *Column_ready = FALSE; + *count = 0; + return(ok); +} + +STATIC MYBOOL appendmpsitem(int *count, int rowIndex[], REAL rowValue[]) +{ + int i = *count; + + if(rowValue[i] == 0) + return( FALSE ); + + while((i > 0) && (rowIndex[i] < rowIndex[i-1])) { + swapINT (rowIndex+i, rowIndex+i-1); + swapREAL(rowValue+i, rowValue+i-1); + i--; + } + (*count)++; + return( TRUE ); +} + +lprec *MPS_readfile(char *input, int typeMPS, int verbose) +{ + lprec *lp = NULL; + FILE *fpin; + + fpin = fopen(input, "r"); + if(fpin != NULL) { + lp = MPS_readhandle(fpin, typeMPS, verbose); + fclose(fpin); + } + return(lp); +} + +lprec *MPS_readhandle(FILE *input, int typeMPS, int verbose) +{ + char field1[BUFSIZ], field2[BUFSIZ], field3[BUFSIZ], field5[BUFSIZ], line[BUFSIZ], tmp[BUFSIZ], + Last_col_name[BUFSIZ], probname[BUFSIZ], *ptr; + int items, row, Lineno, var, + section = MPSUNDEF, variant = 0, NZ = 0, SOS = 0; + MYBOOL Int_section, Column_ready, Column_ready1, + Unconstrained_rows_found = FALSE, OF_found = FALSE, CompleteStatus = FALSE; + double field4, field6; + REAL *Last_column = NULL; + int count = 0, *Last_columnno = NULL; + lprec *lp; + int (*scan_line)(int section, char* line, char *field1, char *field2, char *field3, + double *field4, char *field5, double *field6); + + lp = lp_solve_make_lp(0, 0); + + switch(typeMPS) { + case MPSFIXED: + scan_line = scan_lineFIXED; + break; + case MPSFREE: + scan_line = scan_lineFREE; + break; + default: + report(lp, IMPORTANT, "MPS_readfile: unrecognized MPS file type.\n"); + lp_solve_delete_lp(lp); + return(NULL); + } + + if (lp != NULL) { + lp->source_is_file = TRUE; + lp->verbose = verbose; + strcpy(Last_col_name, ""); + Int_section = FALSE; + Column_ready = FALSE; + Lineno = 0; + + /* let's initialize line to all zero's */ + MEMCLEAR(line, BUFSIZ); + + while(fgets(line, BUFSIZ - 1, input)) { + Lineno++; + + for(ptr = line; (*ptr) && (isspace((unsigned char) *ptr)); ptr++); + + /* skip lines which start with "*", they are comment */ + if((line[0] == '*') || (*ptr == 0) || (*ptr == '\n') || (*ptr == '\r')) { + report(lp, FULL, "Comment on line %d: %s", Lineno, line); + continue; + } + + report(lp, FULL, "Line %6d: %s", Lineno, line); + + /* first check for "special" lines: NAME, ROWS, BOUNDS .... */ + /* this must start in the first position of line */ + if(line[0] != ' ') { + sscanf(line, "%s", tmp); + if(strcmp(tmp, "NAME") == 0) { + section = MPSNAME; + sscanf(line, "NAME %s", probname); + if (!set_lp_name(lp, probname)) + break; + } + else if(strcmp(tmp, "ROWS") == 0) { + section = MPSROWS; + report(lp, FULL, "Switching to ROWS section\n"); + } + else if(strcmp(tmp, "COLUMNS") == 0) { + allocREAL(lp, &Last_column, lp->rows + 1, TRUE); + allocINT(lp, &Last_columnno, lp->rows + 1, TRUE); + count = 0; + if ((Last_column == NULL) || (Last_columnno == NULL)) + break; + section = MPSCOLUMNS; + report(lp, FULL, "Switching to COLUMNS section\n"); + } + else if(strcmp(tmp, "RHS") == 0) { + if (!addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) + break; + section = MPSRHS; + report(lp, FULL, "Switching to RHS section\n"); + } + else if(strcmp(tmp, "BOUNDS") == 0) { + section = MPSBOUNDS; + report(lp, FULL, "Switching to BOUNDS section\n"); + } + else if(strcmp(tmp, "RANGES") == 0) { + section = MPSRANGES; + report(lp, FULL, "Switching to RANGES section\n"); + } + else if((strcmp(tmp, "SOS") == 0) || (strcmp(tmp, "SETS") == 0)) { + section = MPSSOS; + if(strcmp(tmp, "SOS") == 0) + variant = 0; + else + variant = 1; + report(lp, FULL, "Switching to %s section\n", tmp); + } + else if(strcmp(tmp, "ENDATA") == 0) { + report(lp, FULL, "Finished reading MPS file\n"); + CompleteStatus = TRUE; + break; + } + else { /* line does not start with space and does not match above */ + report(lp, IMPORTANT, "Unrecognized line %d: %s\n", Lineno, line); + break; + } + } + else { /* normal line, process */ + items = scan_line(section, line, field1, field2, field3, &field4, field5, &field6); + if(items < 0){ + report(lp, IMPORTANT, "Syntax error on line %d: %s\n", Lineno, line); + break; + } + + switch(section) { + + case MPSNAME: + report(lp, IMPORTANT, "Error, extra line under NAME line\n"); + break; + + /* Process entries in the ROWS section */ + case MPSROWS: + /* field1: rel. operator; field2: name of constraint */ + + report(lp, FULL, "Row %5d: %s %s\n", lp->rows + 1, field1, field2); + + if(strcmp(field1, "N") == 0) { + if(!OF_found) { /* take the first N row as OF, ignore others */ + if (!set_row_name(lp, 0, field2)) + break; + OF_found = TRUE; + } + else if(!Unconstrained_rows_found) { + report(lp, IMPORTANT, "Unconstrained row %s ignored\n", field2); + report(lp, IMPORTANT, "Further messages of this kind will be suppressed\n"); + Unconstrained_rows_found = TRUE; + } + } + else if(strcmp(field1, "L") == 0) { + if ((!str_add_constraint(lp, "" ,LE ,0)) || (!set_row_name(lp, lp->rows, field2))) + break; + } + else if(strcmp(field1, "G") == 0) { + if ((!str_add_constraint(lp, "" ,GE ,0)) || (!set_row_name(lp, lp->rows, field2))) + break; + } + else if(strcmp(field1, "E") == 0) { + if ((!str_add_constraint(lp, "",EQ ,0)) || (!set_row_name(lp, lp->rows, field2))) + break; + } + else { + report(lp, SEVERE, "Unknown relation code '%s' on line %d\n", field1, Lineno); + break; + } + + continue; + + /* Process entries in the COLUMNS section */ + case MPSCOLUMNS: + /* field2: variable; field3: constraint; field4: coef */ + /* optional: field5: constraint; field6: coef */ + + report(lp, FULL, "Column %4d: %s %s %g %s %g\n", + lp->columns + 1, field2, field3, field4, field5, field6); + + if((items == 4) || (items == 5) || (items == 6)) { + if (NZ == 0) + strcpy(Last_col_name, field2); + else if(*field2) { + Column_ready1 = (MYBOOL) (strcmp(field2, Last_col_name) != 0); + if(Column_ready1) { + if (find_var(lp, field2, FALSE) >= 0) { + report(lp, SEVERE, "Variable name (%s) is already used!\n", field2); + break; + } + + if(Column_ready) { /* Added ability to handle non-standard "same as above" column name */ + if (addmpscolumn(lp, Int_section, &Column_ready, &count, Last_column, Last_columnno, Last_col_name)) { + strcpy(Last_col_name, field2); + NZ = 0; + } + else + break; + } + } + } + if(items == 5) { /* there might be an INTEND or INTORG marker */ + /* look for " 'MARKER' 'INTORG'" + or " 'MARKER' 'INTEND'" */ + if(strcmp(field3, "'MARKER'") != 0) + break; + if(strcmp(field5, "'INTORG'") == 0) { + Int_section = TRUE; + report(lp, FULL, "Switching to integer section\n"); + } + else if(strcmp(field5, "'INTEND'") == 0) { + Int_section = FALSE; + report(lp, FULL, "Switching to non-integer section\n"); + } + else + report(lp, IMPORTANT, "Unknown marker (ignored) at line %d: %s\n", + Lineno, field5); + } + else if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { + if(row > lp->rows) + report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field3); + Last_columnno[count] = row; + Last_column[count] = (REAL)field4; + if(appendmpsitem(&count, Last_columnno, Last_column)) { + NZ++; + Column_ready = TRUE; + } + } + } + if(items == 6) { + if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) { + if(row > lp->rows) + report(lp, CRITICAL, "Invalid row %s encountered in the MPS file\n", field6); + Last_columnno[count] = row; + Last_column[count] = (REAL)field6; + if(appendmpsitem(&count, Last_columnno, Last_column)) { + NZ++; + Column_ready = TRUE; + } + } + } + + if((items < 4) || (items > 6)) { /* Wrong! */ + report(lp, CRITICAL, "Wrong number of items (%d) in COLUMNS section (line %d)\n", + items, Lineno); + break; + } + + continue; + + /* Process entries in the RHS section */ + /* field2: uninteresting name; field3: constraint name */ + /* field4: value */ + /* optional: field5: constraint name; field6: value */ + case MPSRHS: + + report(lp, FULL, "RHS line: %s %s %g %s %g\n", + field2, field3, field4, field5, field6); + + if((items != 4) && (items != 6)) { + report(lp, CRITICAL, "Wrong number of items (%d) in RHS section line %d\n", + items, Lineno); + break; + } + + if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { + lp_solve_set_rh(lp, row, (REAL)field4); + } + + if(items == 6) { + if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) { + lp_solve_set_rh(lp, row, (REAL)field6); + } + } + + continue; + + /* Process entries in the BOUNDS section */ + /* field1: bound type; field2: uninteresting name; */ + /* field3: variable name; field4: value */ + case MPSBOUNDS: + + report(lp, FULL, "BOUNDS line: %s %s %s %g\n", + field1, field2, field3, field4); + + var = find_var(lp, field3, FALSE); + if(var < 0){ /* bound on undefined var in COLUMNS section ... */ + Column_ready = TRUE; + if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field3)) + break; + Column_ready = TRUE; + var = find_var(lp, field3, TRUE); + } + if(var < 0) /* undefined var and could add ... */; + else if(strcmp(field1, "UP") == 0) { + /* upper bound */ + set_bounds(lp, var, get_lowbo(lp, var), field4); + } + else if(strcmp(field1, "SC") == 0) { + /* upper bound */ + if(field4 == 0) + field4 = lp->infinite; + set_bounds(lp, var, get_lowbo(lp, var), field4); + set_semicont(lp, var, TRUE); + } + else if(strcmp(field1, "SI") == 0) { + /* upper bound */ + if(field4 == 0) + field4 = lp->infinite; + set_bounds(lp, var, get_lowbo(lp, var), field4); + lp_solve_set_int(lp, var, TRUE); + set_semicont(lp, var, TRUE); + } + else if(strcmp(field1, "LO") == 0) { + /* lower bound */ + if((field4 < lp->negrange) && (lp->splitnegvars == AUTOMATIC)) + field4 = -lp->infinite; + set_bounds(lp, var, field4, get_upbo(lp, var)); + } + else if(strcmp(field1, "PL") == 0) /* plus-ranged variable */ + set_bounds(lp, var, get_lowbo(lp, var), lp->infinite); + else if(strcmp(field1, "MI") == 0) { /* minus-ranged variable */ + set_bounds(lp, var, -lp->infinite, get_upbo(lp, var)); + } + else if(strcmp(field1, "FR") == 0) { /* free variable */ + set_free(lp, var); + } + else if(strcmp(field1, "FX") == 0) { + /* fixed, upper _and_ lower */ + set_bounds(lp, var, field4, field4); + } + else if(strcmp(field1, "BV") == 0) { /* binary variable */ + set_binary(lp, var, TRUE); + } + /* AMPL bounds type UI and LI added by E.Imamura (CRIEPI) */ + else if(strcmp(field1, "UI") == 0) { /* upper bound for integer variable */ + set_bounds(lp, var, get_lowbo(lp, var), field4); + lp_solve_set_int(lp, var, TRUE); + } + else if(strcmp(field1, "LI") == 0) { /* lower bound for integer variable - corrected by KE */ + set_bounds(lp, var, field4, get_upbo(lp, var)); + lp_solve_set_int(lp, var, TRUE); + } + else { + report(lp, CRITICAL, "BOUND type %s on line %d is not supported", + field1, Lineno); + break; + } + + continue; + + /* Process entries in the BOUNDS section */ + + /* We have to implement the following semantics: + + D. The RANGES section is for constraints of the form: h <= + constraint <= u . The range of the constraint is r = u - h . The + value of r is specified in the RANGES section, and the value of u or + h is specified in the RHS section. If b is the value entered in the + RHS section, and r is the value entered in the RANGES section, then + u and h are thus defined: + + row type sign of r h u + ---------------------------------------------- + G + or - b b + |r| + L + or - b - |r| b + E + b b + |r| + E - b - |r| b */ + + /* field2: uninteresting name; field3: constraint name */ + /* field4: value */ + /* optional: field5: constraint name; field6: value */ + + case MPSRANGES: + + report(lp, FULL, "RANGES line: %s %s %g %s %g", + field2, field3, field4, field5, field6); + + if((items != 4) && (items != 6)) { + report(lp, CRITICAL, "Wrong number of items (%d) in RANGES section line %d", + items, Lineno); + break; + } + + if((row = find_row(lp, field3, Unconstrained_rows_found)) >= 0) { + /* Determine constraint type */ + + if(fabs(field4) >= lp->infinite) { + report(lp, IMPORTANT, + "Warning, Range for row %s >= infinity (value %g) on line %d, ignored", + field3, field4, Lineno); + } + else if(field4 == 0) { + /* Change of a GE or LE to EQ */ + if(lp->orig_upbo[row] != 0) + lp_solve_set_constr_type(lp, row, EQ); + } + else if(is_chsign(lp, row)) { + /* GE */ + lp->orig_upbo[row] = fabs(field4); + } + else if((lp->orig_upbo[row] == 0) && (field4 >= 0)) { + /* EQ with positive sign of r value */ + lp_solve_set_constr_type(lp, row, GE); + lp->orig_upbo[row] = field4; + } + else if(lp->orig_upbo[row] == lp->infinite) { + /* LE */ + lp->orig_upbo[row] = fabs(field4); + } + else if((lp->orig_upbo[row] == 0) && (field4 < 0)) { + /* EQ with negative sign of r value */ + lp_solve_set_constr_type(lp, row, LE); + lp->orig_upbo[row] = my_flipsign(field4); + } + else { /* let's be paranoid */ + report(lp, IMPORTANT, + "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line %d", + row, is_chsign(lp, row), (double)lp->orig_upbo[row], Lineno); + } + } + + if(items == 6) { + if((row = find_row(lp, field5, Unconstrained_rows_found)) >= 0) { + /* Determine constraint type */ + + if(fabs(field6) >= lp->infinite) { + report(lp, IMPORTANT, + "Warning, Range for row %s >= infinity (value %g) on line %d, ignored", + field5, field6, Lineno); + } + else if(field6 == 0) { + /* Change of a GE or LE to EQ */ + if(lp->orig_upbo[row] != 0) + lp_solve_set_constr_type(lp, row, EQ); + } + else if(is_chsign(lp, row)) { + /* GE */ + lp->orig_upbo[row] = fabs(field6); + } + else if(lp->orig_upbo[row] == 0 && field6 >= 0) { + /* EQ with positive sign of r value */ + lp_solve_set_constr_type(lp, row, GE); + lp->orig_upbo[row] = field6; + } + else if(lp->orig_upbo[row] == lp->infinite) { + /* LE */ + lp->orig_upbo[row] = fabs(field6); + } + else if((lp->orig_upbo[row] == 0) && (field6 < 0)) { + /* EQ with negative sign of r value */ + lp_solve_set_constr_type(lp, row, LE); + lp->orig_upbo[row] = my_flipsign(field6); + } + else { /* let's be paranoid */ + report(lp, IMPORTANT, + "Cannot figure out row type, row = %d, is_chsign = %d, upbo = %g on line", + row, is_chsign(lp,row), (REAL) lp->orig_upbo[row], Lineno); + } + } + } + + continue; + + /* Process entries in the SOS section */ + + /* We have to implement the following semantics: + + E. The SOS section is for ordered variable sets of the form: + x1, x2, x3 ... xn where only a given number of consequtive variables + may be non-zero. Each set definition is prefaced by type, name + and priority data. Each set member has an optional weight that + determines its order. There are two forms supported; a full format + and a reduced CPLEX-like format. */ + + case MPSSOS: + report(lp, FULL, "SOS line: %s %s %g %s %g", + field2, field3, field4, field5, field6); + + if((items == 0) || (items > 4)) { + report(lp, IMPORTANT, + "Invalid number of items (%d) in SOS section line %d\n", + items, Lineno); + break; + } + + if(strlen(field1) == 0) items--; /* fix scanline anomoly! */ + + /* Check if this is the start of a new SOS */ + if(items == 1 || items == 4) { + row = (int) (field1[1] - '0'); + if((row <= 0) || (row > 9)) { + report(lp, IMPORTANT, + "Error: Invalid SOS type %s line %d\n", field1, Lineno); + break; + } + field1[0] = '\0'; /* fix scanline anomoly! */ + + /* lp_solve needs a name for the SOS */ + if(variant == 0) { + if(strlen(field3) == 0) /* CPLEX format does not provide a SOS name; create one */ + sprintf(field3, "SOS_%d", SOS_count(lp) + 1); + } + else { /* Remap XPRESS format name */ + strcpy(field3, field1); + } + /* Obtain the SOS priority */ + if(items == 4) + SOS = (int) field4; + else + SOS = 1; + + /* Define a new SOS instance */ + + SOS = add_SOS(lp, field3, (int) row, SOS, 0, NULL, NULL); + } + /* Otherwise, add set members to the active SOS */ + else { + char *field = (items == 3) ? field3 /* Native lp_solve and XPRESS formats */ : field2 /* CPLEX format */; + + var = find_var(lp, field, FALSE); /* Native lp_solve and XPRESS formats */ + if(var < 0){ /* SOS on undefined var in COLUMNS section ... */ + Column_ready = TRUE; + if (!addmpscolumn(lp, FALSE, &Column_ready, &count, Last_column, Last_columnno, field)) + break; + Column_ready = TRUE; + var = find_var(lp, field, TRUE); + } + if((var < 0) || (SOS < 1)) /* undefined var and could add ... */; + else append_SOSrec(lp->SOS->sos_list[SOS-1], 1, &var, &field4); + } + + continue; + } + + /* If we got here there was an error "upstream" */ + report(lp, IMPORTANT, + "Error: Cannot handle line %d\n", Lineno); + break; + } + } + + if(CompleteStatus == FALSE) { + lp_solve_delete_lp(lp); + lp = NULL; + } + if(Last_column != NULL) + FREE(Last_column); + if(Last_columnno != NULL) + FREE(Last_columnno); + } + + return(lp); +} + +static void number(char *str,REAL value) + { + char __str[80], *_str; + int i; + + /* sprintf(_str,"%12.6G",value); */ + _str=__str+2; + if (value>=0.0) + if ((value!=0.0) && ((value>0.99999999e12) || (value<0.0001))) { + int n=15; + + do { + n--; + i=sprintf(_str,"%*.*E",n,n-6,(double) value); + if (i>12) { + char *ptr=strchr(_str,'E'); + + if (ptr!=NULL) { + if (*(++ptr)=='-') ptr++; + while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) { + strcpy(ptr,ptr+1); + i--; + } + } + } + } while (i>12); + } + else if (value>=1.0e10) { + int n=13; + + do { + i=sprintf(_str,"%*.0f",--n,(double) value); + } while (i>12); + } + else { + if (((i=sprintf(_str,"%12.10f",(double) value))>12) && (_str[12]>='5')) { + for (i=11;i>=0;i--) + if (_str[i]!='.') { + if (++_str[i]>'9') _str[i]='0'; + else break; + } + if (i<0) { + *(--_str)='1'; + *(--_str)=' '; + } + } + } + else + if ((value<-0.99999999e11) || (value>-0.0001)) { + int n=15; + + do { + n--; + i=sprintf(_str,"%*.*E",n,n-7,(double) value); + if (i>12) { + char *ptr=strchr(_str,'E'); + + if (ptr!=NULL) { + if (*(++ptr)=='-') ptr++; + while ((i>12) && ((*ptr=='+') || (*ptr=='0'))) { + strcpy(ptr,ptr+1); + i--; + } + } + } + } while (i>12); + } + else if (value<=-1.0e9) { + int n=13; + + do { + i=sprintf(_str,"%*.0f",--n,(double) value); + } while (i>12); + } + else + if (((i=sprintf(_str,"%12.9f",(double) value))>12) && (_str[12]>='5')) { + for (i=11;i>=1;i--) + if (_str[i]!='.') { + if (++_str[i]>'9') _str[i]='0'; + else break; + } + if (i<1) { + *_str='1'; + *(--_str)='-'; + *(--_str)=' '; + } + } + strncpy(str,_str,12); + } + +static char numberbuffer[15]; + +static char *formatnumber12(double a) +{ +#if 0 + return(sprintf(numberbuffer, "%12g", a)); +#else + number(numberbuffer, a); + return(numberbuffer); +#endif +} + +STATIC char *MPSnameFIXED(char *name) +{ + static char name0[9]; + + sprintf(name0, "%-8.8s", name); + return(name0); +} + +STATIC char *MPSnameFREE(char *name) +{ + if(strlen(name) < 8) + return(MPSnameFIXED(name)); + else + return(name); +} + +MYBOOL MPS_writefile(lprec *lp, int typeMPS, char *filename) +{ + int i, j, jj, je, k, marker, putheader; + MYBOOL ok, names_used; + REAL a; + FILE *output = stdout; + char * (*MPSname)(char *name); + +#ifdef Paranoia + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "MPS_writefile: Cannot write to MPS file while in row entry mode.\n"); + return(FALSE); + } +#endif + + switch(typeMPS) { + case MPSFIXED: + MPSname = MPSnameFIXED; + break; + case MPSFREE: + MPSname = MPSnameFREE; + break; + default: + report(lp, IMPORTANT, "MPS_writefile: unrecognized MPS file type.\n"); + return(FALSE); + } + + ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); + if(!ok) + return(ok); + if(filename == NULL && lp->outstream != NULL) + output = lp->outstream; + + names_used = lp->names_used; + + if(typeMPS == MPSFIXED) { + /* Check if there is no variable name where the first 8 charachters are equal to the first 8 characters of anothe variable */ + if(names_used) + for(i = 1; (i <= lp->columns) && (ok); i++) + if((lp->col_name[i] != NULL) && (lp->col_name[i]->name != NULL) && (!is_splitvar(lp, i)) && (strlen(lp->col_name[i]->name) > 8)) + for(j = 1; (j < i) && (ok); j++) + if((lp->col_name[j] != NULL) && (lp->col_name[j]->name != NULL) && (!is_splitvar(lp, j))) + if(strncmp(lp->col_name[i]->name, lp->col_name[j]->name, 8) == 0) + ok = FALSE; + } + + if(!ok) { + lp->names_used = FALSE; + ok = TRUE; + } + + marker = 0; + fprintf(output, "* MPS file created by lp_solve v%d.%d!\n", + (int) MAJORVERSION, (int) MINORVERSION); + fprintf(output, "* Model constraints = %d (of which %d equalities)\n", + lp->rows, lp->equalities); + fprintf(output, "* variables = %d (of which %d integer-valued and %d semi-continuous)\n", + lp->columns, lp->int_count, lp->sc_count); + if(SOS_count(lp) > 0) + fprintf(output, "* SOS = %d (involving %d variables)\n", + SOS_count(lp), lp->sos_vars); + fprintf(output, "*\n"); + fprintf(output, "NAME %s\n", MPSname(lp->lp_name)); + fprintf(output, "ROWS\n"); + for(i = 0; i <= lp->rows; i++) { + if(i == 0) + fprintf(output, " N "); + else if(lp->orig_upbo[i] != 0) { + if(is_chsign(lp,i)) + fprintf(output, " G "); + else + fprintf(output, " L "); + } + else + fprintf(output, " E "); + fprintf(output, "%s\n", MPSname(get_row_name(lp, i))); + } + + fprintf(output, "COLUMNS\n"); + for(i = 1; i <= lp->columns; i++) { + if(!is_splitvar(lp, i)) { + if(is_int(lp,i) && (marker % 2) == 0) { + fprintf(output, + " MARK%04d 'MARKER' 'INTORG'\n", + marker); + marker++; + } + if(!is_int(lp,i) && (marker % 2) == 1) { + fprintf(output, + " MARK%04d 'MARKER' 'INTEND'\n", + marker); + marker++; + } + + /* Loop over non-zero column entries */ + je = lp->matA->col_end[i]; + for(k = 1, jj = lp->matA->col_end[i-1]; jj < je; jj++) { + k = 1 - k; + j = lp->matA->col_mat[jj].row_nr; + a = get_mat_byindex(lp, jj, FALSE); + a = my_chsign(is_chsign(lp, j), a); + if (k == 0) { + fprintf(output, " %s", + MPSname(get_col_name(lp, i))); + fprintf(output, " %s %s", + MPSname(get_row_name(lp, j)), +/* formatnumber12((double) a)); */ + formatnumber12((double) (a * (j == 0 && is_maxim(lp) ? -1 : 1)))); + } + else + fprintf(output, " %s %s\n", + MPSname(get_row_name(lp, j)), + formatnumber12((double) (a * (j == 0 && is_maxim(lp) ? -1 : 1)))); +/* formatnumber12((double) a)); */ + } + if(k == 0) + fprintf(output, "\n"); + } + } + if((marker % 2) == 1) { + fprintf(output, " MARK%04d 'MARKER' 'INTEND'\n", + marker); + /* marker++; */ /* marker not used after this */ + } + + fprintf(output, "RHS\n"); + for(k = 1, i = 0; i <= lp->rows; i++) { + a = lp->orig_rh[i]; + if(a) { + a = unscaled_value(lp, a, i); + if((i == 0) || is_chsign(lp, i)) + a = my_flipsign(a); + k = 1 - k; + if(k == 0) + fprintf(output, " RHS %s %s", + MPSname(get_row_name(lp, i)), + formatnumber12((double)a)); + else + fprintf(output, " %s %s\n", + MPSname(get_row_name(lp, i)), + formatnumber12((double)a)); + } + } + if(k == 0) + fprintf(output, "\n"); + + putheader = TRUE; + for(k = 1, i = 1; i <= lp->rows; i++){ + a = 0; + if((lp->orig_upbo[i] < lp->infinite) && (lp->orig_upbo[i] != 0.0)) + a = lp->orig_upbo[i]; +#if 0 + else if((lp->orig_lowbo[i] > -lp->infinite) && (lp->orig_lowbo[i] != 0.0)) + a = my_flipsign(lp->orig_lowbo[i]); +#endif + if(a) { + if(putheader) { + fprintf(output, "RANGES\n"); + putheader = FALSE; + } + a = unscaled_value(lp, a, i); + k = 1 - k; + if(k == 0) + fprintf(output, " RGS %s %s", + MPSname(get_row_name(lp, i)), + formatnumber12((double)a)); + else + fprintf(output, " %s %s\n", + MPSname(get_row_name(lp, i)), + formatnumber12((double)a)); + } + } + if(k == 0) + fprintf(output, "\n"); + + putheader = TRUE; + for(i = lp->rows + 1; i <= lp->sum; i++) + if(!is_splitvar(lp, i - lp->rows)) { + j = i - lp->rows; + if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite) && + (lp->orig_lowbo[i] == lp->orig_upbo[i])) { + a = lp->orig_upbo[i]; + a = unscaled_value(lp, a, i); + if(putheader) { + fprintf(output, "BOUNDS\n"); + putheader = FALSE; + } + fprintf(output, " FX BND %s %s\n", + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); + } + else if(is_binary(lp, j)) { + if(putheader) { + fprintf(output, "BOUNDS\n"); + putheader = FALSE; + } + fprintf(output, " BV BND %s\n", + MPSname(get_col_name(lp, j))); + } + else if(is_free(lp, j)) { + if(putheader) { + fprintf(output, "BOUNDS\n"); + putheader = FALSE; + } + fprintf(output, " FR BND %s\n", + MPSname(get_col_name(lp, j))); + } + else { + if(lp->orig_upbo[i] < lp->infinite) { + a = lp->orig_upbo[i]; + a = unscaled_value(lp, a, i); + if(putheader) { + fprintf(output, "BOUNDS\n"); + putheader = FALSE; + } + if(is_semicont(lp, j)) { + if(is_int(lp, j)) + fprintf(output, " SI BND %s %s\n", + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); + else + fprintf(output, " SC BND %s %s\n", + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); + } + else + fprintf(output, " UP BND %s %s\n", + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); + } + if(lp->orig_lowbo[i] != 0) { + a = lp->orig_lowbo[i]; + a = unscaled_value(lp, a, i); + if(putheader) { + fprintf(output, "BOUNDS\n"); + putheader = FALSE; + } + if(lp->orig_lowbo[i] != -lp->infinite) + fprintf(output, " LO BND %s %s\n", + MPSname(get_col_name(lp, j)), + formatnumber12((double)a)); + else + fprintf(output, " MI BND %s\n", + MPSname(get_col_name(lp, j))); + } + } + } + + /* Write optional SOS section */ + putheader = TRUE; + for(i = 0; i < SOS_count(lp); i++) { + SOSgroup *SOS = lp->SOS; + + if(putheader) { + fprintf(output, "SOS\n"); + putheader = FALSE; + } + fprintf(output, " S%1d SOS %s %s\n", + SOS->sos_list[i]->type, + MPSname(SOS->sos_list[i]->name), + formatnumber12((double) SOS->sos_list[i]->priority)); + for(j = 1; j <= SOS->sos_list[i]->size; j++) { + fprintf(output, " SOS %s %s\n", + MPSname(get_col_name(lp, SOS->sos_list[i]->members[j])), + formatnumber12((double) SOS->sos_list[i]->weights[j])); + } + } + + fprintf(output, "ENDATA\n"); + + lp->names_used = names_used; + + if(filename != NULL) + fclose(output); + return(ok); +} + +MYBOOL MPS_writehandle(lprec *lp, int typeMPS, FILE *output) +{ + set_outputstream(lp, output); + return(MPS_writefile(lp, typeMPS, NULL)); +} Index: src/tools/solver/lp_solve/lp_MPS.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_MPS.h diff -N src/tools/solver/lp_solve/lp_MPS.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_MPS.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,28 @@ +#ifndef HEADER_lp_MPS +#define HEADER_lp_MPS + +#include "lp_types.h" + +/* For MPS file reading and writing */ + +#define MPSFIXED 1 +#define MPSFREE 2 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Read an MPS file */ +lprec *MPS_readfile(char *filename, int typeMPS, int verbose); +lprec *MPS_readhandle(FILE *filename, int typeMPS, int verbose); + +/* Write a MPS file to output */ +MYBOOL MPS_writefile(lprec *lp, int typeMPS, char *filename); +MYBOOL MPS_writehandle(lprec *lp, int typeMPS, FILE *output); + + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_MPS */ Index: src/tools/solver/lp_solve/lp_SOS.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_SOS.c diff -N src/tools/solver/lp_solve/lp_SOS.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_SOS.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1215 @@ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_SOS.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* + Specially Ordered Set (SOS) routines - w/interface for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h + + Release notes: + v1.0 1 September 2003 Complete package for SOS creation and use in a LP + setting. Notable feature of this implementation + compared to those in other commercial systems is + the generalization to SOS'es of "unlimited" order. + v1.1 8 December 2003 Added variable (index) deletion method. + + ---------------------------------------------------------------------------------- +*/ + +/* SOS group functions */ +STATIC SOSgroup *create_SOSgroup(lprec *lp) +{ + SOSgroup *group; + + group = (SOSgroup *) calloc(1, sizeof(*group)); + group->lp = lp; + group->sos_alloc = SOS_START_SIZE; + group->sos_list = (SOSrec **) malloc((group->sos_alloc) * sizeof(*group->sos_list)); + return(group); +} + +STATIC void resize_SOSgroup(SOSgroup *group) +{ + if(group->sos_count == group->sos_alloc) { + group->sos_alloc = (int)((double) group->sos_alloc*RESIZEFACTOR); + group->sos_list = (SOSrec **) realloc(group->sos_list, + (group->sos_alloc) * sizeof(*group->sos_list)); + } +} + +STATIC int append_SOSgroup(SOSgroup *group, SOSrec *SOS) +{ + int i, k; + SOSrec *SOSHold; + + /* Check if we should resize */ + resize_SOSgroup(group); + + /* First append to the end of the list */ + group->sos_list[group->sos_count] = SOS; + group->sos_count++; + k = group->sos_count; + SOS->tagorder = k; + + /* Sort the SOS list by given priority */ + for(i = group->sos_count-1; i > 0; i--) { + if(group->sos_list[i]->priority < group->sos_list[i-1]->priority) { + SOSHold = group->sos_list[i]; + group->sos_list[i] = group->sos_list[i-1]; + group->sos_list[i-1] = SOSHold; + if(SOSHold == SOS) + k = i-1; + } + else + break; + } + /* Return the list index of the new SOS */ + return( k ); +} + + +STATIC int clean_SOSgroup(SOSgroup *group) +{ + int i, n; + SOSrec *SOS; + + if(group == NULL) + return( 0 ); + + /* Delete any SOS without members */ + n = 0; + if(group->sos_alloc > 0) { + for(i = group->sos_count; i > 0; i--) { + SOS = group->sos_list[i-1]; + if(SOS->members[0] == 0) { + delete_SOSrec(group, i); + n++; + } + } + } + return( n ); +} + + +STATIC void free_SOSgroup(SOSgroup **group) +{ + int i; + + if((group == NULL) || (*group == NULL)) + return; + if((*group)->sos_alloc > 0) { + for(i = 0; i < (*group)->sos_count; i++) + free_SOSrec((*group)->sos_list[i]); + FREE((*group)->sos_list); + } + FREE(*group); +} + +/* SOS record functions */ +STATIC SOSrec *create_SOSrec(SOSgroup *group, char *name, int type, int priority, int size, int *variables, REAL *weights) +{ + SOSrec *SOS; + + SOS = (SOSrec *) calloc(1 , sizeof(*SOS)); + SOS->parent = group; + SOS->type = type; + if(name == NULL) + SOS->name = NULL; + else + { + allocCHAR(group->lp, &SOS->name, (int) (strlen(name)+1), FALSE); + strcpy(SOS->name, name); + } + if(type < 0) + type = abs(type); + SOS->tagorder = 0; + SOS->size = 0; + SOS->priority = priority; + SOS->members = NULL; + SOS->weights = NULL; + SOS->membersSorted = NULL; + SOS->membersMapped = NULL; + + if(size > 0) + size = append_SOSrec(SOS, size, variables, weights); + + return(SOS); +} + + +STATIC int append_SOSrec(SOSrec *SOS, int size, int *variables, REAL *weights) +{ + int i, oldsize, newsize, nn; + lprec *lp = SOS->parent->lp; + + oldsize = SOS->size; + newsize = oldsize + size; + nn = abs(SOS->type); + + /* Shift existing active data right (normally zero) */ + if(SOS->members == NULL) + allocINT(lp, &SOS->members, 1+newsize+1+nn, TRUE); + else { + allocINT(lp, &SOS->members, 1+newsize+1+nn, AUTOMATIC); + for(i = newsize+1+nn; i > newsize+1; i--) + SOS->members[i] = SOS->members[i-size]; + } + SOS->members[0] = newsize; + SOS->members[newsize+1] = nn; + + /* Copy the new data into the arrays */ + if(SOS->weights == NULL) + allocREAL(lp, &SOS->weights, 1+newsize, TRUE); + else + allocREAL(lp, &SOS->weights, 1+newsize, AUTOMATIC); + for(i = oldsize+1; i <= newsize; i++) { + SOS->members[i] = variables[i-oldsize-1]; + if((SOS->members[i] < 1) || (SOS->members[i] > lp->columns)) + report(lp, IMPORTANT, "append_SOS_rec: Invalid SOS variable definition index %d\n", SOS->members[i]); + else { + if(SOS->isGUB) + lp->must_be_int[SOS->members[i]] |= ISGUB; + else + lp->must_be_int[SOS->members[i]] |= ISSOS; + } + if(weights == NULL) + SOS->weights[i] = i; /* Follow standard, which is sorted ascending */ + else + SOS->weights[i] = weights[i-oldsize-1]; + SOS->weights[0] += SOS->weights[i]; + } + + /* Sort the new paired lists ascending by weight (simple bubble sort) */ + i = sortByREAL(SOS->members, SOS->weights, newsize, 1, TRUE); + if(i > 0) + report(lp, CRITICAL, "Invalid SOS variable weight at index %d\n", i); + + /* Define mapping arrays to search large SOS's faster */ + allocINT(lp, &SOS->membersSorted, newsize, AUTOMATIC); + allocINT(lp, &SOS->membersMapped, newsize, AUTOMATIC); + for(i = oldsize+1; i <= newsize; i++) { + SOS->membersSorted[i - 1] = SOS->members[i]; + SOS->membersMapped[i - 1] = i; + } + sortByINT(SOS->membersMapped, SOS->membersSorted, newsize, 0, TRUE); + + /* Confirm the new size */ + SOS->size = newsize; + + return(newsize); + +} + + +STATIC int make_SOSchain(lprec *lp, MYBOOL forceresort) +{ + int i, j, k, n; + REAL *order, sum, weight; + SOSgroup *group = lp->SOS; + + /* Resort individual SOS member lists, if specified */ + if(forceresort) + SOS_sort_members(group, 0); + + /* Tally SOS variables and create master SOS variable list */ + n = 0; + for(i = 0; i < group->sos_count; i++) + n += group->sos_list[i]->size; + lp->sos_vars = n; + if(lp->sos_vars > 0) /* Prevent memory loss in case of multiple solves */ + FREE(lp->sos_priority); + allocINT(lp, &lp->sos_priority, n, FALSE); + allocREAL(lp, &order, n, FALSE); + + /* Move variable data to the master SOS list and sort */ + n = 0; + sum = 0; + for(i = 0; i < group->sos_count; i++) { + for(j = 1; j <= group->sos_list[i]->size; j++) { + lp->sos_priority[n] = group->sos_list[i]->members[j]; + weight = group->sos_list[i]->weights[j]; + sum += weight; + order[n] = sum; + n++; + } + } + i = sortByREAL(lp->sos_priority, order, n, 0, FALSE); + + /* Remove duplicate SOS variables */ + for(i = 0; i < n; i++) { + /* Scan forward to look for duplicate variables */ + for(j = i+1; j < n; j++) { + if(lp->sos_priority[i] == lp->sos_priority[j]) { + /* Duplicate found, shrink the tail end of the list */ + for(k = j+1; k < n; k++) + lp->sos_priority[k-1] = lp->sos_priority[k]; + n--; + } + } + } + + /* Adjust the size of the master variable list, if necessary */ + if(n < lp->sos_vars) { + allocINT(lp, &lp->sos_priority, n, AUTOMATIC); + lp->sos_vars = n; + } + + free(order); + return(n); + +} + + +STATIC MYBOOL delete_SOSrec(SOSgroup *group, int sosindex) +{ +#ifdef Paranoia + if((sosindex <= 0) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "delete_SOSrec: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + /* Delete and free the SOS record */ + free_SOSrec(group->sos_list[sosindex-1]); + while(sosindex < group->sos_count) { + group->sos_list[sosindex-1] = group->sos_list[sosindex]; + sosindex++; + } + group->sos_count--; + return(TRUE); +} + + +STATIC void free_SOSrec(SOSrec *SOS) +{ + if(SOS->name != NULL) + free(SOS->name); + if(SOS->size > 0) { + free(SOS->members); + free(SOS->weights); + free(SOS->membersSorted); + free(SOS->membersMapped); + } + free(SOS); +} + + +STATIC MYBOOL SOS_sort_members(SOSgroup *group, int sosindex) +/* Routine to (re-)sort SOS member arrays for faster access to large SOSes */ +{ + int i, n; + int *list; + lprec *lp = group->lp; + SOSrec *SOS; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_sort_members: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + if(!SOS_sort_members(group, i)) + return(FALSE); + } + } + else { + SOS = group->sos_list[sosindex-1]; + list = SOS->members; + n = list[0]; + /* Make sure that the arrays are properly allocated and sized */ + if(n != group->sos_list[sosindex-1]->size) { + allocINT(lp, &SOS->membersSorted, n, AUTOMATIC); + allocINT(lp, &SOS->membersMapped, n, AUTOMATIC); + group->sos_list[sosindex-1]->size = n; + } + /* Reload the arrays and do the sorting */ + for(i = 1; i <= n; i++) { + SOS->membersSorted[i - 1] = list[i]; + SOS->membersMapped[i - 1] = i; + } + sortByINT(SOS->membersMapped, SOS->membersSorted, n, 0, TRUE); + } + return( TRUE ); +} + + +STATIC MYBOOL SOS_shift_col(SOSgroup *group, int sosindex, int column, int delta, MYBOOL forceresort) +/* Routine to adjust SOS indeces for variable insertions or deletions; + Note: SOS_shift_col must be called before make_SOSchain! */ +{ + int i, ii, n, nn, nr; + int changed; + int *list; + REAL *weights; + +#ifdef Paranoia + lprec *lp = group->lp; + + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_shift_col: Invalid SOS index %d\n", sosindex); + return(FALSE); + } + else if((column < 1) || (delta == 0)) { + report(lp, IMPORTANT, "SOS_shift_col: Invalid column %d specified with delta %d\n", + column, delta); + return(FALSE); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + if(!SOS_shift_col(group, i, column, delta, forceresort)) + return(FALSE); + } + } + else { + list = group->sos_list[sosindex-1]->members; + weights = group->sos_list[sosindex-1]->weights; + n = list[0]; + nn = list[n+1]; + + /* Case where variable indeces are to be incremented */ + if(delta > 0) { + for(i = 1; i <= n; i++) { + if(list[i] >= column) + list[i] += delta; + } + } + /* Case where variables are to be deleted/indeces decremented */ + else { + changed = 0; + for(i = 1, ii = 0; i <= n; i++) { + nr = list[i]; + /* Check if this SOS variable should be deleted */ + if((nr >= column) && (nr < column-delta)) + continue; + /* If the index is "high" then decrement */ + if(nr > column) { + changed++; + nr += delta; + } + ii++; + list[ii] = nr; + weights[ii] = weights[i]; + } + /* Update the SOS length / type indicators */ + if(ii < n) { + list[0] = ii; + list[ii+1] = nn; + } + + /* Update mapping arrays to search large SOS's faster */ + if(forceresort && ((ii < n) || (changed > 0))) + SOS_sort_members(group, sosindex); + } + + } + return(TRUE); + +} + + +int SOS_get_type(SOSgroup *group, int sosindex) +{ +#ifdef Paranoia + if((sosindex < 1) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "SOS_get_type: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + return(group->sos_list[sosindex-1]->type); +} + + +int SOS_infeasible(SOSgroup *group, int sosindex) +{ + int i, n, nn, failindex; + int *list; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_infeasible: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(sosindex == 0 && group->sos_count == 1) + sosindex = 1; + + failindex = 0; + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + failindex = SOS_infeasible(group, i); + if(failindex > 0) break; + } + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]; + nn = list[n+1]; + /* Find index of next lower-bounded variable */ + for(i = 1; i <= n; i++) { + if(lp->orig_lowbo[lp->rows + abs(list[i])] > 0) + break; + } + + /* Find if there is another lower-bounded variable beyond the type window */ + i = i+nn; + while(i <= n) { + if(lp->orig_lowbo[lp->rows + abs(list[i])] > 0) + break; + i++; + } + if(i <= n) + failindex = abs(list[i]); + } + return(failindex); +} + + +int SOS_member_index(SOSgroup *group, int sosindex, int member) +{ + int n; + SOSrec *SOS; + + SOS = group->sos_list[sosindex-1]; + n = SOS->members[0]; + + n = searchFor(member, SOS->membersSorted, n, 0, FALSE); + if(n >= 0) + n = SOS->membersMapped[n]; + + return(n); +} + + +int SOS_is_member(SOSgroup *group, int sosindex, int column) +{ + int i, n = FALSE, *list; + lprec *lp; + + if(group == NULL) + return( FALSE ); + lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_member: Invalid SOS index %d\n", sosindex); + return(n); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + if(lp->must_be_int[column] & (ISSOS | ISGUB)) { + for(i = 1; i <= group->sos_count; i++) { + n = SOS_is_member(group, i, column); + if(n) + break; + } + } + } + else if(lp->must_be_int[column] & (ISSOS | ISGUB)) { + + /* Search for the variable */ + i = SOS_member_index(group, sosindex, column); + + /* Signal active status if found, otherwise return FALSE */ + if(i > 0) { + list = group->sos_list[sosindex-1]->members; + if(list[i] < 0) + n = -TRUE; + else + n = TRUE; + } + } + return(n); +} + + +MYBOOL SOS_is_member_of_type(SOSgroup *group, int column, int sostype) +{ + int i; + + for(i = 1; i <= group->sos_count; i++) { + if((SOS_get_type(group, i) == sostype) && SOS_is_member(group, i, column)) + return(TRUE); + } + return(FALSE); +} + + +MYBOOL SOS_set_GUB(SOSgroup *group, int sosindex, MYBOOL state) +{ + int i; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "SOS_set_GUB: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) + SOS_set_GUB(group, i, state); + } + else + group->sos_list[sosindex-1]->isGUB = state; + return(TRUE); +} + + +MYBOOL SOS_is_GUB(SOSgroup *group, int sosindex) +{ + int i; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(group->lp, IMPORTANT, "SOS_is_GUB: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + if(SOS_is_GUB(group, i)) + return(TRUE); + } + return(FALSE); + } + else + return( group->sos_list[sosindex-1]->isGUB ); +} + + +MYBOOL SOS_is_marked(SOSgroup *group, int sosindex, int column) +{ + int i, n, *list; + lprec *lp; + + if(group == NULL) + return( FALSE ); + lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_marked: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->must_be_int[column] & (ISSOS | ISGUB))) + return(FALSE); + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + n = SOS_is_marked(group, i, column); + if(n) + return(TRUE); + } + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]; + + /* Search for the variable (normally always faster to do linear search here) */ + column = -column; + for(i = 1; i <= n; i++) + if(list[i] == column) + return(TRUE); + } + return(FALSE); +} + + +MYBOOL SOS_is_active(SOSgroup *group, int sosindex, int column) +{ + int i, n, nn, *list; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_active: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->must_be_int[column] & (ISSOS | ISGUB))) + return(FALSE); + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + n = SOS_is_active(group, i, column); + if(n) + return(TRUE); + } + } + else { + + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Scan the active (non-zero) SOS index list */ + for(i = 1; (i <= nn) && (list[n+i] != 0); i++) + if(list[n+i] == column) return(TRUE); + } + return(FALSE); +} + + +MYBOOL SOS_is_full(SOSgroup *group, int sosindex, int column, MYBOOL activeonly) +{ + int i, nn, n, *list; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_full: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->must_be_int[column] & (ISSOS | ISGUB))) + return(FALSE); + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + if(SOS_is_full(group, i, column, activeonly)) + return(TRUE); + } + } + else if(SOS_is_member(group, sosindex, column)) { + + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Info: Last item in the active list is non-zero if the current SOS is full */ + if(list[n+nn] != 0) + return(TRUE); + + if(!activeonly) { + /* Spool to last active item */ + for(i = nn-1; (i > 0) && (list[n+i] == 0); i--); + if(i > 0) { + nn = nn - i; + i = SOS_member_index(group, sosindex, list[n+i]); + for(; (nn > 0) && (list[i] < 0); i++, nn--); + if(nn == 0) + return(TRUE); + } + } + } + + return(FALSE); +} + + +MYBOOL SOS_can_activate(SOSgroup *group, int sosindex, int column) +{ + int i, n, nn, *list; + lprec *lp; + + if(group == NULL) + return( FALSE ); + lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_can_activate: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->must_be_int[column] & (ISSOS | ISGUB))) + return(FALSE); + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + nn = SOS_can_activate(group, i, column); + if(nn == FALSE) + return(FALSE); + } + } + else if(SOS_is_member(group, sosindex, column)) { + + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Accept if the SOS is empty */ + if(list[n+1] == 0) + return(TRUE); + + /* Cannot activate a variable if the SOS is full */ + if(list[n+nn] != 0) + return(FALSE); + + /* Check if we can set variable active in SOS2..SOSn + (must check left and right neighbours if one variable is already active) */ + if(nn > 1) { + + /* Find the variable that was last activated; + Also check that the candidate variable is not already active */ + for(i = 1; i <= nn; i++) { + if(list[n+i] == 0) + break; + if(list[n+i] == column) + return(FALSE); + } + i--; + nn = list[n+i]; + + /* SOS accepts an additional variable; confirm neighbourness of candidate; + Search for the SOS set index of the last activated variable */ + n = list[0]; + for(i = 1; i <= n; i++) + if(abs(list[i]) == nn) break; + if(i > n) { + report(lp, CRITICAL, "SOS_can_activate: Internal index error at SOS %d\n", sosindex); + return(FALSE); + } + + /* SOS accepts an additional variable; confirm neighbourness of candidate */ + + /* Check left neighbour */ + if((i > 1) && (list[i-1] == column)) + return(TRUE); + /* Check right neighbour */ + if((i < n) && (list[i+1] == column)) + return(TRUE); + + /* It is not the right neighbour; return false */ + return(FALSE); + } + } + return(TRUE); +} + + +MYBOOL SOS_set_marked(SOSgroup *group, int sosindex, int column, MYBOOL asactive) +{ + int i, n, nn, *list; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_set_marked: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->must_be_int[column] & (ISSOS | ISGUB))) + return(FALSE); + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + /* Define an IBM-"SOS3" member variable temporarily as integer, if it is + not already a permanent integer; is reset in SOS_unmark */ + if(asactive && !is_int(lp, column) && SOS_is_member_of_type(group, column, SOS3)) { + lp->must_be_int[column] |= ISSOSTEMPINT; + lp_solve_set_int(lp, column, TRUE); + } + + if(sosindex == 0) { + nn = 0; + for(i = 1; i <= group->sos_count; i++) + if(SOS_set_marked(group, i, column, asactive)) + nn++; + return((MYBOOL) (nn == group->sos_count)); + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Search for the variable */ + i = SOS_member_index(group, sosindex, column); + + /* First mark active in the set member list as used */ + if((i > 0) && (list[i] > 0)) + list[i] *= -1; + else + return(TRUE); + + /* Then move the variable to the live list */ + if(asactive) { + for(i = 1; i <= nn; i++) { + if(list[n+i] == column) + return(FALSE); + else if(list[n+i] == 0) { + list[n+i] = column; + return(FALSE); + } + } + } + return(TRUE); + } +} + + +MYBOOL SOS_unmark(SOSgroup *group, int sosindex, int column) +{ + int i, n, nn, *list; + MYBOOL isactive; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_unmark: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if(!(lp->must_be_int[column] & (ISSOS | ISGUB))) + return(FALSE); + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + /* Undefine a SOS3 member variable that has temporarily been set as integer */ + if(lp->must_be_int[column] & ISSOSTEMPINT) { + lp->must_be_int[column] &= !ISSOSTEMPINT; + lp_solve_set_int(lp, column, FALSE); + } + + if(sosindex == 0) { + nn = 0; + for(i = 1; i <= group->sos_count; i++) + if(SOS_unmark(group, i, column)) + nn++; + return((MYBOOL) (nn == group->sos_count)); + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Search for the variable */ + i = SOS_member_index(group, sosindex, column); + + /* Restore sign in main list */ + if((i > 0) && (list[i] < 0)) + list[i] *= -1; + else + return(TRUE); + + /* Find the variable in the active list... */ + isactive = SOS_is_active(group, sosindex, column); + if(isactive) { + for(i = 1; i <= nn; i++) + if(list[n+i] == column) + break; + /* ...shrink the list if found, otherwise return error */ + if(i <= nn) { + for(; ilp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_fix_unmarked: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + count = 0; + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + if(SOS_is_member(group, i, variable)) + count += SOS_fix_unmarked(group, variable, i, bound, value, isupper, diffcount); + } + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + + /* Count the number of active and free SOS variables */ + nn = list[n]; + for(i = 1; i <= nn; i++) { + if(list[n+i] == 0) + break; + } + i--; + i = nn - i; /* Establish the number of unused slots */ + + /* Determine the free SOS variable window */ + if(i == nn) { + nLeft = 0; + nRight = SOS_member_index(group, sosindex, variable); + } + else { + nLeft = SOS_member_index(group, sosindex, list[n+1]); + if(variable == list[n+1]) + nRight = nLeft; + else + nRight = SOS_member_index(group, sosindex, variable); + } + + nRight += i; /* Loop (nRight+1)..n */ + + /* Fix variables outside of the free SOS variable window */ + for(i = 1; i < n; i++) { + /* Skip the SOS variable window */ + if((i >= nLeft) && (i <= nRight)) + continue; + /* Otherwise proceed to set bound */ + ii = list[i]; + if(ii > 0) { + ii += lp->rows; + if(bound[ii] != value) { + /* Verify that we don't violate original bounds */ + if(isupper && (value < lp->orig_lowbo[ii])) + return(-ii); + else if(!isupper && (value > lp->orig_upbo[ii])) + return(-ii); + /* OK, set the new bound */ + count++; + bound[ii] = value; + } + if((diffcount != NULL) && (lp->solution[ii] != value)) + (*diffcount)++; + } + } + } + return(count); +} + + +int SOS_fix_GUB(SOSgroup *group, int variable, int sosindex, REAL *bound, MYBOOL isleft) +{ + int i, ii, count, n, nn, nLeft, *list; + REAL value = 0; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_fix_GUB: Invalid SOS index %d\n", sosindex); + return(FALSE); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + count = 0; + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + if(SOS_is_member(group, i, variable)) + count += SOS_fix_GUB(group, variable, i, bound, isleft); + } + } + else { + list = group->sos_list[sosindex-1]->members; + n = list[0]; + + /* Count the number of unmarked SOS variables */ + nn = 0; + for(i = 1; i <= n; i++) { + ii = list[i]; + if((ii > 0) && (bound[lp->rows+ii] > value)) + nn++; + } + if(nn == 0) + return(count); + + /* Establish the number of unmarked variables in the left window + (note that "variable" should have been marked previously) */ + nLeft = nn / 2; + + /* Fix variables in the specified variable window */ + nn = 0; + for(i = 1; (i <= n); i++) { + + /* Find next unmarked variable */ + ii = list[i]; + if((ii < 0) || (bound[lp->rows+ii] == value)) + continue; + nn++; + + /* Check if we are in the appropriate window */ + if((isleft && (nn <= nLeft)) || + (!isleft && (nn > nLeft))) { + + /* Proceed to set 0-bound */ + ii += lp->rows; + /* Verify that we don't violate original bounds */ + if(value < lp->orig_lowbo[ii]) + return(-ii); + /* OK, set the new bound */ + count++; + bound[ii] = value; + } + } + } + return(count); +} + +int SOS_is_satisfied(SOSgroup *group, int sosindex, REAL *solution) +/* Determine if the SOS is satisfied for the current solution vector; + The return code is in the range [-2..+2], depending on the type of + satisfaction. Positive return value means too many non-zero values, + negative value means set incomplete: + + -2: Set member count not full (SOS3) + -1: Set member count not full + 0: Set is full (also returned if the SOS index is invalid) + 1: Too many non-zero sequential variables + 2: Set consistency error + +*/ +{ + int i, n, nn, count, *list; + int type, status = 0; + lprec *lp = group->lp; + +#ifdef Paranoia + if((sosindex < 0) || (sosindex > group->sos_count)) { + report(lp, IMPORTANT, "SOS_is_satisfied: Invalid SOS index %d\n", sosindex); + return( 0 ); + } +#endif + + if((sosindex == 0) && (group->sos_count == 1)) + sosindex = 1; + + if(sosindex == 0) { + for(i = 1; i <= group->sos_count; i++) { + status = SOS_is_satisfied(group, i, solution); + if((status != 0) && (status != -1)) + break; + } + } + else { + type = SOS_get_type(group, sosindex); + list = group->sos_list[sosindex-1]->members; + n = list[0]+1; + nn = list[n]; + + /* Count the number of active SOS variables */ + for(i = 1; i <= nn; i++) { + if(list[n+i] == 0) + break; + } + count = i-1; + if(count == nn) + status = 0; /* Set is full */ + else + status = -1; /* Set is partial */ + + /* Find index of the first active variable; fail if some are non-zero */ + if(count > 0) { + nn = list[n+1]; + for(i = 1; i < n; i++) { + if(abs(list[i]) == nn || (solution[lp->rows + abs(list[i])] != 0)) + break; + } + if(abs(list[i]) != nn) + status = 2; /* Set consistency error (leading set variables are non-zero) */ + else { + /* Scan the active SOS variables; fail if some are zero */ + while(count > 0) { + if(solution[lp->rows + abs(list[i])] == 0) + break; + i++; + count--; + } + if(count > 0) + status = 2; /* Set consistency error (active set variables are zero) */ + } + } + else { + i = 1; + /* There are no active variables; see if we have happened to find a valid header */ + while((i < n) && (solution[lp->rows + abs(list[i])] == 0)) + i++; + count = 0; + while((i < n) && (count <= nn) && (solution[lp->rows + abs(list[i])] != 0)) { + count++; + i++; + } + if(count > nn) + status = 1; /* Too-many sequential non-zero variables */ + } + + /* Scan the trailing set of SOS variables; fail if some are non-zero */ + if(status <= 0) { + n--; + while(i <= n) { + if(solution[lp->rows + abs(list[i])] != 0) + break; + i++; + } + if(i <= n) + status = 1; /* Too-many sequential non-zero variables */ + + /* Code member deficiency for SOS3 separately */ + else if((status == -1) && (type <= SOS3)) + status = -2; + } + + } + return(status); +} Index: src/tools/solver/lp_solve/lp_SOS.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_SOS.h diff -N src/tools/solver/lp_solve/lp_SOS.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_SOS.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,84 @@ +#ifndef HEADER_lp_SOS +#define HEADER_lp_SOS + +/* Specially Ordered Sets (SOS) prototypes and settings */ +/* ------------------------------------------------------------------------- */ + +#include "lp_types.h" + + +/* SOS constraint defines */ +/* ------------------------------------------------------------------------- */ +#define SOS1 1 +#define SOS2 2 +#define SOS3 -1 +#define SOS_START_SIZE 10 /* Start size of SOS_list array; realloced if needed */ + +typedef struct _SOSgroup SOSgroup; + +typedef struct _SOSrec +{ + SOSgroup *parent; + int tagorder; + char *name; + int type; + MYBOOL isGUB; + int size; + int priority; + int *members; + REAL *weights; + int *membersSorted; + int *membersMapped; +} SOSrec; + +/* typedef */ struct _SOSgroup +{ + lprec *lp; /* Pointer to owner */ + SOSrec **sos_list; /* Array of pointers to SOS lists */ + int sos_alloc; /* Size allocated to specially ordered sets (SOS1, SOS2...) */ + int sos_count; /* Number of specially ordered sets (SOS1, SOS2...) */ +} /* SOSgroup */; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* SOS storage structure */ +STATIC SOSgroup *create_SOSgroup(lprec *lp); +STATIC void resize_SOSgroup(SOSgroup *group); +STATIC int append_SOSgroup(SOSgroup *group, SOSrec *SOS); +STATIC int clean_SOSgroup(SOSgroup *group); +STATIC void free_SOSgroup(SOSgroup **group); +STATIC SOSrec *create_SOSrec(SOSgroup *group, char *name, int type, int priority, int size, int *variables, REAL *weights); +STATIC MYBOOL delete_SOSrec(SOSgroup *group, int sosindex); +STATIC int append_SOSrec(SOSrec *SOS, int size, int *variables, REAL *weights); +STATIC void free_SOSrec(SOSrec *SOS); + +/* SOS utilities */ +STATIC int make_SOSchain(lprec *lp, MYBOOL forceresort); +STATIC MYBOOL SOS_sort_members(SOSgroup *group, int sosindex); +STATIC MYBOOL SOS_shift_col(SOSgroup *group, int sosindex, int column, int delta, MYBOOL forceresort); +int SOS_get_type(SOSgroup *group, int sosindex); +int SOS_infeasible(SOSgroup *group, int sosindex); +int SOS_member_index(SOSgroup *group, int sosindex, int member); +int SOS_is_member(SOSgroup *group, int sosindex, int column); +MYBOOL SOS_is_member_of_type(SOSgroup *group, int column, int sostype); +MYBOOL SOS_set_GUB(SOSgroup *group, int sosindex, MYBOOL state); +MYBOOL SOS_is_GUB(SOSgroup *group, int sosindex); +MYBOOL SOS_is_marked(SOSgroup *group, int sosindex, int column); +MYBOOL SOS_is_active(SOSgroup *group, int sosindex, int column); +MYBOOL SOS_is_full(SOSgroup *group, int sosindex, int column, MYBOOL activeonly); +MYBOOL SOS_can_activate(SOSgroup *group, int sosindex, int column); +MYBOOL SOS_set_marked(SOSgroup *group, int sosindex, int column, MYBOOL asactive); +MYBOOL SOS_unmark(SOSgroup *group, int sosindex, int column); +int SOS_fix_unmarked(SOSgroup *group, int variable, int sosindex, REAL *bound, REAL value, + MYBOOL isupper, int *diffcount); +int SOS_fix_GUB(SOSgroup *group, int variable, int sosindex, REAL *bound, MYBOOL isleft); +int SOS_is_satisfied(SOSgroup *group, int sosindex, REAL *solution); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_SOS */ Index: src/tools/solver/lp_solve/lp_crash.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_crash.c diff -N src/tools/solver/lp_solve/lp_crash.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_crash.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,201 @@ + +/* + ---------------------------------------------------------------------------------- + Crash management routines in lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h, lp_utils.h, lp_matrix.h + + Release notes: + v1.0.0 1 April 2004 First version. + + ---------------------------------------------------------------------------------- +*/ + +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_utils.h" +#include "lp_matrix.h" +#include "lp_crash.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +MYBOOL crash_basis(lprec *lp) +{ + int i; + MATrec *mat = lp->matA; + + /* Initialize basis indicators */ + if(!lp->basis_valid) + default_basis(lp); + else + lp->var_basic[0] = FALSE; + for(i = 0; i <= lp->sum; i++) + lp->is_basic[i] = FALSE; + for(i = 1; i <= lp->rows; i++) + lp->is_basic[lp->var_basic[i]] = TRUE; + + if((lp->crashmode == CRASH_MOSTFEASIBLE) && mat_validate(mat)) { + /* The logic here follows Maros */ + LLrec *rowLL = NULL, *colLL = NULL; + MATitem *matelem; + int ii, rx, cx, ix; + REAL wx, tx, *rowMAX = NULL, *colMAX = NULL; + int *rowNZ = NULL, *colNZ = NULL, *rowWT = NULL, *colWT = NULL; + + report(lp, NORMAL, "crash_basis: Basis crashing selected\n"); + + /* Tally row and column non-zero counts */ + allocINT(lp, &rowNZ, lp->rows+1, TRUE); + allocINT(lp, &colNZ, lp->columns+1, TRUE); + allocREAL(lp, &rowMAX, lp->rows+1, FALSE); + allocREAL(lp, &colMAX, lp->columns+1, FALSE); + for(i = 0, matelem = mat->col_mat; i < mat_nonzeros(mat); i++, matelem++) { + rx = (*matelem).row_nr; + cx = (*matelem).col_nr; + wx = fabs((*matelem).value); + rowNZ[rx]++; + colNZ[cx]++; + if(i == 0) { + rowMAX[rx] = wx; + colMAX[cx] = wx; + colMAX[0] = wx; + } + else { + rowMAX[rx] = my_max(rowMAX[rx], wx); + colMAX[cx] = my_max(colMAX[cx], wx); + colMAX[0] = my_max(colMAX[0], wx); + } + } + /* Reduce counts for small magnitude to preserve stability */ + for(i = 0, matelem = mat->col_mat; i < mat_nonzeros(mat); i++, matelem++) { + rx = (*matelem).row_nr; + cx = (*matelem).col_nr; + wx = fabs((*matelem).value); +#ifdef CRASH_SIMPLESCALE + if(wx < CRASH_THRESHOLD * colMAX[0]) { + rowNZ[rx]--; + colNZ[cx]--; + } +#else + if(wx < CRASH_THRESHOLD * rowMAX[rx]) + rowNZ[rx]--; + if(wx < CRASH_THRESHOLD * colMAX[cx]) + colNZ[cx]--; +#endif + } + + /* Set up priority tables */ + allocINT(lp, &rowWT, lp->rows+1, TRUE); + createLink(lp->rows, &rowLL, NULL); + for(i = 1; i <= lp->rows; i++) { + if(get_constr_type(lp, i)==EQ) + ii = 3; + else if(lp->upbo[i] < lp->infinite) + ii = 2; + else if(fabs(lp->rhs[i]) < lp->infinite) + ii = 1; + else + ii = 0; + rowWT[i] = ii; + if(ii > 0) + appendLink(rowLL, i); + } + allocINT(lp, &colWT, lp->columns+1, TRUE); + createLink(lp->columns, &colLL, NULL); + for(i = 1; i <= lp->columns; i++) { + ix = lp->rows+i; + if(is_free(lp, i)) + ii = 3; + else if(lp->upbo[ix] >= lp->infinite) + ii = 2; + else if(fabs(lp->upbo[ix]-lp->lowbo[ix]) > lp->epsmachine) + ii = 1; + else + ii = 0; + colWT[i] = ii; + if(ii > 0) + appendLink(colLL, i); + } + + /* Loop over all basis variables */ + for(i = 1; i <= lp->rows; i++) { + + /* Select row */ + rx = 0; + wx = -lp->infinite; + for(ii = firstActiveLink(rowLL); ii > 0; ii = nextActiveLink(rowLL, ii)) { + tx = rowWT[ii] - CRASH_SPACER*rowNZ[ii]; + if(tx > wx) { + rx = ii; + wx = tx; + } + } + if(rx == 0) + break; + removeLink(rowLL, rx); + + /* Select column */ + cx = 0; + wx = -lp->infinite; + for(ii = mat->row_end[rx-1]; ii < mat->row_end[rx]; ii++) { + + /* Update NZ column counts for row selected above */ + tx = fabs(ROW_MAT_VALUE(mat->row_mat[ii])); + ix = ROW_MAT_COL(mat->row_mat[ii]); +#ifdef CRASH_SIMPLESCALE + if(tx >= CRASH_THRESHOLD * colMAX[0]) +#else + if(tx >= CRASH_THRESHOLD * colMAX[ix]) +#endif + colNZ[ix]--; + if(!isActiveLink(colLL, ix) || (tx < CRASH_THRESHOLD * rowMAX[rx])) + continue; + + /* Now do the test for best pivot */ + tx = my_sign(get_OF_raw(lp, lp->rows+ix)) - my_sign(ROW_MAT_VALUE(mat->row_mat[ii])); + tx = colWT[ix] + CRASH_WEIGHT*tx - CRASH_SPACER*colNZ[ix]; + if(tx > wx) { + cx = ix; + wx = tx; + } + } + if(cx == 0) + break; + removeLink(colLL, cx); + + /* Update row NZ counts */ + for(ii = mat->col_end[cx-1]; ii < mat->col_end[cx]; ii++) { + wx = fabs(mat->col_mat[ii].value); + ix = mat->col_mat[ii].row_nr; +#ifdef CRASH_SIMPLESCALE + if(wx >= CRASH_THRESHOLD * colMAX[0]) +#else + if(wx >= CRASH_THRESHOLD * rowMAX[ix]) +#endif + rowNZ[ix]--; + } + + /* Set new basis variable */ + setBasisVar(lp, rx, lp->rows+cx); + } + + /* Clean up */ + FREE(rowNZ); + FREE(colNZ); + FREE(rowMAX); + FREE(colMAX); + FREE(rowWT); + FREE(colWT); + freeLink(&rowLL); + freeLink(&colLL); + } + return( TRUE ); +} + Index: src/tools/solver/lp_solve/lp_crash.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_crash.h diff -N src/tools/solver/lp_solve/lp_crash.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_crash.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,25 @@ + +#ifndef HEADER_lp_crash +#define HEADER_lp_crash + +#include "lp_types.h" + +#define CRASH_SIMPLESCALE /* Specify if we should use a simple absolute scaling threshold */ + +#define CRASH_THRESHOLD 0.167 +#define CRASH_SPACER 10 +#define CRASH_WEIGHT 0.500 + + + +#ifdef __cplusplus +__EXTERN_C { +#endif + +STATIC MYBOOL crash_basis(lprec *lp); + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_lp_crash */ Index: src/tools/solver/lp_solve/lp_etaPFI.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_etaPFI.c diff -N src/tools/solver/lp_solve/lp_etaPFI.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_etaPFI.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1050 @@ + +/* + Modularized simplex inverse modules - w/interface for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Michel Berkelaar (to lp_solve v3.2), + Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_etaPFI.h, lp_lib.h, lp_colamdMDO.h + + Release notes: + v1.0 1 September 2003 Implementation of the original lp_solve product form + of the inverse using a sparse eta matrix format with + a significant zero'th index containing the objective. + The new implementation includes optimal column + ordering at reinversion using the colamd library. + For lp_solve B-inverse details confer the text at the + beginning of lp_lib.cpp. + v2.0 1 April 2004 Repackaged and streamlined for both internal and + external usage, using the new inverse/factorization + interface library. + v2.0.1 23 May 2004 Moved mustrefact() function into the BFP structure. + + ---------------------------------------------------------------------------------- +*/ + +#include +#include + +#include "lp_lib.h" +#include "commonlib.h" +#include "lp_etaPFI.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* Include routines common to inverse implementations; unfortunately, + etaPFI requires the optional shared routines to be unique. */ +#include "lp_BFP1.c" + + +/* MUST MODIFY */ +char * BFP_CALLMODEL bfp_name(void) +{ +#if INVERSE_ACTIVE == INVERSE_LEGACY + return( "etaPFI v1.0" ); +#else + return( "etaPFI v2.0" ); +#endif +} + +/* MUST MODIFY */ +MYBOOL BFP_CALLMODEL bfp_init(lprec *lp, int size, int delta) +{ + INVrec *eta; + + eta = (INVrec *) calloc(1, sizeof(*lp->invB)); + lp->invB = eta; + + bfp_resize(lp, ETA_START_SIZE); + if(delta <= 0) + delta = bfp_pivotmax(lp); + bfp_pivotalloc(lp, size+delta); + + bfp_restart(lp); + bfp_preparefactorization(lp); + eta->extraD = lp->infinite; + eta->num_refact = 0; + + eta->statistic1 = 0; + eta->statistic2 = delta; + + return(TRUE); +} + + +MYBOOL BFP_CALLMODEL bfp_restart(lprec *lp) +{ + INVrec *eta; + + eta = lp->invB; + if(eta == NULL) + return( FALSE ); + + eta->status = BFP_STATUS_SUCCESS; + eta->max_Bsize = 0; + eta->max_colcount = 0; + eta->max_etasize = 0; + eta->num_refact = 0; + eta->pcol = NULL; +/* eta->set_Bidentity = FALSE; */ + eta->extraD = lp->infinite; + eta->num_pivots = 0; + + eta->last_colcount = 0; + eta->statistic1 = 0; + eta->statistic2 = bfp_pivotmax(lp); + + return( TRUE ); +} + +void BFP_CALLMODEL bfp_pivotalloc(lprec *lp, int newsize) +{ + INVrec *eta; + MYBOOL isfirst; + + eta = lp->invB; + isfirst = (MYBOOL) (eta->eta_col_end == NULL); + newsize += 1; + allocINT(lp, &eta->eta_col_end, newsize, AUTOMATIC); + allocINT(lp, &eta->eta_col_nr, newsize, AUTOMATIC); + if(isfirst) + eta->eta_col_end[0] = 0; +} + + +void BFP_CALLMODEL bfp_resize(lprec *lp, int newsize) +{ + INVrec *eta; + + eta = lp->invB; + if(eta->eta_matalloc == 0) + eta->eta_matalloc = newsize; + else { + while(eta->eta_matalloc <= newsize) + eta->eta_matalloc += eta->eta_matalloc / RESIZEFACTOR; + } + allocREAL(lp, &eta->eta_value, eta->eta_matalloc + 1, AUTOMATIC); + allocINT(lp, &eta->eta_row_nr, eta->eta_matalloc + 1, AUTOMATIC); +} + + +void BFP_CALLMODEL bfp_free(lprec *lp) +{ + INVrec *eta; + + eta = lp->invB; + if(eta == NULL) + return; + + FREE(eta->eta_value); + FREE(eta->eta_row_nr); + FREE(eta->eta_col_end); + FREE(eta->eta_col_nr); + FREE(eta); + lp->invB = NULL; +} + + +/* MUST MODIFY */ +MYBOOL BFP_CALLMODEL bfp_canresetbasis(lprec *lp) +{ + return( TRUE ); +} + +int BFP_CALLMODEL bfp_preparefactorization(lprec *lp) +{ + INVrec *eta; + + eta = lp->invB; + + /* Finish any outstanding business */ + if(eta->is_dirty == AUTOMATIC) + bfp_finishfactorization(lp); + + /* Reset additional indicators */ + eta->force_refact = FALSE; + eta->num_refact++; + eta->eta_colcount = 0; + + /* Signal that we are reinverting */ + eta->is_dirty = AUTOMATIC; + + /* Set time of start of current refactorization cycle */ + eta->time_refactstart = timeNow(); + eta->time_refactnext = 0; + + return( 0 ); +} + + +void BFP_CALLMODEL bfp_finishfactorization(lprec *lp) +{ + INVrec *eta; + + eta = lp->invB; + + /* Collect and optionally report statistics */ + if((lp->bb_totalnodes <= 1) && (lp->verbose & MSG_PERFORMANCE)) { + REAL hold; + int stepsize = 5; + + /* Report size of eta */ + hold = bfp_efficiency(lp); + if((hold >= 10) && (hold >= stepsize*(1 + (int) eta->statistic1 / stepsize))) + lp->report(lp, NORMAL, "Reduced speed with inverse density at %.1fx basis matrix.\n", + hold); + eta->statistic1 = my_max(eta->statistic1, hold); + /* Report numeric stability */ + hold = (REAL) (lp->total_iter-lp->total_bswap)/(bfp_refactcount(lp)+1); + if((hold <= 30) && (hold <= stepsize*(1 + (int) eta->statistic2 / stepsize))) + lp->report(lp, NORMAL, "Reduced numeric accuracy with %.1f pivots/reinverse.\n", + hold); + eta->statistic2 = my_min(eta->statistic2, hold); + } + eta->last_colcount = bfp_colcount(lp); + eta->max_colcount = my_max(eta->max_colcount, eta->last_colcount); + eta->max_etasize = my_max(eta->max_etasize, bfp_nonzeros(lp, FALSE)); + + /* Signal that we done reinverting */ + lp->invB->is_dirty = FALSE; + lp->justInverted = TRUE; + lp->doInvert = FALSE; + + /* Store information about the current inverse */ + eta->extraD = lp->Extrad; + eta->num_pivots = 0; + +} + + +int BFP_CALLMODEL bfp_colcount(lprec *lp) +{ + return(lp->invB->eta_colcount); +} + + +int BFP_CALLMODEL bfp_nonzeros(lprec *lp, MYBOOL maximum) +{ + if(maximum == TRUE) + return(lp->invB->max_etasize); + else if(maximum == AUTOMATIC) + return(lp->invB->max_Bsize); + else + return(lp->invB->eta_col_end[lp->invB->eta_colcount]); +} + + +int BFP_CALLMODEL bfp_memallocated(lprec *lp) +{ + return(lp->invB->eta_matalloc); +} + + +int bfp_ETApreparepivot(lprec *lp, int row_nr, int col_nr, REAL *pcol, MYBOOL *frow) +/* Find optimal pivot row and do any column preprocessing before + being added to the eta file -- Only used in bfp_refactorize() */ +{ + int i, rowalt, refnr; + REAL hold, test; + +#if 0 + fsolve(lp, col_nr, pcol, NULL, lp->epsmachine, 1.0, TRUE); +#else + i = lp->get_lpcolumn(lp, col_nr, pcol, NULL, NULL); + bfp_ftran_prepare(lp, pcol, NULL); +#endif + + if(frow == NULL) { + if(fabs(pcol[row_nr]) < lp->epsmachine) + return( -1 ); + else + return( row_nr ); + } + + /* Find largest coefficient row available to be pivoted */ + refnr = row_nr; + row_nr = lp->rows + 1; + rowalt = 0; + hold = -lp->infinite; + /* Loop over rows that currently contain slacks; + (i.e. the positions that are not already taken by another pivot) */ +#ifdef UseMarkowitzStatistic + i = 0; + while((i = nextActiveLink((LLrec *) frow, i)) != 0) { +#else + for(i = 1; i <= lp->rows; i++) + if(frow[i] == FALSE) { +#endif + /* Get largest absolute value */ + test = fabs(pcol[i]); + if(test > hold) { + hold = test; + row_nr = i; +#ifdef LegacyEtaPivotChoice + if(hold > lp->epspivot) + break; +#endif + } + if((rowalt == 0) && (test == 1)) /* Unit row */ + rowalt = i; + } + + if((row_nr > lp->rows) || (hold < lp->epsmachine)) + row_nr = -1; + + return( row_nr ); + +} + +int bfp_ETApivotrow(lprec *lp, int datacolumn) +/* Get pivot row for a particular data column */ +{ + INVrec *eta; + int n = -1; + + eta = lp->invB; + if(datacolumn <= eta->last_colcount) { + n = eta->eta_col_end[datacolumn] - 1; + n = eta->eta_row_nr[n]; + if(n < 1) { + lp->report(lp, CRITICAL, "bfp_ETApivotrow: Invalid pivot row identified"); + n = -1; + } + } + return( n ); +} + + +void bfp_ETAupdateCounts(lprec *lp, MYBOOL *usedpos, int rownr, int colnr) +{ + usedpos[rownr] = AUTOMATIC; + usedpos[colnr] = AUTOMATIC; +} +void bfp_ETAreduceCounts(lprec *lp, MYBOOL *usedpos, + int *rownum, int *colnum, int rownr, int colnr, MYBOOL defer) +{ + int i, j, k; + MATrec *mat = lp->matA; + + /* Reduce row NZ counts */ + if(rownum != NULL) { + for(j = mat->col_end[colnr - 1]; j < mat->col_end[colnr]; j++) { + i = mat->col_mat[j].row_nr; + if(usedpos[i] == FALSE) + rownum[i]--; + } + rownum[rownr] = -1; + } + usedpos[rownr] = AUTOMATIC+defer; /* To distinguish it from basic slacks! */ + + /* Reduce column NZ counts */ + if(colnum != NULL) { + k = mat->row_end[rownr]; + for(j = mat->row_end[rownr - 1]; j < k; j++) { + i = ROW_MAT_COL(mat->row_mat[j]); + if(usedpos[lp->rows + i] == TRUE) + colnum[i]--; + } + colnum[colnr] = -1; + } + usedpos[lp->rows+colnr] = AUTOMATIC+defer; + +} + +void bfp_ETAsimpleiteration(lprec *lp, int row_nr, int col_nr, REAL *pcol) +/* Called from bfp_refactorize() with two cases; + pcol == NULL : A singleton row or column that allows easy elimination, + pcol != NULL : A dense row/column combination presolved with ftran */ +{ + bfp_prepareupdate(lp, row_nr, col_nr, pcol); + lp->set_basisvar(lp, row_nr, col_nr); + bfp_finishupdate(lp, FALSE); +} /* bfp_ETAsimpleiteration */ + + +int BFP_CALLMODEL bfp_factorize(lprec *lp, int uservars, int Bsize, MYBOOL *usedpos) +{ + REAL *pcol, hold; + int *colnum, *rownum, *col, *row; + int *mdo = NULL; +#ifdef UseMarkowitzStatistic + REAL absval, testval; + LLrec *nextrow; + int ii, jj; +#endif + int k, kk, i, j, numit, rownr, colnr; + int singularities = 0; + MATitem *matentry; + MATrec *mat = lp->matA; + + /* Check if there is anyting to do */ + lp->invB->max_Bsize = my_max(lp->invB->max_Bsize, Bsize+(1+lp->rows-uservars)); + if(uservars == 0) + return(singularities); + + /* Allocate other necessary working arrays */ + allocINT(lp, &col, lp->rows + 1, TRUE); + allocINT(lp, &row, lp->rows + 1, TRUE); + allocREAL(lp, &pcol, lp->rows + 1, TRUE); + allocINT(lp, &rownum, lp->rows + 1, TRUE); + allocINT(lp, &colnum, lp->columns + 1, TRUE); + + /* Get (net/un-eliminated) row and column entry counts for basic user variables */ +#ifdef ExcludeCountOrderOF + usedpos[0] = TRUE; +#else + usedpos[0] = FALSE; +#endif + for(j = 1; j <= lp->columns; j++) { + + /* If it is a basic user variable...*/ + if(usedpos[lp->rows+j]) { + i = mat->col_end[j - 1]; + kk = mat->col_end[j]; + + /* Count relevant non-zero values */ + for(matentry = mat->col_mat + i, numit = 0; i < kk; i++, matentry++, numit++) { + k = (*matentry).row_nr; + + /* Check for objective row phase 1 adjustments; + increment count only if necessary following modifyOF1() test */ + if((numit == 0) && !usedpos[0]) { + if(k == 0) + hold = (*matentry).value; + else + hold = 0; + if(!lp->modifyOF1(lp, lp->rows+j, &hold, 1.0)) { + if(k == 0) + continue; + } + else if(k > 0) { + numit++; + colnum[j]++; + rownum[0]++; + } + } + + /* Exclude pre-eliminated rows due to basic slacks; + this is a characteristic of the eta-model that presumes an + initial basis with all slacks; i.e. an identity matrix */ + if(!usedpos[k]) { + numit++; + colnum[j]++; + rownum[k]++; + } + } + } + } + + /* Initialize counters */ + numit = 0; + k = 0; + +#ifdef UseMarkowitzStatistic + /* Create a linked list for the available pivot rows */ + createLink(lp->rows, &nextrow, usedpos); +#endif + + /* Loop over constraint rows, hunting for ROW singletons */ +#ifdef ReprocessSingletons +Restart: +#endif + kk = 0; + for(rownr = 1; rownr <= lp->rows; rownr++) { + + /* Only process if the corresponding slack is non-basic (basis slot is available) */ + if((usedpos[rownr] == FALSE) && (rownum[rownr] == 1)) { + + /* Find first basic user column available to be pivoted */ + j = mat->row_end[rownr - 1]; + i = mat->row_end[rownr]; + while((j < i) && (usedpos[lp->rows + ROW_MAT_COL(mat->row_mat[j])] != TRUE)) + j++; +#ifdef Paranoia + if(j >= i) + lp->report(lp, SEVERE, "bfp_factorize: No column to pivot IN due to an internal error\n"); +#endif + colnr = ROW_MAT_COL(mat->row_mat[j]); + + /* Reduce item counts for the selected pivot column/row */ +#ifdef UseMarkowitzStatistic + removeLink(nextrow, rownr); +#endif + bfp_ETAreduceCounts(lp, usedpos, rownum, colnum, rownr, colnr, TRUE); + + /* Perform the pivot */ + bfp_ETAsimpleiteration(lp, rownr, lp->rows+colnr, NULL); + k++; + kk++; + } + } + + /* Loop over columns, hunting for COLUMN singletons; + (only store row and column indexes for pivoting in at the end of refactorization) */ + if(k < lp->rows) + for(colnr = 1; (k < lp->rows) && (colnr <= lp->columns); colnr++) { + + /* Only accept basic user columns not already pivoted in */ + if((usedpos[lp->rows + colnr] == TRUE) && (colnum[colnr] == 1)) { + + /* Find first available basis column to be pivoted out */ + j = mat->col_end[colnr - 1]; + i = mat->col_end[colnr]; + for(matentry = mat->col_mat + j; (j < i) && (usedpos[(*matentry).row_nr] != FALSE); + j++, matentry++); +#ifdef Paranoia + if(j >= i) + lp->report(lp, SEVERE, "bfp_factorize: No column to pivot OUT due to an internal error\n"); +#endif + rownr = (*matentry).row_nr; + + /* Reduce item counts for the selected pivot column/row */ +#ifdef UseMarkowitzStatistic + removeLink(nextrow, rownr); +#endif + bfp_ETAreduceCounts(lp, usedpos, rownum, colnum, rownr, colnr, FALSE); + + /* Store pivot information and update counters */ + col[numit] = colnr; + row[numit] = rownr; + numit++; + k++; + kk++; + } + } + + /* Check timeout and user abort again */ + if(lp->userabort(lp, -1)) + goto Cleanup; + if(k >= lp->rows) + goto Process; + + /* Reprocess the singleton elimination loop until supply exhausted */ +#ifdef ReprocessSingletons + if(kk > 0) + goto Restart; +#endif + + /* Determine the number of remaining pivots and fill a minimum degree ordering column index array */ + k = lp->rows - k; + mdo = bfp_createMDO(lp, usedpos, k, +#ifdef UseLegacyOrdering + FALSE); +#else + TRUE); +#endif + if(mdo == NULL) + goto Cleanup; + else if(mdo[0] == 0) + goto Process; + kk = mdo[0]; + + /* Loop over all unprocessed basic user columns, finding an appropriate + unused basis column position to pivot the user column into */ + for(i = 1; i <= kk; i++) { + + /* Get the entering variable */ + colnr = mdo[i]; + + /* Solve for and eliminate the entering column / variable */ +#ifdef UseMarkowitzStatistic + rownr = bfp_ETApreparepivot(lp, -i, colnr, pcol, (MYBOOL*) nextrow); +#else + rownr = bfp_ETApreparepivot(lp, -i, colnr, pcol, usedpos); +#endif + if(rownr < 0) { + /* This column is singular; Just let it leave the basis, making one of the + slack variables basic in its place. (Source: Geosteiner changes!) */ + if(lp->spx_trace) + lp->report(lp, DETAILED, "bfp_factorize: Skipped singular column %d\n", colnr); + singularities++; + continue; + } + else { +#ifdef UseMarkowitzStatistic + /* Do a simple "local" Markowitz-based pivot selection; + this generally reduces eta NZ count, but does not always give + a speed improvement due to weaker numerics and added overhead + (the numeric pivot selection tolerance limit for application + of the Markowitz metric is 0.1, which is a typical value) */ + k = rownr; + absval = fabs(pcol[rownr]); + hold = 0.1*absval; +/* for(j = 1; j <= lp->rows; j++) if(!usedpos[j]) { */ + j = 0; + while((j = nextActiveLink(nextrow, j)) != 0) { + + /* Skip previously selected positions and focus row */ + if(j == rownr) + continue; + /* Pick a pivot row that is shorter than the previous best */ + jj = rownum[j]; + ii = rownum[k]; + if((jj > 0) && (jj <= ii)) { + /* Make sure that we preserve numeric accuracy */ + testval = fabs(pcol[j]); + if(((jj == ii) && (testval > absval)) || + ((jj < ii) && (testval > hold))) { + k = j; + absval = testval; + } + } + } + rownr = k; + bfp_ETAsimpleiteration(lp, rownr, colnr, pcol); + } + + /* Reduce item counts for the selected pivot column/row */ + if(rownr > 0) + removeLink(nextrow, rownr); + bfp_ETAreduceCounts(lp, usedpos, rownum, NULL, rownr, colnr-lp->rows, FALSE); + +#else + bfp_ETAsimpleiteration(lp, rownr, colnr, pcol); + } + + /* Update occupancy states for pivot column/row */ + bfp_ETAupdateCounts(lp, usedpos, rownr, colnr); +#endif + + /* Check timeout and user abort again */ + if(lp->userabort(lp, -1)) + goto Cleanup; + } + + /* Perform pivoting of the singleton columns stored above */ +Process: + for(i = numit - 1; i >= 0; i--) { + colnr = col[i]; + rownr = row[i]; + bfp_ETAsimpleiteration(lp, rownr, lp->rows+colnr, NULL); + } + + /* Finally, wrap up the refactorization */ +Cleanup: + FREE(mdo); + if(pcol != NULL) { + FREE(pcol); + FREE(col); + FREE(row); + FREE(rownum); + FREE(colnum); + } +#ifdef UseMarkowitzStatistic + freeLink(&nextrow); +#endif + + lp->invB->num_singular += singularities; + return(singularities); +} + + +LREAL BFP_CALLMODEL bfp_prepareupdate(lprec *lp, int row_nr, int col_nr, REAL *pcol) +/* Was condensecol() in versions of lp_solve before 4.0.1.8 - KE */ +{ + int i, j, k, colusr, elnr, min_size; + LREAL pivValue; + INVrec *eta; + MATrec *mat = lp->matA; + + eta = lp->invB; + eta->pcol = pcol; + + elnr = eta->eta_col_end[eta->eta_colcount]; + pivValue = 0; + +#ifdef Paranoia + if(row_nr < 1 || col_nr < 1) + lp->report(lp, CRITICAL, "bfp_prepareupdate: Invalid row/column combination specified"); +#endif + + min_size = elnr + lp->rows + 2; + if(min_size >= eta->eta_matalloc) /* maximum local growth of Eta */ + bfp_resize(lp, min_size); + + /* Fill the eta-colum from the A matrix */ + if(pcol == NULL) { + k = 0; + colusr = col_nr - lp->rows; + i = mat->col_mat[mat->col_end[colusr - 1]].row_nr; + + /* Handle phase 1 objective function adjustments */ + pivValue = 0; + if((i > 0) && lp->modifyOF1(lp, col_nr, &pivValue, 1.0)) { + eta->eta_row_nr[elnr] = 0; + eta->eta_value[elnr] = pivValue; + elnr++; + } + + for(j = mat->col_end[colusr - 1]; j < mat->col_end[colusr]; j++) { + + /* The pivot row is stored at the end of the column; store its index */ + i = mat->col_mat[j].row_nr; + if(i == row_nr) { + k = j; + continue; + } + + /* Append other non-zero column values */ + pivValue = mat->col_mat[j].value; + + /* Handle non-zero phase 1 objective function values */ + if((i == 0) && !lp->modifyOF1(lp, col_nr, &pivValue, 1.0)) + continue; + + /* Do some additional pivot accuracy management */ +#if 0 + if(my_abs(my_abs(pivValue)-1) < lp->epsmachine) pivValue = my_sign(pivValue, 1); +#endif + eta->eta_row_nr[elnr] = i; + eta->eta_value[elnr] = pivValue; + elnr++; + } + eta->eta_row_nr[elnr] = mat->col_mat[k].row_nr; + pivValue = mat->col_mat[k].value; + eta->eta_value[elnr] = pivValue; + elnr++; + } + + /* Fill the eta-colum from a dense, ftran-preprocessed column + where the data has been retrieved with obtain_column(). + Note that phase 1 objective function adjustments are done in + obtain_column/expand_column, called in fsolve() */ + else { + + for(i = 0; i <= lp->rows; i++) { + + pivValue = pcol[i]; + + if((i != row_nr) && (pivValue != 0)) { + eta->eta_row_nr[elnr] = i; + eta->eta_value[elnr] = pivValue; + elnr++; + } + } + eta->eta_row_nr[elnr] = row_nr; + pivValue = pcol[row_nr]; + eta->eta_value[elnr] = pivValue; + elnr++; + } + + eta->eta_col_nr[eta->eta_colcount] = col_nr; + eta->eta_col_end[eta->eta_colcount + 1] = elnr; + eta->last_colcount = my_max(eta->last_colcount, eta->eta_colcount+1); + + /* Set completion status; but hold if we are reinverting */ + if(eta->is_dirty != AUTOMATIC) + lp->invB->is_dirty = TRUE; + + return(pivValue); +} + + +REAL BFP_CALLMODEL bfp_pivotRHS(lprec *lp, LREAL theta, REAL *pcol) +/* Was rhsmincol(), ie. "rhs minus column" in versions of lp_solve before 4.0.1.8 - KE */ +{ + int i; + LREAL f = 0; + + /* Round net RHS values (should not be smaller than the factor used in recompute_solution) */ + REAL roundzero = lp->epsmachine; + + if(pcol == NULL) { + int j, k; + INVrec *eta; + + eta = lp->invB; + j = eta->eta_col_end[eta->eta_colcount]; + k = eta->eta_col_end[eta->eta_colcount + 1]; + { +#if 1 + int *nv; + REAL *vv; + for(i = j, nv = eta->eta_row_nr+j, vv = eta->eta_value+j; + i < k; i++, nv++, vv++) { + f = lp->rhs[*nv] - theta * (*vv); + my_roundzero(f, roundzero); + lp->rhs[*nv] = f; + } +#else + int varbas; + for(i = j; i < k; i++) { + varbas = eta->eta_row_nr[i]; + f = lp->rhs[varbas] - theta*eta->eta_value[i]; + my_roundzero(f, roundzero); + lp->rhs[varbas] = f; + } +#endif + } + f = eta->eta_value[k - 1]; + } + else { + LREAL *rhs; + + for(i = 0, rhs = lp->rhs; i<= lp->rows; i++, rhs++, pcol++) { + if(*pcol == 0) + continue; + *rhs -= theta * (*pcol); + my_roundzero((*rhs), roundzero); + } + f = 0; + } + + return( f ); + +} + + +MYBOOL BFP_CALLMODEL bfp_finishupdate(lprec *lp, MYBOOL changesign) +/* Was addetacol() in versions of lp_solve before 4.0.1.8 - KE */ +{ + int i, j, k; + REAL theta, *value; + INVrec *eta; + + eta = lp->invB; + + /* Check if a data column has been loaded by bfp_putcolumn */ + if(!eta->is_dirty) + return( FALSE ); + + /* Do fast eta transformation (formally "u") */ + j = eta->eta_col_end[eta->eta_colcount]; + eta->eta_colcount++; + k = eta->eta_col_end[eta->eta_colcount] - 1; + + /* Handle following cases: 1) changesign == FALSE, theta == -1 -> Drop straight through + 2) == FALSE != -1 + 3) == TRUE == 1 -> Sign change only + 4) == TRUE != 1 */ +#if 1 + /* Amended old style */ + if(changesign) + for(i = j, value = eta->eta_value+j; i <= k; i++, value++) + *value = -(*value); + + value = eta->eta_value+k; + theta = 1.0 / (*value); + *value = theta; + if(fabs(theta+1) > EPS_ETAMACHINE) { + theta = -theta; + while(k > j) { + k--; + value--; + *value *= theta; + } + } + +#else + /* New streamlined style (change sign loop eliminated) -- not entirely debugged */ + value = eta->eta_value+k; + theta = my_chsign(changesign, *value); + if(fabs(theta+1) > EPS_ETAMACHINE) { + theta = 1.0 / theta; + *value = theta; + theta = my_chsign(changesign, -theta); + while(k > j) { + k--; + value--; + *value *= theta; + } + } + else if(fabs(theta-1) <= EPS_ETAMACHINE) { + if(changesign) { + k++; + value++; + } + while(k > j) { + k--; + value--; + *value = -(*value); + } + } + +#endif + + eta->num_pivots++; + + /* Reset indicators; treat reinversion specially */ + if(eta->is_dirty != AUTOMATIC) { + eta->is_dirty = FALSE; + } + + lp->justInverted = FALSE; + return( TRUE ); + +} /* bfp_finishupdate */ + + +void BFP_CALLMODEL bfp_ftran_normal(lprec *lp, REAL *pcol, int *nzidx) +/* Note that ftran does not "expand" B indexes to the actual basis + column, and that both the input and output range is [0..rows] */ +{ + int i, j, k, r, *rowp; + LREAL theta, *vcol; + REAL *valuep; + INVrec *eta; + + eta = lp->invB; + + /* Initialize case where long doubles may be used */ + if(sizeof(LREAL) == sizeof(REAL)) + vcol = pcol; + else { + allocLREAL(lp, &vcol, lp->rows + 1, FALSE); + for(i = 0; i <= lp->rows; i++) + vcol[i] = pcol[i]; + } + + for(i = 1; i <= eta->eta_colcount; i++) { + k = eta->eta_col_end[i] - 1; + r = eta->eta_row_nr[k]; + theta = vcol[r]; + if(theta != 0) { + j = eta->eta_col_end[i - 1]; + + /* CPU intensive loop, let's do pointer arithmetic */ + for(rowp = eta->eta_row_nr + j, valuep = eta->eta_value + j; + j < k; j++, rowp++, valuep++) { + vcol[(*rowp)] += theta * (*valuep); + } + vcol[r] *= eta->eta_value[k]; + } + } + + /* Finalize case where long doubles may be used */ + if(sizeof(LREAL) != sizeof(REAL)) { + for(i = 0; i <= lp->rows; i++) + pcol[i] = vcol[i]; + FREE(vcol); + } + +} /* ftran */ + +void BFP_CALLMODEL bfp_ftran_prepare(lprec *lp, REAL *pcol, int *nzidx) +/* Does nothing particular in the etaPFI version of the inverse; + in other versions it is used to additionally store necessary data + to prepare for pivoting / update of the inverse */ +{ +#ifdef EtaFtranRoundRelative + int i; + REAL pmax, *x; +#endif + + bfp_ftran_normal(lp, pcol, nzidx); + + /* Help numeric accuracy in case we have not scaled to equilibration */ +#ifdef EtaFtranRoundRelative + pmax = 0; + for(i = 0, x = pcol; i <= lp->rows; i++, x++) + if(fabs(*x) > pmax) + pmax = fabs(*x); + if(pmax > 0) + for(i = 0, x = pcol; i <= lp->rows; i++, x++) + if(fabs(*x/pmax) < EPS_ETAPIVOT) + *x = 0; +#else + roundVector(pcol, lp->rows, EPS_ETAPIVOT); +#endif +} + + +void BFP_CALLMODEL bfp_btran_normal(lprec *lp, REAL *prow, int *nzidx) +/* Note that btran does not "expand" B indexes to the actual basis + column, and that both the input and output range is [0..rows] */ +{ + int i, jb, je, k, *rowp; + LREAL fmax; + REGISTER LREAL f; + REAL *valuep; + INVrec *eta = lp->invB; + + /* Prepare for maximum value (initialize to prevent division by zero */ + k = 0; + fmax = 0; + for(i = eta->eta_colcount; i >= 1; i--) { + f = 0; + jb = eta->eta_col_end[i-1]; + je = eta->eta_col_end[i] - 1; + for(rowp = eta->eta_row_nr + jb, valuep = eta->eta_value + jb; + jb <= je; jb++, rowp++, valuep++) { + f += prow[(*rowp)] * (*valuep); + } + fmax = my_max(fmax, fabs(f)); +#ifndef EtaBtranRoundRelative + my_roundzero(f, EPS_ETAPIVOT); +#endif + jb = eta->eta_row_nr[je]; + prow[jb] = f; + + } + + /* Help numeric accuracy in case we have not scaled to equilibration */ +#ifdef EtaBtranRoundRelative + if(fmax > 0) { + for(i = 0, valuep = prow; i <= lp->rows; i++, valuep++) + if(fabs(*valuep/fmax) < EPS_ETAPIVOT) + *valuep = 0; + } +#endif + +} /* btran */ + + +void BFP_CALLMODEL bfp_btran_double(lprec *lp, REAL *prow, int *pnzidx, REAL *drow, int *dnzidx) +{ + int i, j, k, *rowp; + LREAL dmax, fmax; + REGISTER LREAL d, f; + REAL *valuep; + INVrec *eta; + + eta = lp->invB; + + fmax = 0; + dmax = 0; + for(i = eta->eta_colcount; i >= 1; i--) { + d = 0; + f = 0; + k = eta->eta_col_end[i] - 1; + j = eta->eta_col_end[i - 1]; + + /* This is one of the loops where the program consumes a lot of CPU time; + let's help the compiler by doing some pointer arithmetic instead of array indexing */ + for(rowp = eta->eta_row_nr + j, valuep = eta->eta_value + j; + j <= k; j++, rowp++, valuep++) { + f += prow[(*rowp)] * (*valuep); + d += drow[(*rowp)] * (*valuep); + } + fmax = my_max(fmax, fabs(f)); + dmax = my_max(dmax, fabs(d)); + + j = eta->eta_row_nr[k]; + +#ifndef EtaBtranRoundRelative + my_roundzero(f, EPS_ETAPIVOT); + my_roundzero(d, EPS_ETAPIVOT); +#endif + prow[j] = f; + drow[j] = d; + } + + /* Help numeric accuracy in case we have not scaled to equilibration */ +#ifdef EtaBtranRoundRelative + if((fmax > 0) && (dmax > 0)) { + REAL *valued; + for(i = 0, valuep = prow, valued = drow; i <= lp->rows; i++, valuep++, valued++) { + if(fabs(*valuep/fmax) < EPS_ETAPIVOT) + *valuep = 0; + if(fabs(*valued/dmax) < EPS_ETAPIVOT) + *valued = 0; + } + } +#endif + +} Index: src/tools/solver/lp_solve/lp_etaPFI.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_etaPFI.h diff -N src/tools/solver/lp_solve/lp_etaPFI.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_etaPFI.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,72 @@ +#ifndef HEADER_lp_etaPFI +#define HEADER_lp_etaPFI + +#include "lp_types.h" + +/* Eta PFI option defines */ +#if INVERSE_ACTIVE == INVERSE_LEGACY + #define UseLegacyOrdering /* Use unoptimized column ordering like in v4 and before */ + #define LegacyEtaPivotChoice /* Do not look for largest pivot, only first satisfactory */ +#else + #define ReprocessSingletons /* Try to increase sparsity by iteratively looking for singletons */ + #define UseMarkowitzStatistic /* Try to minimize fill-in by the Markowitz statistic */ +#endif +#define ExcludeCountOrderOF /* This define typically gives sparser inverses */ + +#define EtaFtranRoundRelative /* Do FTRAN relative value rounding management */ +/*#define EtaBtranRoundRelative*/ /* Do BTRAN relative value rounding management */ + +/* Eta parameter defines */ +#define MATINDEXBASE 0 +#define INVDELTAROWS 0 /* Additional rows inserted at the top */ +#define ETA_START_SIZE 3*MAT_START_SIZE /* Start size of Eta-array; realloc'ed if needed; + typical scalars are in the 2-10 range with MDO */ +#define DEF_MAXPIVOT 42 /* Maximum number of pivots before reinversion; + best average performance in the 38-48 range */ +#define EPS_ETAMACHINE lp->epsmachine /* lp->epsvalue */ +#define EPS_ETAPIVOT lp->epsmachine + + +/* typedef */ struct _INVrec +{ + int status; /* Last operation status code */ + int eta_matalloc; /* The allocated memory for Eta non-zero values */ + int eta_colcount; /* The number of Eta columns */ + REAL *eta_value; /* eta_alloc: Structure containing values of Eta */ + int *eta_row_nr; /* " " : Structure containing row indexes of Eta */ + int *eta_col_nr; /* eta_columns: Structure containting column indexes of Eta */ + int *eta_col_end; /* rows_alloc + MaxNumInv : eta_col_end[i] is + the start index of the next Eta column */ + int max_Bsize; /* The largest B matrix of user variables */ + int max_colcount; /* The maximum number of user columns in eta */ + int last_colcount; /* The previous column size of the eta */ + int max_etasize; /* The largest eta-file generated */ + int num_refact; /* Number of times the basis was refactored */ + int num_pivots; /* Number of pivots since last refactorization */ + double time_refactstart; /* Time since start of last refactorization-pivots cyle */ + double time_refactnext; /* Time estimated to next refactorization */ + REAL *pcol; /* Vector pointer to the last base for the eta update */ + int num_singular; /* The total number of singular updates */ + REAL extraD; /* The dual objective function offset for the current inverse */ + REAL statistic1; + REAL statistic2; + MYBOOL is_dirty; /* Specifies if a column is incompletely processed */ + MYBOOL force_refact; /* Force refactorization at the next opportunity */ + MYBOOL set_Bidentity; /* Force B to be the identity matrix at the next refactorization */ +} /* INVrec */; + + +#ifdef __cplusplus +/*namespace etaPFI*/ +extern "C" { +#endif + +/* Put function headers here */ +#include "lp_BFP.h" + + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_etaPFI */ Index: src/tools/solver/lp_solve/lp_lib.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_lib.c diff -N src/tools/solver/lp_solve/lp_lib.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_lib.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,7841 @@ + +/* ---------------------------------------------------------------------------------- + Main library of routines for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Michel Berkelaar (to v3.2), + Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: (see below) + + Release notes: + v5.0.0 1 January 2004 First integrated and repackaged version. + v5.0.1 8 May 2004 Cumulative update since initial release; + overall functionality scope maintained. + + ---------------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------------------- */ +/* Main library of routines for lp_solve */ +/*----------------------------------------------------------------------------------- */ +#include +#include +/*#include */ +#include +#include +#include + +#if LoadInverseLib == TRUE + #ifdef WIN32 + #include + #else + #include + #endif +#endif + + +/* ---------------------------------------------------------------------------------- */ +/* Include core and support modules via headers */ +/* ---------------------------------------------------------------------------------- */ +#include "lp_lib.h" +#include "commonlib.h" +#include "lp_utils.h" +#include "lp_matrix.h" +#include "lp_SOS.h" +#include "lp_Hash.h" +#include "lp_MPS.h" +#include "lp_wlp.h" +#include "lp_presolve.h" +#include "lp_scale.h" +#include "lp_simplex.h" +#include "lp_mipbb.h" +#include "lp_report.h" + +#if INVERSE_ACTIVE==INVERSE_LUMOD + #include "lp_LUMOD.h" +#elif INVERSE_ACTIVE==INVERSE_LUSOL + #include "lp_LUSOL.h" +#elif INVERSE_ACTIVE==INVERSE_GLPKLU + #include "lp_glpkLU.h" +#elif INVERSE_ACTIVE==INVERSE_ETA + #include "lp_etaPFI.h" +#elif INVERSE_ACTIVE==INVERSE_LEGACY + #include "lp_etaPFI.h" +#endif + +#ifdef __BORLANDC__ + #pragma hdrstop + #pragma package(smart_init) +#endif + +/* ---------------------------------------------------------------------------------- */ +/* Include selected basis inverse routines and price norm scalars */ +/* ---------------------------------------------------------------------------------- */ +#ifdef UseLegacyOrdering +/* #include "lp_noMDO.cpp" */ +#else + #include "lp_MDO.h" +#endif + +#include "lp_price.h" +#include "lp_pricePSE.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* ---------------------------------------------------------------------------------- */ +/* Define some globals */ +/* ---------------------------------------------------------------------------------- */ +int callcount = 0; + +/* Return lp_solve version information */ +void __WINAPI lp_solve_version(int *majorversion, int *minorversion, int *release, int *build) +{ + if(majorversion != NULL) + (*majorversion) = MAJORVERSION; + if(minorversion != NULL) + (*minorversion) = MINORVERSION; + if(release != NULL) + (*release) = RELEASE; + if(build != NULL) + (*build) = BUILD; +} + + +/* ---------------------------------------------------------------------------------- */ +/* Various interaction elements */ +/* ---------------------------------------------------------------------------------- */ + +MYBOOL __WINAPI userabort(lprec *lp, int message) +{ + int spx_save; + MYBOOL abort; + spx_save = lp->spx_status; + lp->spx_status = RUNNING; + if(yieldformessages(lp) != 0) { + lp->spx_status = USERABORT; + if(lp->bb_level > 0) + lp->bb_break = TRUE; + } + if((message > 0) && (lp->usermessage != NULL) && (lp->msgmask & message)) + lp->usermessage(lp, lp->msghandle, message); + abort = (MYBOOL) (lp->spx_status != RUNNING); + if(!abort) + lp->spx_status = spx_save; + return( abort ); +} + +STATIC int yieldformessages(lprec *lp) +{ + double currenttime = timeNow(); + + if((lp->sectimeout > 0) && ((currenttime-lp->timestart)-(REAL)lp->sectimeout>0)) + lp->spx_status = TIMEOUT; + + if(lp->ctrlc != NULL) { + int retcode = lp->ctrlc(lp, lp->ctrlchandle); + /* Check for command to restart the B&B */ + if((retcode == ACTION_RESTART) && (lp->bb_level > 1)) { + lp->bb_break = AUTOMATIC; + retcode = 0; + } + return(retcode); + } + else + return(0); +} + +void __WINAPI set_outputstream(lprec *lp, FILE *stream) +{ + if((lp->outstream != NULL) && (lp->outstream != stdout)) + fclose(lp->outstream); + if(stream == NULL) { + lp->outstream = stdout; + } + else + lp->outstream = stream; +} + +MYBOOL __WINAPI set_outputfile(lprec *lp, char *filename) +{ + MYBOOL ok; + FILE *output = stdout; + + ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); + if(ok) + set_outputstream(lp, output); + return(ok); +} + +REAL __WINAPI time_elapsed(lprec *lp) +{ + if(lp->timeend > 0) + return(lp->timeend - lp->timestart); + else + return(timeNow() - lp->timestart); +} + +void __WINAPI put_abortfunc(lprec *lp, ctrlcfunc newctrlc, void *ctrlchandle) +{ + lp->ctrlc = newctrlc; + lp->ctrlchandle = ctrlchandle; +} +void __WINAPI put_logfunc(lprec *lp, logfunc newlog, void *loghandle) +{ + lp->writelog = newlog; + lp->loghandle = loghandle; +} +void __WINAPI put_msgfunc(lprec *lp, msgfunc newmsg, void *msghandle, int mask) +{ + lp->usermessage = newmsg; + lp->msgmask = mask; + lp->msghandle = msghandle; +} + + +/* ---------------------------------------------------------------------------------- */ +/* DLL exported function */ +/* ---------------------------------------------------------------------------------- */ +lprec * __WINAPI read_MPS(char *filename, int verbose) +{ + return(MPS_readfile(filename, MPSFIXED, verbose)); +} +lprec * __WINAPI read_mps(FILE *filename, int verbose) +{ + return(MPS_readhandle(filename, MPSFIXED, verbose)); +} +lprec * __WINAPI read_freeMPS(char *filename, int verbose) +{ + return(MPS_readfile(filename, MPSFREE, verbose)); +} +lprec * __WINAPI read_freemps(FILE *filename, int verbose) +{ + return(MPS_readhandle(filename, MPSFREE, verbose)); +} +MYBOOL __WINAPI write_mps(lprec *lp, char *filename) +{ + return(MPS_writefile(lp, MPSFIXED, filename)); +} +MYBOOL write_MPS(lprec *lp, FILE *output) +{ + return(MPS_writehandle(lp, MPSFIXED, output)); +} + +MYBOOL __WINAPI write_freemps(lprec *lp, char *filename) +{ + return(MPS_writefile(lp, MPSFREE, filename)); +} +MYBOOL write_freeMPS(lprec *lp, FILE *output) +{ + return(MPS_writehandle(lp, MPSFREE, output)); +} + +MYBOOL __WINAPI write_lp(lprec *lp, char *filename) +{ + return(LP_writefile(lp, filename)); +} +MYBOOL write_LP(lprec *lp, FILE *output) +{ + return(LP_writehandle(lp, output)); +} +#ifndef PARSER_LP +lprec* __WINAPI read_lp(FILE *filename, int verbose, char *lp_name) +{ + return(NULL); +} +lprec* __WINAPI read_LP(char *filename, int verbose, char *lp_name) +{ + return(NULL); +} +#endif + +#ifndef PARSER_CPLEX +lprec* __WINAPI read_LPT(char *filename, int verbose, char *lp_name) +{ + return(NULL); +} +lprec* __WINAPI read_lpt(FILE *filename, int verbose, char *lp_name) +{ + return(NULL); +} +MYBOOL __WINAPI write_lpt(lprec *lp, char *output) +{ + return(TRUE); +} +MYBOOL write_LPT(lprec *lp, FILE *output) +{ + return(TRUE); +} +#endif + +void __WINAPI unscale(lprec *lp) +{ + undoscale(lp); +} +int __WINAPI lp_solve_solve(lprec *lp) +{ + if(has_BFP(lp)) { + lp->solvecount++; + if(is_add_rowmode(lp)) + set_add_rowmode(lp, FALSE); + return(lin_solve(lp)); + } + else + return( NOBFP ); +} +void __WINAPI lp_solve_print_lp(lprec *lp) +{ + REPORT_lp(lp); +} +void __WINAPI print_tableau(lprec *lp) +{ + REPORT_tableau(lp); +} +void __WINAPI print_objective(lprec *lp) +{ + REPORT_objective(lp); +} +void __WINAPI print_solution(lprec *lp, int columns) +{ + REPORT_solution(lp, columns); +} +void __WINAPI print_constraints(lprec *lp, int columns) +{ + REPORT_constraints(lp, columns); +} +void __WINAPI print_duals(lprec *lp) +{ + REPORT_duals(lp); +} +void __WINAPI print_scales(lprec *lp) +{ + REPORT_scales(lp); +} +MYBOOL __WINAPI print_debugdump(lprec *lp, char *filename) +{ + return(REPORT_debugdump(lp, filename, (MYBOOL) (get_total_iter(lp) > 0))); +} +void __WINAPI print_str(lprec *lp, char *str) +{ + report(lp, lp->verbose, "%s", str); +} + + + +/* ---------------------------------------------------------------------------------- */ +/* Parameter setting and retrieval functions */ +/* ---------------------------------------------------------------------------------- */ + +void __WINAPI set_timeout(lprec *lp, long sectimeout) +{ + lp->sectimeout = sectimeout; +} + +long __WINAPI get_timeout(lprec *lp) +{ + return(lp->sectimeout); +} + +void __WINAPI set_verbose(lprec *lp, int verbose) +{ + lp->verbose = verbose; +} + +int __WINAPI get_verbose(lprec *lp) +{ + return(lp->verbose); +} + +void __WINAPI set_print_sol(lprec *lp, int print_sol) +{ + lp->print_sol = print_sol; +} + +int __WINAPI get_print_sol(lprec *lp) +{ + return(lp->print_sol); +} + +void __WINAPI set_debug(lprec *lp, MYBOOL debug) +{ + lp->bb_trace = debug; +} + +MYBOOL __WINAPI is_debug(lprec *lp) +{ + return(lp->bb_trace); +} + +void __WINAPI set_trace(lprec *lp, MYBOOL trace) +{ + lp->spx_trace = trace; +} + +MYBOOL __WINAPI is_trace(lprec *lp) +{ + return(lp->spx_trace); +} + +void __WINAPI set_anti_degen(lprec *lp, int anti_degen) +{ + lp->anti_degen = anti_degen; +} + +int __WINAPI get_anti_degen(lprec *lp) +{ + return(lp->anti_degen); +} + +MYBOOL __WINAPI is_anti_degen(lprec *lp, int testmask) +{ + return((MYBOOL) ((lp->anti_degen == testmask) || ((lp->anti_degen & testmask) != 0))); +} + +void __WINAPI set_presolve(lprec *lp, int do_presolve) +{ + lp->do_presolve = do_presolve; +} + +int __WINAPI get_presolve(lprec *lp) +{ + return(lp->do_presolve); +} + +MYBOOL __WINAPI is_presolve(lprec *lp, int testmask) +{ + return((MYBOOL) ((lp->do_presolve == testmask) || ((lp->do_presolve & testmask) != 0))); +} + +void __WINAPI set_maxpivot(lprec *lp, int maxpivot) +{ + lp->max_pivots = maxpivot; +} + +int __WINAPI get_maxpivot(lprec *lp) +{ + return( lp->bfp_pivotmax(lp) ); +} + +void __WINAPI set_bb_rule(lprec *lp, int bb_rule) +{ + lp->bb_rule = bb_rule; +} + +int __WINAPI get_bb_rule(lprec *lp) +{ + return(lp->bb_rule); +} + +MYBOOL is_bb_rule(lprec *lp, int bb_rule) +{ + return( (MYBOOL) ((lp->bb_rule & NODE_STRATEGYMASK) == bb_rule) ); +} + +MYBOOL is_bb_mode(lprec *lp, int bb_mask) +{ + return( (MYBOOL) ((lp->bb_rule & bb_mask) > 0) ); +} + +STATIC MYBOOL is_bb_action(lprec *lp, int testmask) +{ + return((MYBOOL) ((lp->bb_action & testmask) != 0)); +} + +void __WINAPI set_bb_depthlimit(lprec *lp, int bb_maxlevel) +{ + lp->bb_limitlevel = bb_maxlevel; +} + +int __WINAPI get_bb_depthlimit(lprec *lp) +{ + return(lp->bb_limitlevel); +} + +void __WINAPI set_obj_bound(lprec *lp, REAL bb_heuristicOF) +{ + lp->bb_heuristicOF = bb_heuristicOF; +} + +REAL __WINAPI get_obj_bound(lprec *lp) +{ + return(lp->bb_heuristicOF); +} + +void __WINAPI set_mip_gap(lprec *lp, MYBOOL absolute, REAL mip_gap) +{ + if(absolute) + lp->mip_absgap = mip_gap; + else + lp->mip_relgap = mip_gap; +} + +REAL __WINAPI get_mip_gap(lprec *lp, MYBOOL absolute) +{ + if(absolute) + return(lp->mip_absgap); + else + return(lp->mip_relgap); +} + +MYBOOL __WINAPI set_var_branch(lprec *lp, int column, int branch_mode) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "set_var_branch: Column %d out of range\n", column); + return( FALSE ); + } +#endif + + if(lp->bb_varbranch == NULL) { + int i; + if(branch_mode == BRANCH_DEFAULT) + return( TRUE ); + allocMYBOOL(lp, &lp->bb_varbranch, lp->columns_alloc, FALSE); + for(i = 0; i < lp->columns; i++) + lp->bb_varbranch[i] = BRANCH_DEFAULT; + } + lp->bb_varbranch[column-1] = (MYBOOL) branch_mode; + return( TRUE ); +} + +int __WINAPI get_var_branch(lprec *lp, int column) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "get_var_branch: Column %d out of range\n", column); + return(lp->bb_floorfirst); + } +#endif + + if(lp->bb_varbranch == NULL) + return(lp->bb_floorfirst); + if(lp->bb_varbranch[column-1] == BRANCH_DEFAULT) + return(lp->bb_floorfirst); + else + return(lp->bb_varbranch[column-1]); +} + +static void set_infiniteex(lprec *lp, REAL infinite, MYBOOL init) +{ + int i; + + infinite = fabs(infinite); + if((init) || (lp->bb_heuristicOF == lp->infinite)) + lp->bb_heuristicOF = infinite; + if((init) || (lp->break_at_value == -lp->infinite)) + lp->break_at_value = -infinite; + for(i = 0; i <= lp->sum; i++) { + if((!init) && (lp->orig_lowbo[i] == lp->infinite)) + lp->orig_lowbo[i] = infinite; + if((init) || (lp->orig_upbo[i] == lp->infinite)) + lp->orig_upbo[i] = infinite; + } + lp->obj_mincost = infinite; + lp->infinite = infinite; +} + + +void __WINAPI set_infinite(lprec *lp, REAL infinite) +{ + set_infiniteex(lp, infinite, FALSE); +} + +REAL __WINAPI get_infinite(lprec *lp) +{ + return(lp->infinite); +} + +void __WINAPI set_epsilon(lprec *lp, REAL epsilon) +{ + lp->epspivot = epsilon; +} + +REAL __WINAPI get_epsilon(lprec *lp) +{ + return(lp->epspivot); +} + +void __WINAPI set_epsb(lprec *lp, REAL epsb) +{ + lp->epsprimal = epsb; +} + +REAL __WINAPI get_epsb(lprec *lp) +{ + return(lp->epsprimal); +} + +void __WINAPI set_epsd(lprec *lp, REAL epsd) +{ + lp->epsdual = epsd; +} + +REAL __WINAPI get_epsd(lprec *lp) +{ + return(lp->epsdual); +} + +void __WINAPI set_epsel(lprec *lp, REAL epsel) +{ + lp->epsvalue = epsel; +} + +REAL __WINAPI get_epsel(lprec *lp) +{ + return(lp->epsvalue); +} + +void __WINAPI set_scaling(lprec *lp, int scalemode) +{ + lp->scalemode = scalemode; +} + +int __WINAPI get_scaling(lprec *lp) +{ + return(lp->scalemode); +} + +MYBOOL __WINAPI is_scalemode(lprec *lp, int testmask) +{ + return((MYBOOL) ((lp->scalemode & testmask) != 0)); +} + +MYBOOL __WINAPI is_scaletype(lprec *lp, int scaletype) +{ + int testtype; + + testtype = lp->scalemode & SCALE_MAXTYPE; + return((MYBOOL) (scaletype == testtype)); +} + +void __WINAPI set_scalelimit(lprec *lp, REAL scalelimit) +/* Set the relative scaling convergence criterion for the active scaling mode; + the integer part specifies the maximum number of iterations (default = 5). */ +{ + lp->scalelimit = fabs(scalelimit); +} + +REAL __WINAPI get_scalelimit(lprec *lp) +{ + return(lp->scalelimit); +} + +MYBOOL __WINAPI is_integerscaling(lprec *lp) +{ + return(is_scalemode(lp, SCALE_INTEGERS)); +} + +void __WINAPI set_improve(lprec *lp, int improve) +{ + lp->improve = improve; +} + +int __WINAPI get_improve(lprec *lp) +{ + return(lp->improve); +} + +void __WINAPI set_lag_trace(lprec *lp, MYBOOL lag_trace) +{ + lp->lag_trace = lag_trace; +} + +MYBOOL __WINAPI is_lag_trace(lprec *lp) +{ + return(lp->lag_trace); +} + +void __WINAPI set_pivoting(lprec *lp, int pivoting) +{ + lp->piv_strategy = pivoting; +} + +int __WINAPI get_pivoting(lprec *lp) +{ + return( lp->piv_strategy ); +} + +int get_piv_rule(lprec *lp) +{ + int piv = lp->piv_strategy; + piv = my_mod(piv, PRICE_PRIMALFALLBACK); + piv = my_mod(piv, PRICE_MULTIPLE); + piv = my_mod(piv, PRICE_ADAPTIVE); + piv = my_mod(piv, PRICE_HYBRID); +#ifdef EnableRandomizedPricing + piv = my_mod(piv, PRICE_RANDOMIZE); +#endif + return(piv); +} + +MYBOOL __WINAPI is_piv_rule(lprec *lp, int rule) +{ + return( (MYBOOL) (get_piv_rule(lp) == rule) ); +} + +MYBOOL __WINAPI is_piv_mode(lprec *lp, int testmask) +{ + return((MYBOOL) (((testmask & PRICE_STRATEGYMASK) != 0) && + ((lp->piv_strategy & testmask) != 0))); +} + +void __WINAPI set_break_at_first(lprec *lp, MYBOOL break_at_first) +{ + lp->break_at_first = break_at_first; +} + +MYBOOL __WINAPI is_break_at_first(lprec *lp) +{ + return(lp->break_at_first); +} + +void __WINAPI set_bb_floorfirst(lprec *lp, int bb_floorfirst) +{ + lp->bb_floorfirst = (MYBOOL) bb_floorfirst; +} + +int __WINAPI get_bb_floorfirst(lprec *lp) +{ + return(lp->bb_floorfirst); +} + +void __WINAPI set_break_at_value(lprec *lp, REAL break_at_value) +{ + lp->break_at_value = break_at_value; +} + +REAL __WINAPI get_break_at_value(lprec *lp) +{ + return(lp->break_at_value); +} + +void __WINAPI set_negrange(lprec *lp, REAL negrange) +{ + if(negrange <= 0) + lp->negrange = negrange; + else + lp->negrange = 0.0; +} + +REAL __WINAPI get_negrange(lprec *lp) +{ + return(lp->negrange); +} + +void __WINAPI set_splitnegvars(lprec *lp, int splitnegvars) +{ + lp->splitnegvars = splitnegvars; +} + +int __WINAPI get_splitnegvars(lprec *lp) +{ + return(lp->splitnegvars); +} + +void __WINAPI set_epsperturb(lprec *lp, REAL epsperturb) +{ + lp->epsperturb = epsperturb; +} + +REAL __WINAPI get_epsperturb(lprec *lp) +{ + return(lp->epsperturb); +} + +void __WINAPI set_epspivot(lprec *lp, REAL epspivot) +{ + lp->epspivot = epspivot; +} + +REAL __WINAPI get_epspivot(lprec *lp) +{ + return(lp->epspivot); +} + +int __WINAPI get_max_level(lprec *lp) +{ + return(lp->bb_maxlevel); +} + +int __WINAPI get_total_nodes(lprec *lp) +{ + return(lp->bb_totalnodes); +} + +int __WINAPI get_total_iter(lprec *lp) +{ + return(lp->total_iter + lp->current_iter); +} + +REAL __WINAPI get_objective(lprec *lp) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_objective: Not a valid basis"); + return(0.0); + } + + return(*(lp->best_solution)); +} + +int __WINAPI get_nonzeros(lprec *lp) +{ + return(mat_nonzeros(lp->matA)); +} + +MYBOOL __WINAPI lp_solve_set_mat(lprec *lp, int row, int column, REAL value) +{ +#ifdef Paranoia + if(row < 0 || row > lp->rows) { + report(lp, IMPORTANT, "lp_solve_set_mat: Row %d out of range", row); + return(0); + } + if(column < 1 || column > lp->columns) { + report(lp, IMPORTANT, "lp_solve_set_mat: Column %d out of range", column); + return(0); + } + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "lp_solve_set_mat: Cannot set a single matrix value while in row entry mode\n"); + return(FALSE); + } +#endif + return( mat_setvalue(lp->matA, row, column, value, lp->scaling_used) ); +} + +REAL __WINAPI get_working_objective(lprec *lp) +{ + REAL value = 0.0; + + if(!lp->basis_valid) + report(lp, CRITICAL, "get_working_objective: Not a valid basis"); + else if((lp->spx_status == RUNNING) && (lp->solutioncount == 0)) + value = my_chsign(is_maxim(lp), *(lp->rhs)); + else + value = *(lp->solution); + + return(value); +} + +REAL __WINAPI get_var_primalresult(lprec *lp, int index) +{ + if((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE) + return(lp->full_solution[index]); + else + return(lp->best_solution[index]); +} +REAL __WINAPI get_var_dualresult(lprec *lp, int index) +{ + REAL *duals; + + if(index == 0) + return( lp->best_solution[0] ); + else if(lp->duals == NULL) + return( 0.0 ); + else if((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE) { + int ix; + + ix = lp->orig_to_var[index]; + if(index > lp->orig_rows) + ix += lp->rows; + if(ix <= 0) + return( 0 ); + index = ix; + } + if(!get_ptr_sensitivity_rhs(lp, &duals, NULL, NULL)) + return( 0.0 ); + else + return( duals[index - 1] ); +} + +MYBOOL __WINAPI get_variables(lprec *lp, REAL *var) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_variables: Not a valid basis"); + return(FALSE); + } + + MEMCOPY(var, lp->best_solution + (1 + lp->rows), lp->columns); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_variables(lprec *lp, REAL **var) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_ptr_variables: Not a valid basis"); + return(FALSE); + } + + if(var != NULL) + *var = lp->best_solution + (1 + lp->rows); + return(TRUE); +} + +MYBOOL __WINAPI get_constraints(lprec *lp, REAL *constr) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_constraints: Not a valid basis"); + return(FALSE); + } + + MEMCOPY(constr, lp->best_solution + 1, lp->rows); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_constraints(lprec *lp, REAL **constr) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_ptr_constraints: Not a valid basis"); + return(FALSE); + } + + if(constr != NULL) + *constr = lp->best_solution + 1; + return(TRUE); +} + +MYBOOL __WINAPI get_sensitivity_rhs(lprec *lp, REAL *duals, REAL *dualsfrom, REAL *dualstill) +{ + REAL *duals0, *dualsfrom0, *dualstill0; + + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_sensitivity_rhs: Not a valid basis"); + return(FALSE); + } + + if(!get_ptr_sensitivity_rhs(lp, + (duals != NULL) ? &duals0 : NULL, + (dualsfrom != NULL) ? &dualsfrom0 : NULL, + (dualstill != NULL) ? &dualstill0 : NULL)) + return(FALSE); + + if(duals != NULL) + MEMCOPY(duals, duals0, lp->sum); + if(dualsfrom != NULL) + MEMCOPY(dualsfrom, dualsfrom0, lp->sum); + if(dualstill != NULL) + MEMCOPY(dualstill, dualstill0, lp->sum); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_sensitivity_rhs(lprec *lp, REAL **duals, REAL **dualsfrom, REAL **dualstill) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_ptr_sensitivity_rhs: Not a valid basis"); + return(FALSE); + } + + if(duals != NULL) { + if(lp->duals == NULL) { + if((MIP_count(lp) > 0) && (lp->bb_totalnodes > 0)) { + report(lp, CRITICAL, "get_ptr_sensitivity_rhs: Sensitivity unknown"); + return(FALSE); + } + calculate_duals(lp); + if(lp->duals == NULL) + return(FALSE); + } + *duals = lp->duals + 1; + } + + if((dualsfrom != NULL) || (dualstill != NULL)) { + if((lp->dualsfrom == NULL) || (lp->dualstill == NULL)) { + if((MIP_count(lp) > 0) && (lp->bb_totalnodes > 0)) { + report(lp, CRITICAL, "get_ptr_sensitivity_rhs: Sensitivity unknown"); + return(FALSE); + } + calculate_sensitivity_duals(lp); + if((lp->dualsfrom == NULL) || (lp->dualstill == NULL)) + return(FALSE); + } + if(dualsfrom != NULL) + *dualsfrom = lp->dualsfrom + 1; + if(dualstill != NULL) + *dualstill = lp->dualstill + 1; + } + return(TRUE); +} + +MYBOOL __WINAPI get_sensitivity_obj(lprec *lp, REAL *objfrom, REAL *objtill) +{ + REAL *objfrom0, *objtill0; + + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_sensitivity_obj: Not a valid basis"); + return(FALSE); + } + + if(!get_ptr_sensitivity_obj(lp, (objfrom != NULL) ? &objfrom0 : NULL, (objtill != NULL) ? &objtill0 : NULL)) + return(FALSE); + + if(objfrom != NULL) + MEMCOPY(objfrom, objfrom0, lp->columns); + if(objtill != NULL) + MEMCOPY(objtill, objtill0, lp->columns); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_sensitivity_obj(lprec *lp, REAL **objfrom, REAL **objtill) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_ptr_sensitivity_obj: Not a valid basis"); + return(FALSE); + } + + if((objfrom != NULL) || (objtill != NULL)) { + if((lp->objfrom == NULL) || (lp->objtill == NULL)) { + if((MIP_count(lp) > 0) && (lp->bb_totalnodes > 0)) { + report(lp, CRITICAL, "get_ptr_sensitivity_rhs: sensitivity unknown"); + return(FALSE); + } + calculate_sensitivity_obj(lp); + if((lp->objfrom == NULL) || (lp->objtill == NULL)) + return(FALSE); + } + if(objfrom != NULL) + *objfrom = lp->objfrom + 1; + if(objtill != NULL) + *objtill = lp->objtill + 1; + } + + return(TRUE); +} + +void __WINAPI set_solutionlimit(lprec *lp, int limit) +{ + lp->solutionlimit = limit; +} +int __WINAPI get_solutionlimit(lprec *lp) +{ + return(lp->solutionlimit); +} +int __WINAPI get_solutioncount(lprec *lp) +{ + return(lp->solutioncount); +} + +int __WINAPI get_Nrows(lprec *lp) +{ + return(lp->rows); +} + +int __WINAPI get_Norig_rows(lprec *lp) +{ + if(lp->varmap_locked) + return(lp->orig_rows); + else + return(lp->rows); +} + +int __WINAPI get_Lrows(lprec *lp) +{ + if(lp->matL == NULL) + return( 0 ); + else + return( lp->matL->rows ); +} + +int __WINAPI get_Ncolumns(lprec *lp) +{ + return(lp->columns); +} + +int __WINAPI get_Norig_columns(lprec *lp) +{ + if(lp->varmap_locked) + return(lp->orig_columns); + else + return(lp->columns); +} + + +/* ---------------------------------------------------------------------------------- */ +/* Core routines for lp_solve */ +/* ---------------------------------------------------------------------------------- */ +const char * __WINAPI get_statustext(lprec *lp, int statuscode) +{ + if (statuscode == NOBFP) return("No basis factorization package"); + else if (statuscode == DATAIGNORED) return("Invalid input data provided"); + else if (statuscode == NOMEMORY) return("Not enough memory available"); + else if (statuscode == NOTRUN) return("Model has not been optimized"); + else if (statuscode == OPTIMAL) return("OPTIMAL solution"); + else if (statuscode == SUBOPTIMAL) return("SUB-OPTIMAL solution"); + else if (statuscode == INFEASIBLE) return("Model is primal INFEASIBLE"); + else if (statuscode == UNBOUNDED) return("Model is primal UNBOUNDED"); + else if (statuscode == RUNNING) return("lp_solve is currently running"); + else if (statuscode == NUMFAILURE) return("NUMERIC FAILURE encountered"); + else if (statuscode == DEGENERATE) return("DEGENERATE situation"); + else if (statuscode == USERABORT) return("User-requested termination"); + else if (statuscode == TIMEOUT) return("Termination due to timeout"); + else if (statuscode == FUTURESTATUS) return("(Future)"); + else if (statuscode == PROCFAIL) return("B&B routine failed"); + else if (statuscode == PROCBREAK) return("B&B routine terminated"); + else if (statuscode == FEASFOUND) return("Feasible B&B solution found"); + else if (statuscode == NOFEASFOUND) return("No feasible B&B solution found"); + else return("Undefined internal error"); +} + +lprec * __WINAPI lp_solve_make_lp(int rows, int columns) +{ + lprec *lp; + int sum; + + callcount++; + if(rows < 0 || columns < 0) + return(NULL); + + /* Initialize randomization engine */ + srand((unsigned) time( NULL )); + + lp = (lprec*) calloc(1, sizeof(*lp)); + if(!lp) + return(NULL); + + set_lp_name(lp, "Unnamed"); + lp->names_used = FALSE; + + set_callbacks(lp); + set_BFP(lp, NULL); + set_XLI(lp, NULL); + + set_outputstream(lp, NULL); /* Set to default output stream */ + lp->verbose = NORMAL; + lp->print_sol = FALSE; /* Can be FALSE, TRUE, AUTOMATIC (only non-zeros printed) */ + lp->spx_trace = FALSE; + lp->lag_trace = FALSE; + lp->bb_trace = FALSE; + + lp->source_is_file = FALSE; + lp->model_is_pure = TRUE; + lp->model_is_valid = FALSE; + lp->spx_status = NOTRUN; + lp->lag_status = NOTRUN; + + lp->epsmachine = DEF_EPSMACHINE; + lp->epsvalue = DEF_EPSVALUE; + lp->epsprimal = DEF_EPSPRIMAL; + lp->epsdual = DEF_EPSDUAL; + lp->epsperturb = DEF_PERTURB; + lp->epspivot = DEF_EPSPIVOT; + lp->epsint = DEF_EPSINT; + lp->mip_absgap = DEF_MIP_GAP; + lp->mip_relgap = DEF_MIP_GAP; + lp->lag_accept = DEF_LAGACCEPT; + lp->wasPreprocessed = FALSE; + lp->wasPresolved = FALSE; + + lp->rows_alloc = 0; + lp->columns_alloc = 0; + lp->sum_alloc = 0; + + lp->equalities = 0; + lp->fixedvars = 0; + lp->int_count = 0; + lp->sc_count = 0; + + lp->bb_varactive = NULL; + lp->bb_floorfirst = BRANCH_FLOOR; + lp->bb_varbranch = NULL; + lp->bb_rule = NODE_FIRSTSELECT; + lp->bb_action = ACTION_NONE; + lp->bb_limitlevel = DEF_BB_LIMITLEVEL; + lp->var_priority = NULL; + + lp->bigM = 0.0; + lp->bb_deltaOF = 0.0; + + lp->sos_ints = 0; + lp->sos_vars = 0; + lp->sos_priority = NULL; + + sum = rows + columns; + lp->rows = rows; + lp->columns = columns; + lp->sum = sum; + varmap_clear(lp); + + lp->matA = mat_create(lp, rows, columns, lp->epsvalue); + lp->matL = NULL; + lp->invB = NULL; + lp->duals = NULL; + lp->dualsfrom = NULL; + lp->dualstill = NULL; + lp->objfrom = NULL; + lp->objtill = NULL; + + inc_col_space(lp, columns+1); + inc_row_space(lp, rows+1); + + /* Avoid bound-checker uninitialized variable error */ + lp->orig_lowbo[0] = 0; + + lp->max_pivots = 0; + + lp->crashmode = CRASH_NOTHING; + lp->rootbounds = NULL; +/* lp->bb_bounds = NULL; */ + lp->bb_basis = NULL; + + lp->basis_valid = FALSE; + lp->Extrap = 0; + lp->Extrad = 0.0; + lp->current_iter = 0; + lp->total_iter = 0; + lp->current_bswap = 0; + lp->total_bswap = 0; + allocINT(lp, &lp->rejectpivot, DEF_MAXPIVOTRETRY+1, TRUE); + + lp_solve_set_minim(lp); + set_infiniteex(lp, DEF_INFINITE, TRUE); + +/* lp->piv_strategy = PRICER_DANTZIG + PRICE_METHODDEFAULT + PRICE_ADAPTIVE; */ + lp->piv_strategy = PRICER_DEVEX + PRICE_METHODDEFAULT + PRICE_ADAPTIVE; +/* lp->piv_strategy = PRICER_STEEPESTEDGE + PRICE_METHODDEFAULT + PRICE_PRIMALFALLBACK + PRICE_ADAPTIVE; */ +/* lp->piv_strategy = PRICER_STEEPESTEDGE + PRICE_METHODDEFAULT + PRICE_ADAPTIVE; */ +/* lp->piv_strategy = PRICER_FIRSTINDEX + PRICE_METHODDEFAULT; */ +#ifdef EnableRandomizedPricing + lp->piv_strategy = lp->piv_strategy + PRICE_RANDOMIZE; +#endif + + lp->edgeVector = NULL; + initPricer(lp); + + lp->simplex_strategy = SIMPLEX_DUAL_PRIMAL; + lp->simplex_mode = SIMPLEX_DYNAMIC; + lp->tighten_on_set = FALSE; + lp->negrange = DEF_NEGRANGE; + lp->splitnegvars = DEF_SPLITNEGVARS; + lp->do_presolve = PRESOLVE_NONE; + lp->improve = IMPROVE_NONE; + lp->anti_degen = ANTIDEGEN_NONE; + + lp->scalemode = SCALE_NONE; + lp->scalelimit = DEF_SCALINGLIMIT; + lp->scaling_used = FALSE; + lp->columns_scaled = FALSE; + + lp->solvecount = 0; + + lp->sectimeout = 0; + lp->solutioncount = 0; + lp->solutionlimit = 1; + + /* Call-back routines by KE */ + lp->ctrlc = NULL; + lp->ctrlchandle = NULL; + lp->writelog = NULL; + lp->loghandle = NULL; + lp->debuginfo = NULL; + lp->usermessage = NULL; + lp->msgmask = MSG_NONE; + lp->msghandle = NULL; + + return(lp); +} + +void __WINAPI free_lp(lprec **plp) +{ + lprec *lp; + + lp = *plp; + if(lp != NULL) + lp_solve_delete_lp(lp); + *plp = NULL; +} + +void __WINAPI lp_solve_delete_lp(lprec *lp) +{ + FREE(lp->lp_name); + if(lp->names_used) { + FREE(lp->row_name); + FREE(lp->col_name); + free_hash_table(lp->rowname_hashtab); + free_hash_table(lp->colname_hashtab); + } + + mat_free(&lp->matA); + lp->bfp_free(lp); +#if LoadInverseLib == TRUE + if(lp->hBFP != NULL) + set_BFP(lp, NULL); +#endif +#if LoadLanguageLib == TRUE + if(lp->hXLI != NULL) + set_XLI(lp, NULL); +#endif + + FREE(lp->orig_rh); + FREE(lp->rhs); + FREE(lp->must_be_int); + set_var_weights(lp, NULL); + if(lp->bb_varbranch != NULL) + FREE(lp->bb_varbranch); + FREE(lp->var_is_sc); + if(lp->var_is_free != NULL) + FREE(lp->var_is_free); + FREE(lp->orig_upbo); + FREE(lp->orig_lowbo); + FREE(lp->upbo); + FREE(lp->lowbo); + FREE(lp->var_basic); + FREE(lp->is_basic); + FREE(lp->is_lower); + if(lp->bb_PseudoCost != NULL) { + report(lp, SEVERE, "lp_solve_delete_lp: The B&B pseudo-cost array was not cleared on delete\n"); + free_PseudoCost(lp); + } + if(lp->bb_bounds != NULL) { + report(lp, SEVERE, "lp_solve_delete_lp: The stack of B&B levels was not empty on delete\n"); + unload_BB(lp); + } + if(lp->bb_basis != NULL) { + report(lp, SEVERE, "lp_solve_delete_lp: The stack of saved bases was not empty on delete\n"); + unload_basis(lp, FALSE); + } + + FREE(lp->rejectpivot); + partial_freeBlocks(&(lp->rowblocks)); + partial_freeBlocks(&(lp->colblocks)); + FREE(lp->multivar); + + FREE(lp->solution); + FREE(lp->best_solution); + if(lp->full_solution != NULL) + FREE(lp->full_solution); + FREE(lp->var_to_orig); + FREE(lp->orig_to_var); + + freePricer(lp); + + if(lp->duals != NULL) + FREE(lp->duals); + if(lp->dualsfrom != NULL) + FREE(lp->dualsfrom); + if(lp->dualstill != NULL) + FREE(lp->dualstill); + if(lp->objfrom != NULL) + FREE(lp->objfrom); + if(lp->objtill != NULL) + FREE(lp->objtill); + FREE(lp->row_type); + + if(lp->sos_vars > 0) + FREE(lp->sos_priority); + free_SOSgroup(&(lp->SOS)); + free_SOSgroup(&(lp->GUB)); + + if(lp->scaling_used) + FREE(lp->scalars); + if(lp->matL != NULL) { + FREE(lp->lag_rhs); + FREE(lp->lambda); + FREE(lp->lag_con_type); + mat_free(&lp->matL); + } + + free(lp); + +} + +/* Utility routine group for constraint and column deletion/insertion + mapping in relation to the original set of constraints and columns */ +STATIC void varmap_lock(lprec *lp) +{ + int i; + + for(i = 0; i <= lp->rows; i++) { + lp->var_to_orig[i] = i; + lp->orig_to_var[i] = i; + } + for(i = 1; i <= lp->columns; i++) { + lp->var_to_orig[lp->rows + i] = i; + lp->orig_to_var[lp->rows + i] = i; + } + lp->orig_columns = lp->columns; + lp->orig_rows = lp->rows; + lp->varmap_locked = TRUE; +} +STATIC void varmap_clear(lprec *lp) +{ + lp->orig_columns = 0; + lp->orig_rows = 0; + lp->varmap_locked = FALSE; +} +STATIC void varmap_add(lprec *lp, int base, int delta) +{ + int i, ii; + + /* Don't do anything if variables aren't locked yet */ + if(!lp->varmap_locked) + return; + + /* Set new constraints/columns to have an "undefined" mapping to original + constraints/columns (assumes that counters have NOT yet been updated) */ + for(i = lp->sum; i >= base; i--) { + ii = i + delta; + lp->var_to_orig[ii] = lp->var_to_orig[i]; + } + ii = base; + for(i = 0; i < delta; i++) { + ii = base + i; + lp->var_to_orig[ii] = 0; + } +} + +STATIC void varmap_delete(lprec *lp, int base, int delta) +{ + int i, ii, j; + + /* Set the model "dirty" if we are deleting row of constraint */ + lp->model_is_pure = FALSE; + + /* Don't do anything if 1) variables aren't locked yet, or + 2) the constraint was added after the variables were locked */ + if(!lp->varmap_locked) + return; + + /* We are deleting an original constraint/column; + 1) clear mapping of original to deleted + 2) shift the deleted variable to original mappings left + 3) decrement all subsequent original-to-current pointers + */ + for(i = base; i < base-delta; i++) { + ii = lp->var_to_orig[i]; + if(ii > 0) + lp->orig_to_var[ii] = 0; + } + for(i = base; i <= lp->sum+delta; i++) { + ii = i - delta; + lp->var_to_orig[i] = lp->var_to_orig[ii]; + } + + j = lp->orig_rows; + if(base <= lp->rows) + i = 1; + else { + i = lp->orig_rows+1; + j += lp->orig_columns; + } + ii = base-delta; + for(; i <= j; i++) { + if(lp->orig_to_var[i] >= ii) + lp->orig_to_var[i] += delta; + } + +} + +/* Utility group for shifting row and column data */ +STATIC MYBOOL shift_rowcoldata(lprec *lp, int base, int delta, MYBOOL isrow) +/* Note: Assumes that "lp->sum" and "lp->rows" HAVE NOT been updated to the new counts */ +{ + int i, ii; + REAL lodefault; + + /* Shift data right/down (insert), and set default values in positive delta-gap */ + if(delta > 0) { + + /* Shift the row/column data */ +#if 0 + for(ii = lp->sum; ii >= base; ii--) { + i = ii + delta; + lp->upbo[i] = lp->upbo[ii]; + lp->orig_upbo[i] = lp->orig_upbo[ii]; + lp->lowbo[i] = lp->lowbo[ii]; + lp->orig_lowbo[i] = lp->orig_lowbo[ii]; + lp->solution[i] = lp->solution[ii]; + lp->best_solution[i] = lp->best_solution[ii]; + lp->is_lower[i] = lp->is_lower[ii]; + } +#else + MEMMOVE(lp->upbo + base + delta, lp->upbo + base, lp->sum - base + 1); + MEMMOVE(lp->orig_upbo + base + delta, lp->orig_upbo + base, lp->sum - base + 1); + MEMMOVE(lp->lowbo + base + delta, lp->lowbo + base, lp->sum - base + 1); + MEMMOVE(lp->orig_lowbo + base + delta, lp->orig_lowbo + base, lp->sum - base + 1); + if(lp->model_is_valid) { + MEMMOVE(lp->solution + base + delta, lp->solution + base, lp->sum - base + 1); + MEMMOVE(lp->best_solution + base + delta, lp->best_solution + base, lp->sum - base + 1); + } + MEMMOVE(lp->is_lower + base + delta, lp->is_lower + base, lp->sum - base + 1); +#endif + + /* Deal with scalars; the vector can be NULL */ + if(lp->scalars != NULL) { + for(ii = lp->sum; ii >= base; ii--) { /* **** */ + i = ii + delta; + lp->scalars[i] = lp->scalars[ii]; + } + for(ii = base; ii < base + delta; ii++) + lp->scalars[ii] = 1; + } + + /* Set defaults */ + if(isrow) + lodefault = -lp->infinite; + else + lodefault = 0; + + for(i = 0; i < delta; i++) { + ii = base + i; + lp->upbo[ii] = lp->infinite; + lp->orig_upbo[ii] = lp->upbo[ii]; + lp->lowbo[ii] = lodefault; + lp->orig_lowbo[ii] = lp->lowbo[ii]; + lp->is_lower[ii] = TRUE; + } + } + + /* Shift data left/up (delete) */ + else if(delta < 0) { + + /* First make sure we don't cross the sum count border */ + if(base-delta-1 > lp->sum) + delta = base - lp->sum - 1; + + /* Shift the data*/ + for(i = base; i <= lp->sum + delta; i++) { + ii = i - delta; + lp->upbo[i] = lp->upbo[ii]; + lp->orig_upbo[i] = lp->orig_upbo[ii]; + lp->lowbo[i] = lp->lowbo[ii]; + lp->orig_lowbo[i] = lp->orig_lowbo[ii]; + lp->solution[i] = lp->solution[ii]; + lp->best_solution[i] = lp->best_solution[ii]; + lp->is_lower[i] = lp->is_lower[ii]; + } + + /* Deal with scalars */ + if(lp->scalars != NULL) { + for(i = base; i <= lp->sum + delta; i++) { + ii = i - delta; + lp->scalars[i] = lp->scalars[ii]; + } + } + + } + + lp->sum += delta; + + lp->matA->row_end_valid = FALSE; + + return(TRUE); +} + +STATIC MYBOOL shift_basis(lprec *lp, int base, int delta, MYBOOL isrow) +/* Note: Assumes that "lp->sum" and "lp->rows" HAVE NOT been updated to the new counts */ +{ + int i, ii; + MYBOOL Ok = TRUE; + + /* Don't bother to shift the basis if it is not yet ready */ + if(!is_BasisReady(lp)) + return( Ok ); + + /* Basis adjustments due to insertions (after actual row/column insertions) */ + if(delta > 0) { + + /* Determine if the basis becomes invalidated */ + if(isrow) { + lp->basis_valid = FALSE; + lp->doRebase = TRUE; /* Force rebasing and reinversion */ + lp->doInvert = TRUE; + } + + /* Shift and fix invalid basis references (increment higher order basic variable index) */ +#if 0 + for(ii = lp->sum; ii >= base; ii--) { + i = ii + delta; + lp->is_basic[i] = lp->is_basic[ii]; + } +#else + if(base <= lp->sum) + MEMMOVE(lp->is_basic + base + delta, lp->is_basic + base, lp->sum - base + 1); +#endif + + /* Prevent CPU-expensive basis updating if this is the initial model creation */ + if(!lp->model_is_pure || (lp->solvecount > 0)) + for(i = 1; i <= lp->rows; i++) { + ii = lp->var_basic[i]; + if(ii >= base) + lp->var_basic[i] += delta; + } + + /* Update the basis (shift and extend) */ + for(i = 0; i < delta; i++) { + ii = base + i; + lp->is_basic[ii] = isrow; + if(isrow) + lp->var_basic[lp->rows+1+i] = ii; + } + + } + /* Basis adjustments due to deletions (after actual row/column deletions) */ + else { + int j,k; + + /* Fix invalid basis references (decrement high basic slack variable indexes), + but reset the entire basis if a deleted variable is found in the basis */ + k = 0; + for(i = 1; i <= lp->rows; i++) { + ii = lp->var_basic[i]; + lp->is_basic[ii] = FALSE; + if(ii >= base) { + /* Skip to next basis variable if this one is to be deleted */ + if(ii < base-delta) { + lp->doRebase = TRUE; + continue; + } + /* Otherwise, update the index of the basic variable for deleted variables */ + ii += delta; + } + k++; + lp->var_basic[k] = ii; + } + + /* Set the new basis indicators */ + i = k; + if(isrow) + i = MIN(k, lp->rows+delta); + for(; i > 0; i--) { + j = lp->var_basic[i]; + lp->is_basic[j] = TRUE; + } + + /* If a column was deleted from the basis then simply add back a non-basic + slack variable; do two scans, if necessary to avoid adding equality slacks */ + if(!isrow && (k < lp->rows)) { + int j; + for(j = 0; j <= 1; j++) + for(i = 1; (i <= lp->rows) && (k < lp->rows); i++) + if(!lp->is_basic[i]) { + if(!is_constr_type(lp, i, EQ) || (j == 1)) { + k++; + lp->var_basic[k] = i; + lp->is_basic[i] = TRUE; + } + } + k = 0; + } + + /* We are left with "k" indexes; if no basis variable was deleted, k=rows and the + inverse is still valid, if k+delta < 0 we do not have a valid + basis and must create one (in most usage modes this should not happen, + unless there is a bug) */ + if(k+delta < 0) + Ok = FALSE; + if(isrow || (k != lp->rows)) + lp->doInvert = TRUE; + + } + return(Ok); + +} + +STATIC MYBOOL shift_rowdata(lprec *lp, int base, int delta) +/* Note: Assumes that "lp->rows" HAS NOT been updated to the new count */ +{ + int i, ii; + + /* Shift sparse matrix row data */ + if(lp->matA->is_roworder) + mat_shiftcols(lp->matA, base+1, delta); + else + mat_shiftrows(lp->matA, &base, delta); + + /* Shift data down (insert row), and set default values in positive delta-gap */ + if(delta > 0) { + + /* Shift row data */ + for(ii = lp->rows; ii >= base; ii--) { + i = ii + delta; + lp->orig_rh[i] = lp->orig_rh[ii]; + lp->rhs[i] = lp->rhs[ii]; + lp->row_type[i] = lp->row_type[ii]; + } + + /* Set defaults (actual basis set in separate procedure) */ + for(i = 0; i < delta; i++) { + ii = base + i; + lp->orig_rh[ii] = 0; + lp->rhs[ii] = 0; + lp->row_type[ii] = ROWTYPE_EMPTY; + } + } + + /* Shift data up (delete row) */ + else if(delta < 0) { + + /* First make sure we don't cross the row count border */ + if(base-delta-1 > lp->rows) + delta = base - lp->rows - 1; + + /* Shift row data (don't shift basis indexes here; done in next step) */ + for(i = base; i <= lp->rows + delta; i++) { + ii = i - delta; + lp->orig_rh[i] = lp->orig_rh[ii]; + lp->rhs[i] = lp->rhs[ii]; + lp->row_type[i] = lp->row_type[ii]; + } + } + + shift_basis(lp, base, delta, TRUE); + shift_rowcoldata(lp, base, delta, TRUE); + inc_rows(lp ,delta); + + return(TRUE); +} + +STATIC MYBOOL shift_coldata(lprec *lp, int base, int delta) +/* Note: Assumes that "lp->columns" HAS NOT been updated to the new count */ +{ + int i, ii; + + /* Shift A matrix data */ + if(lp->matA->is_roworder) + mat_shiftrows(lp->matA, &base, delta); + else + mat_shiftcols(lp->matA, base, delta); + + /* Shift data right (insert), and set default values in positive delta-gap */ + if(delta > 0) { + + /* Fix invalid variable priority data */ + if((lp->var_priority != NULL) && (base <= lp->columns)) { + for(i = 0; i < lp->columns; i++) + if(lp->var_priority[i] >= base) + lp->var_priority[i] += delta; + } + + /* Fix invalid split variable data */ + if((lp->var_is_free != NULL) && (base <= lp->columns)) { + for(i = 1; i <= lp->columns; i++) + if(abs(lp->var_is_free[i]) >= base) + lp->var_is_free[i] += my_chsign(lp->var_is_free[i] < 0, delta); + } + + /* Shift column data right */ + for(ii = lp->columns; ii >= base; ii--) { /* **** */ + i = ii + delta; + lp->must_be_int[i] = lp->must_be_int[ii]; + lp->var_is_sc[i] = lp->var_is_sc[ii]; + if(lp->objfrom != NULL) + lp->objfrom[i] = lp->objfrom[ii]; + if(lp->objtill != NULL) + lp->objtill[i] = lp->objtill[ii]; + if(lp->var_priority != NULL) + lp->var_priority[i-1] = lp->var_priority[ii-1]; + if(lp->bb_varbranch != NULL) + lp->bb_varbranch[i-1] = lp->bb_varbranch[ii-1]; + if(lp->var_is_free != NULL) + lp->var_is_free[i] = lp->var_is_free[ii]; + } + + /* Set defaults */ + for(i = 0; i < delta; i++) { + ii = base + i; + lp->must_be_int[ii] = ISREAL; + lp->var_is_sc[ii] = 0; + if(lp->objfrom != NULL) + lp->objfrom[ii] = 0; + if(lp->objtill != NULL) + lp->objtill[ii] = 0; + if(lp->var_priority != NULL) + lp->var_priority[ii-1] = ii; + if(lp->bb_varbranch != NULL) + lp->bb_varbranch[ii-1] = BRANCH_DEFAULT; + if(lp->var_is_free != NULL) + lp->var_is_free[ii] = 0; + } + } + + /* Shift data left (delete) */ + else if(delta < 0) { + + /* Fix invalid split variable data */ + if(lp->var_is_free != NULL) { + for(i = 1; i <= lp->columns; i++) + if(abs(lp->var_is_free[i]) >= base) + lp->var_is_free[i] -= my_chsign(lp->var_is_free[i] < 0, delta); + } + + /* Shift column data (excluding the basis) */ + for(i = base; i < base-delta; i++) { + if(is_int(lp, i)) { + lp->int_count--; + if(SOS_is_member(lp->SOS, 0, i)) + lp->sos_ints--; + } + if(is_semicont(lp, i)) + lp->sc_count--; + } + for(i = base; i <= lp->columns + delta; i++) { + ii = i - delta; + lp->must_be_int[i] = lp->must_be_int[ii]; + lp->var_is_sc[i] = lp->var_is_sc[ii]; + if(lp->objfrom != NULL) + lp->objfrom[i] = lp->objfrom[ii]; + if(lp->objtill != NULL) + lp->objtill[i] = lp->objtill[ii]; + if(lp->var_priority != NULL) + lp->var_priority[i-1] = lp->var_priority[ii-1]; + if(lp->bb_varbranch != NULL) + lp->bb_varbranch[i-1] = lp->bb_varbranch[ii-1]; + if(lp->var_is_free != NULL) + lp->var_is_free[i] = lp->var_is_free[ii]; + } + + /* Fix invalid variable priority data */ + if(lp->var_priority != NULL) { + for(i = 0; i < lp->columns+delta; i++) + if(lp->var_priority[i] >= base) + lp->var_priority[i] += delta; + } + } + + shift_basis(lp, lp->rows+base, delta, FALSE); + if(SOS_count(lp) > 0) + SOS_shift_col(lp->SOS, 0, base, delta, FALSE); + shift_rowcoldata(lp, lp->rows+base, delta, FALSE); + inc_columns(lp, delta); + + return(TRUE); +} + +/* Utility group for incrementing row and column vector storage space */ +STATIC void inc_rows(lprec *lp, int delta) +{ + lp->rows += delta; + if(lp->matA->is_roworder) + lp->matA->columns += delta; + else + lp->matA->rows += delta; +} + +STATIC void inc_columns(lprec *lp, int delta) +{ + lp->columns += delta; + if(lp->matA->is_roworder) + lp->matA->rows += delta; + else + lp->matA->columns += delta; + if(get_Lrows(lp) > 0) + lp->matL->columns += delta; +} + +STATIC MYBOOL inc_rowcol_space(lprec *lp, int delta) +{ + int i, oldrowcolalloc, rowcolsum; + + /* Set constants */ + oldrowcolalloc = lp->sum_alloc; + lp->sum_alloc += delta; + rowcolsum = lp->sum_alloc + 1; + + /* Reallocate lp memory */ + allocREAL(lp, &lp->upbo, rowcolsum, AUTOMATIC); + allocREAL(lp, &lp->orig_upbo, rowcolsum, AUTOMATIC); + allocREAL(lp, &lp->lowbo, rowcolsum, AUTOMATIC); + allocREAL(lp, &lp->orig_lowbo, rowcolsum, AUTOMATIC); + allocREAL(lp, &lp->solution, rowcolsum, AUTOMATIC); + allocREAL(lp, &lp->best_solution, rowcolsum, AUTOMATIC); + allocMYBOOL(lp, &lp->is_basic, rowcolsum, AUTOMATIC); + allocMYBOOL(lp, &lp->is_lower, rowcolsum, AUTOMATIC); + allocINT(lp, &lp->var_to_orig, rowcolsum, AUTOMATIC); + allocINT(lp, &lp->orig_to_var, rowcolsum, AUTOMATIC); + + /* Fill in default values, where appropriate */ + for(i = oldrowcolalloc+1; i < rowcolsum; i++) { + lp->upbo[i] = lp->infinite; + lp->orig_upbo[i] = lp->upbo[i]; + lp->lowbo[i] = 0; + lp->orig_lowbo[i] = lp->lowbo[i]; + lp->is_basic[i] = FALSE; + lp->is_lower[i] = TRUE; + lp->var_to_orig[i] = 0; + lp->orig_to_var[i] = 0; + } + + /* Deal with scalars; the vector can be NULL and also contains Lagrangean information */ + if(lp->scalars != NULL) { + allocREAL(lp, &lp->scalars, rowcolsum, AUTOMATIC); + for(i = oldrowcolalloc+1; i < rowcolsum; i++) + lp->scalars[i] = 1; + } + + resizePricer(lp); + + return(TRUE); +} + +STATIC MYBOOL inc_lag_space(lprec *lp, int deltarows, MYBOOL ignoreMAT) +{ + int newsize; + + if(deltarows > 0) { + + newsize = get_Lrows(lp) + deltarows; + + /* Reallocate arrays */ + allocREAL(lp, &lp->lag_rhs, newsize+1, AUTOMATIC); + allocREAL(lp, &lp->lambda, newsize+1, AUTOMATIC); + allocINT(lp, &lp->lag_con_type, newsize+1, AUTOMATIC); + + /* Reallocate the matrix (note that the row scalars are stored at index 0) */ + if(!ignoreMAT) { + if(lp->matL == NULL) + lp->matL = mat_create(lp, newsize, lp->columns, lp->epsvalue); + else + inc_matrow_space(lp->matL, deltarows); + } + lp->matL->rows += deltarows; + + } + /* Handle column count expansion as special case */ + else if(!ignoreMAT) { + inc_matcol_space(lp->matL, lp->columns_alloc-lp->matL->columns_alloc+1); + } + + + return(TRUE); +} + +STATIC MYBOOL inc_row_space(lprec *lp, int deltarows) +{ + int i, rowsum, oldrowsalloc, rowdelta = DELTAROWALLOC; + MYBOOL ok = TRUE; + + /* Adjust lp row structures */ + if(lp->matA->is_roworder) + inc_matcol_space(lp->matA, deltarows); + else + inc_matrow_space(lp->matA, deltarows); + if(lp->rows+deltarows > lp->rows_alloc) { + + deltarows = MAX(deltarows, rowdelta); + oldrowsalloc = lp->rows_alloc; + lp->rows_alloc += deltarows; + rowsum = lp->rows_alloc + 1; + + allocREAL(lp, &lp->orig_rh, rowsum, AUTOMATIC); + allocLREAL(lp, &lp->rhs, rowsum, AUTOMATIC); + allocINT(lp, &lp->row_type, rowsum, AUTOMATIC); + allocINT(lp, &lp->var_basic, rowsum, AUTOMATIC); + if(oldrowsalloc == 0) { + lp->var_basic[0] = AUTOMATIC; /*TRUE;*/ /* Indicates default basis */ + lp->orig_rh[0] = 0; + lp->row_type[0] = ROWTYPE_OFMIN; + } + for(i = oldrowsalloc+1; i < rowsum; i++) { + lp->orig_rh[i] = 0; + lp->rhs[i] = 0; + lp->row_type[i] = ROWTYPE_EMPTY; + lp->var_basic[i] = i; + } + + /* Adjust hash name structures */ + if(lp->names_used && (lp->row_name != NULL)) { + + /* First check the hash table */ + if(lp->rowname_hashtab->size < lp->rows_alloc) { + hashtable *ht; + + ht = copy_hash_table(lp->rowname_hashtab, lp->row_name, lp->rows_alloc + 1); + if(ht != NULL) { + free_hash_table(lp->rowname_hashtab); + lp->rowname_hashtab = ht; + } + } + + /* Then the string storage (i.e. pointer to the item's hash structure) */ + lp->row_name = (hashelem **) realloc(lp->row_name, (rowsum) * sizeof(*lp->row_name)); + for(i = oldrowsalloc + 1; i < rowsum; i++) + lp->row_name[i] = NULL; + } + + ok = inc_rowcol_space(lp, deltarows); + + } + return(ok); +} + +STATIC MYBOOL inc_col_space(lprec *lp, int deltacols) +{ + int i,colsum, oldcolsalloc, newcolcount; + + if(lp->matA->is_roworder) + inc_matrow_space(lp->matA, deltacols); + else + inc_matcol_space(lp->matA, deltacols); + + newcolcount = lp->columns+deltacols; + if(newcolcount >= lp->columns_alloc) { + + oldcolsalloc = lp->columns_alloc; + deltacols = MAX(DELTACOLALLOC, newcolcount); + lp->columns_alloc += deltacols; + colsum = lp->columns_alloc + 1; + + /* Adjust hash name structures */ + if(lp->names_used && (lp->col_name != NULL)) { + + /* First check the hash table */ + if(lp->colname_hashtab->size < lp->columns_alloc) { + hashtable *ht; + + ht = copy_hash_table(lp->colname_hashtab, lp->col_name, lp->columns_alloc + 1); + if(ht != NULL) { + free_hash_table(lp->colname_hashtab); + lp->colname_hashtab = ht; + } + } + + /* Then the string storage (i.e. pointer to the item's hash structure) */ + lp->col_name = (hashelem **) realloc(lp->col_name, (colsum) * sizeof(*lp->col_name)); + for(i = oldcolsalloc+1; i < colsum; i++) + lp->col_name[i] = NULL; + } + + allocMYBOOL(lp, &lp->must_be_int, colsum, AUTOMATIC); + allocREAL(lp, &lp->var_is_sc, colsum, AUTOMATIC); + if(lp->var_priority != NULL) + allocINT(lp, &lp->var_priority, colsum-1, AUTOMATIC); + + /* Make sure that Lagrangean constraints have the same number of columns */ + if(get_Lrows(lp) > 0) + inc_lag_space(lp, 0, FALSE); + + /* Update column pointers */ + for(i = MIN(oldcolsalloc, lp->columns) + 1; i < colsum; i++) { + lp->must_be_int[i] = ISREAL; + lp->var_is_sc[i] = 0; + if(lp->var_priority != NULL) + lp->var_priority[i-1] = i; + } + + if(lp->var_is_free != NULL) { + allocINT(lp, &lp->var_is_free, colsum, AUTOMATIC); + for(i = oldcolsalloc+1; i < colsum; i++) + lp->var_is_free[i] = 0; + } + + if(lp->bb_varbranch != NULL) { + allocMYBOOL(lp, &lp->bb_varbranch, colsum-1, AUTOMATIC); + for(i = oldcolsalloc; i < colsum-1; i++) + lp->bb_varbranch[i] = BRANCH_DEFAULT; + } + + inc_rowcol_space(lp, deltacols); + + } + return(TRUE); +} + + +/* Problem manipulation routines */ + +MYBOOL __WINAPI set_obj(lprec *lp, int Column, REAL Value) +{ + if(Column <= 0) + Column = lp_solve_set_rh(lp, 0, Value); + else + Column = lp_solve_set_mat(lp, 0, Column, Value); + return((MYBOOL) Column); +} + +MYBOOL __WINAPI set_obj_fnex(lprec *lp, int count, REAL *row, int *colno) +{ + int i, n; + + if(colno == NULL) + n = lp->columns; + else + n = count; + if(lp->matA->is_roworder && (mat_nonzeros(lp->matA) == 0)) { + mat_appendrow(lp->matA, n, row, colno, my_chsign(is_maxim(lp), 1.0)); + } + else if(colno == NULL) { + for(i = 1; i <= n; i++) + if(!lp_solve_set_mat(lp, 0, i, row[i])) + return(FALSE); + } + else + for(i = 0; i < n; i++) + if(!lp_solve_set_mat(lp, 0, colno[i], row[i])) + return(FALSE); + + return(TRUE); +} + +MYBOOL __WINAPI set_obj_fn(lprec *lp, REAL *row) +{ + return( set_obj_fnex(lp, 0, row, NULL) ); +} + +MYBOOL __WINAPI str_set_obj_fn(lprec *lp, char *row_string) +{ + int i; + MYBOOL ret = TRUE; + REAL *arow; + char *p, *newp; + + allocREAL(lp, &arow, lp->columns + 1, FALSE); + p = row_string; + for(i = 1; i <= lp->columns; i++) { + arow[i] = (REAL) strtod(p, &newp); + if(p == newp) { + report(lp, IMPORTANT, "str_set_obj_fn: Bad string %s\n", p); + lp->spx_status = DATAIGNORED; + ret = FALSE; + break; + } + else + p = newp; + } + if(lp->spx_status != DATAIGNORED) + ret = set_obj_fn(lp, arow); + FREE(arow); + return( ret ); +} + +STATIC MYBOOL append_columns(lprec *lp, int deltacolumns) +{ + if(!inc_col_space(lp, deltacolumns)) + return( FALSE ); + varmap_add(lp, lp->sum+1, deltacolumns); + shift_coldata(lp, lp->columns+1, deltacolumns); + return( TRUE ); +} + +STATIC MYBOOL append_rows(lprec *lp, int deltarows) +{ + if(!inc_row_space(lp, deltarows)) + return( FALSE ); + varmap_add(lp, lp->rows+1, deltarows); + shift_rowdata(lp, lp->rows+1, deltarows); + return( TRUE ); +} + +MYBOOL __WINAPI set_add_rowmode(lprec *lp, MYBOOL turnon) +{ + MYBOOL status = TRUE; + + if(lp->matA->is_roworder && !turnon) + mat_transpose(lp->matA, TRUE); + else if((mat_nonzeros(lp->matA) == 0) && turnon) + mat_transpose(lp->matA, FALSE); + else + status = FALSE; + return(status); +} + +MYBOOL __WINAPI is_add_rowmode(lprec *lp) +{ + return(lp->matA->is_roworder); +} + +MYBOOL __WINAPI add_constraintex(lprec *lp, int count, REAL *row, int *colno, int constr_type, REAL rh) +{ + int n; + MYBOOL status = FALSE; + +#ifdef Paranoia + if(!(constr_type == LE || constr_type == GE || constr_type == EQ)) { + report(lp, IMPORTANT, "add_constraintex: Invalid %d constraint type\n", constr_type); + return( status ); + } +#endif + + /* Prepare for a new row */ + if(!append_rows(lp, 1)) + return( status ); + + /* Set constraint parameters, fix the slack */ + if((constr_type & ROWTYPE_CONSTRAINT) == EQ) { + lp->equalities++; + lp->orig_upbo[lp->rows] = 0; + lp->upbo[lp->rows] = 0; + } + lp->row_type[lp->rows] = constr_type; + + if(is_chsign(lp, lp->rows) && (rh != 0)) + lp->orig_rh[lp->rows] = -rh; + else + lp->orig_rh[lp->rows] = rh; + + /* Insert the non-zero constraint values */ + if(colno == NULL) + n = lp->columns; + else + n = count; + mat_appendrow(lp->matA, n, row, colno, my_chsign(is_chsign(lp, lp->rows), 1.0)); + +#ifdef Paranoia + if(lp->matA->is_roworder) + n = lp->matA->columns-1; + else + n = lp->matA->rows; + if(lp->rows != n) { + report(lp, SEVERE, "add_constraintex: Row count mismatch %d vs %d\n", + lp->rows, n); + } + else if(is_BasisReady(lp) && !verifyBasis(lp)) + report(lp, SEVERE, "add_constraintex: Invalid basis detected for row %d\n", lp->rows); + else +#endif + status = TRUE; + + return( status ); +} + +MYBOOL __WINAPI add_constraint(lprec *lp, REAL *row, int constr_type, REAL rh) +{ + return( add_constraintex(lp, 0, row, NULL, constr_type, rh) ); +} + +MYBOOL __WINAPI str_add_constraint(lprec *lp, char *row_string, int constr_type, REAL rh) +{ + int i; + char *p, *newp; + REAL *aRow; + MYBOOL status = FALSE; + + allocREAL(lp, &aRow, lp->columns + 1, FALSE); + p = row_string; + + for(i = 1; i <= lp->columns; i++) { + aRow[i] = (REAL) strtod(p, &newp); + if(p == newp) { + report(lp, IMPORTANT, "str_add_constraint: Bad string %s\n", p); + lp->spx_status = DATAIGNORED; + break; + } + else + p = newp; + } + if(lp->spx_status != DATAIGNORED) + status = add_constraint(lp, aRow, constr_type, rh); + FREE(aRow); + + return(status); +} + +MYBOOL __WINAPI del_constraint(lprec *lp, int del_row) +{ + MYBOOL preparecompact = (MYBOOL) (del_row < 0); + + preparecompact = (MYBOOL) (del_row < 0); + if(preparecompact) + del_row = -del_row; +#ifdef Paranoia + if((del_row < 1) || (del_row > lp->rows)) { + report(lp, IMPORTANT, "del_constraint: Attempt to delete non-existing constraint %d\n", del_row); + return(FALSE); + } + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "del_constraint: Cannot delete constraint while in row entry mode.\n"); + return(FALSE); + } +#endif + + if(is_constr_type(lp, del_row, EQ) && (lp->equalities > 0)) + lp->equalities--; + + varmap_delete(lp, del_row, -1); + shift_rowdata(lp, my_chsign(preparecompact,del_row), -1); +#ifdef Paranoia + if(is_BasisReady(lp) && !verifyBasis(lp)) + report(lp, SEVERE, "del_constraint: Invalid basis detected at row %d\n", del_row); +#endif + + return(TRUE); +} + +MYBOOL __WINAPI add_lag_con(lprec *lp, REAL *row, int con_type, REAL rhs) +{ + int k; + REAL sign; + + if(con_type == LE || con_type == EQ) + sign = 1; + else if(con_type == GE) + sign = -1; + else { + report(lp, IMPORTANT, "add_lag_con: Constraint type %d not implemented\n", con_type); + return(FALSE); + } + + inc_lag_space(lp, 1, FALSE); + + k = get_Lrows(lp); + lp->lag_rhs[k] = rhs * sign; + mat_appendrow(lp->matL, lp->columns, row, NULL, sign); + lp->lambda[k] = 0; + lp->lag_con_type[k] = con_type; + + return(TRUE); +} + +MYBOOL __WINAPI str_add_lag_con(lprec *lp, char *row_string, int con_type, REAL rhs) +{ + int i; + MYBOOL ret = TRUE; + REAL *a_row; + char *p, *new_p; + + allocREAL(lp, &a_row, lp->columns + 1, FALSE); + p = row_string; + + for(i = 1; i <= lp->columns; i++) { + a_row[i] = (REAL) strtod(p, &new_p); + if(p == new_p) { + report(lp, IMPORTANT, "str_add_lag_con: Bad string %s\n", p); + lp->spx_status = DATAIGNORED; + ret = FALSE; + break; + } + else + p = new_p; + } + if(lp->spx_status != DATAIGNORED) + ret = add_lag_con(lp, a_row, con_type, rhs); + FREE(a_row); + return( ret ); +} + +STATIC MYBOOL is_splitvar(lprec *lp, int column) +/* Two cases handled by var_is_free: + + 1) LB:-Inf / UB:var_is_free != NULL) && + (lp->var_is_free[column] < 0) && (-lp->var_is_free[column] != column))); +} + +void del_splitvars(lprec *lp) +{ + int j, jj, i; + + if(lp->var_is_free != NULL) { + for (j = lp->columns; j >= 1; j--) + if(is_splitvar(lp, j)) { + /* Check if we need to modify the basis */ + jj = lp->rows+abs(lp->var_is_free[j]); + if(lp->is_basic[lp->rows+j] && !lp->is_basic[jj]) { + i = findBasisPos(lp, jj, NULL); + setBasisVar(lp, i, jj); + } + /* Delete the helper column */ + del_column(lp, j); + } + FREE(lp->var_is_free); + } +} + +MYBOOL __WINAPI add_columnex(lprec *lp, int count, REAL *column, int *rowno) +/* This function adds a data column to the current model; three cases handled: + + 1: Prepare for column data by setting column = NULL + 2: Dense vector indicated by (rowno == NULL) over 0..count+get_Lrows() elements + 3: Sparse vector set over row vectors rowno, over 0..count-1 elements. + + NB! If the column has only one entry, this should be handled as + a bound, but this currently is not the case */ +{ + MYBOOL status = FALSE; + + /* Prepare and shift column vectors */ + if(!append_columns(lp, 1)) + return( status ); + + /* Append sparse regular constraint values */ + if(mat_appendcol(lp->matA, count, column, rowno, 1.0) < 0) + report(lp, SEVERE, "add_columnex: Data column supplied in non-ascending row index order.\n"); + +#ifdef Paranoia + if(lp->columns != lp->matA->columns) { + report(lp, SEVERE, "add_columnex: Column count mismatch %d vs %d\n", + lp->columns, lp->matA->columns); + } + else if(is_BasisReady(lp) && (lp->Extrap == 0) && !verifyBasis(lp)) + report(lp, SEVERE, "add_columnex: Invalid basis detected for column %d\n", lp->columns); +#endif + else + status = TRUE; + + return( status ); +} + +MYBOOL __WINAPI add_column(lprec *lp, REAL *column) +{ + del_splitvars(lp); + return(add_columnex(lp, lp->rows, column, NULL)); +} + +MYBOOL __WINAPI str_add_column(lprec *lp, char *col_string) +{ + int i; + MYBOOL ret = TRUE; + REAL *aCol; + char *p, *newp; + + allocREAL(lp, &aCol, lp->rows + 1, FALSE); + p = col_string; + + for(i = 0; i <= lp->rows; i++) { + aCol[i] = (REAL) strtod(p, &newp); + if(p == newp) { + report(lp, IMPORTANT, "str_add_column: Bad string %s\n", p); + lp->spx_status = DATAIGNORED; + ret = FALSE; + break; + } + else + p = newp; + } + if(lp->spx_status != DATAIGNORED) + ret = add_column(lp, aCol); + FREE(aCol); + return( ret ); +} + +MYBOOL __WINAPI del_column(lprec *lp, int column) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "del_column: Column %d out of range\n", column); + return(FALSE); + } + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "del_column: Cannot delete column while in row entry mode.\n"); + return(FALSE); + } +#endif + + if((lp->var_is_free != NULL) && (lp->var_is_free[column] > 0)) + del_column(lp, lp->var_is_free[column]); /* delete corresponding split column (is always after this column) */ + + varmap_delete(lp, lp->rows+column, -1); + shift_coldata(lp, column, -1); +#ifdef Paranoia + if(is_BasisReady(lp) && (lp->Extrap == 0) && !verifyBasis(lp)) + report(lp, SEVERE, "del_column: Invalid basis detected at column %d (%d)\n", column, lp->columns); +#endif + + return(TRUE); +} + +void __WINAPI set_simplextype(lprec *lp, int simplextype) +{ + lp->simplex_strategy = simplextype; +} + +int __WINAPI get_simplextype(lprec *lp) +{ + return(lp->simplex_strategy); +} + +void __WINAPI set_preferdual(lprec *lp, MYBOOL dodual) +{ + if(dodual & TRUE) + lp->simplex_strategy = SIMPLEX_DUAL_DUAL; + else + lp->simplex_strategy = SIMPLEX_PRIMAL_PRIMAL; +} + +void __WINAPI set_bounds_tighter(lprec *lp, MYBOOL tighten) +{ + lp->tighten_on_set = tighten; +} +MYBOOL __WINAPI get_bounds_tighter(lprec *lp) +{ + return(lp->tighten_on_set); +} + +MYBOOL __WINAPI lp_solve_set_upbo(lprec *lp, int column, REAL value) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "lp_solve_set_upbo: Column %d out of range\n", column); + return(FALSE); + } +#endif + +#ifdef DoBorderRounding + if(fabs(value) < lp->infinite) + value = roundToPrecision(value, lp->epsvalue); +#endif + value = scaled_value(lp, value, lp->rows + column); + if(lp->tighten_on_set) { + if(value < lp->orig_lowbo[lp->rows + column]) { + report(lp, IMPORTANT, "lp_solve_set_upbo: Upperbound must be >= lowerbound\n"); + return(FALSE); + } + if(value < lp->orig_upbo[lp->rows + column]) { + lp->doRebase = TRUE; + lp->orig_upbo[lp->rows + column] = value; + } + } + else + { + lp->doRebase = TRUE; + if(value > lp->infinite) + value = lp->infinite; + lp->orig_upbo[lp->rows + column] = value; + } + return(TRUE); +} + +REAL __WINAPI get_upbo(lprec *lp, int column) +{ + REAL value; + +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "get_upbo: Column %d out of range\n", column); + return(0); + } +#endif + value = lp->orig_upbo[lp->rows + column]; + value = unscaled_value(lp, value, lp->rows + column); + return(value); +} + +MYBOOL __WINAPI lp_solve_set_lowbo(lprec *lp, int column, REAL value) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "lp_solve_set_lowbo: Column %d out of range\n", column); + return(FALSE); + } +#endif + +#ifdef DoBorderRounding + if(fabs(value) < lp->infinite) + value = roundToPrecision(value, lp->epsvalue); +#endif + value = scaled_value(lp, value, lp->rows + column); + if(lp->tighten_on_set) { + if(value > lp->orig_upbo[lp->rows + column]) { + report(lp, IMPORTANT, "lp_solve_set_lowbo: Upper bound must be >= lower bound\n"); + return(FALSE); + } + if((value < 0) || (value > lp->orig_lowbo[lp->rows + column])) { + lp->doRebase = TRUE; + lp->orig_lowbo[lp->rows + column] = value; + } + } + else + { + lp->doRebase = TRUE; + if(value < -lp->infinite) + value = -lp->infinite; + lp->orig_lowbo[lp->rows + column] = value; + } + return(TRUE); +} + +REAL __WINAPI get_lowbo(lprec *lp, int column) +{ + REAL value; + +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "get_lowbo: Column %d out of range\n", column); + return(0); + } +#endif + value = lp->orig_lowbo[lp->rows + column]; + value = unscaled_value(lp, value, lp->rows + column); + return(value); +} + +MYBOOL __WINAPI set_bounds(lprec *lp, int column, REAL lower, REAL upper) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "set_bounds: Column %d out of range\n", column); + return(FALSE); + } +#endif + + if(lp->scaling_used) { + lower = scaled_value(lp, lower, lp->rows + column); + upper = scaled_value(lp, upper, lp->rows + column); + } + if(lower > upper) { + report(lp, IMPORTANT, "set_bounds: Column %d upper bound must be >= lower bound\n", + column); + return(FALSE); + } + + lp->doRebase = TRUE; + if(lower < -lp->infinite) + lower = -lp->infinite; + if(upper > lp->infinite) + upper = lp->infinite; + + lp->orig_lowbo[lp->rows+column] = lower; + lp->orig_upbo[lp->rows+column] = upper; + + return(TRUE); +} + +MYBOOL get_bounds(lprec *lp, int column, REAL *lower, REAL *upper) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "get_bounds: Column %d out of range", column); + return(FALSE); + } +#endif + + if(lower != NULL) + *lower = get_lowbo(lp, column); + if(upper != NULL) + *upper = get_upbo(lp, column); + + return(TRUE); +} + +MYBOOL __WINAPI lp_solve_set_int(lprec *lp, int column, MYBOOL must_be_int) +{ +#ifdef Paranoia + if((column > lp->columns) || (column < 1)) { + report(lp, IMPORTANT, "lp_solve_set_int: Column %d out of range\n", column); + return(FALSE); + } +#endif + + if((lp->must_be_int[column] & ISINTEGER) != 0) { + lp->int_count--; + lp->must_be_int[column] &= !ISINTEGER; + } + if(must_be_int) { + lp->must_be_int[column] |= ISINTEGER; + lp->int_count++; + if(lp->columns_scaled && !is_integerscaling(lp)) + unscale_columns(lp); + } + return(TRUE); +} + +MYBOOL __WINAPI is_int(lprec *lp, int column) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "is_int: Column %d out of range\n", column); + return(FALSE); + } +#endif + + return((lp->must_be_int[column] & ISINTEGER) != 0); +} + +MYBOOL __WINAPI is_SOS_var(lprec *lp, int column) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "is_SOS_var: Column %d out of range\n", column); + return(FALSE); + } +#endif + + return((lp->must_be_int[column] & ISSOS) != 0); +} + +int __WINAPI add_SOS(lprec *lp, char *name, int sostype, int priority, int count, int *sosvars, REAL *weights) +{ + SOSrec *SOS; + int k; + +#ifdef Paranoia + if((sostype < 1) || (count < 0)) { + report(lp, IMPORTANT, "add_SOS: Invalid SOS type definition %d\n", sostype); + return(FALSE); + } +#endif + if(sostype > 3) + report(lp, DETAILED, "add_SOS: High-order SOS selected (%d)\n", sostype); + + /* Make size in the list to handle another SOS record */ + if(lp->SOS == NULL) + lp->SOS = create_SOSgroup(lp); + + /* Create and append SOS to list */ + SOS = create_SOSrec(lp->SOS, name, sostype, priority, count, sosvars, weights); + k = append_SOSgroup(lp->SOS, SOS); + + return(k); +} + +STATIC int add_GUB(lprec *lp, char *name, int priority, int count, int *gubvars) +{ + SOSrec *GUB; + int k; + +#ifdef Paranoia + if(count < 0) { + report(lp, IMPORTANT, "add_GUB: Invalid GUB member count %d\n", count); + return(FALSE); + } +#endif + + /* Make size in the list to handle another GUB record */ + if(lp->GUB == NULL) + lp->GUB = create_SOSgroup(lp); + + /* Create and append GUB to list */ + GUB = create_SOSrec(lp->GUB, name, 1, priority, count, gubvars, NULL); + GUB->isGUB = TRUE; + k = append_SOSgroup(lp->GUB, GUB); + + return(k); +} + +MYBOOL __WINAPI set_binary(lprec *lp, int column, MYBOOL must_be_bin) +{ + MYBOOL status = FALSE; + +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "set_binary: Column %d out of range\n", column); + return( status ); + } +#endif + status = lp_solve_set_int(lp, column, must_be_bin); + if(status && must_be_bin) + status = set_bounds(lp, column, 0, 1); + return( status ); +} + +MYBOOL __WINAPI is_binary(lprec *lp, int column) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "is_binary: Column %d out of range\n", column); + return(FALSE); + } +#endif + + return((MYBOOL) (((lp->must_be_int[column] & ISINTEGER) != 0) && + (get_lowbo(lp, column) == 0) && + (fabs(get_upbo(lp, column) - 1) < lp->epsprimal))); +} + +MYBOOL __WINAPI set_free(lprec *lp, int column) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "set_free: Column %d out of range\n", column); + return( FALSE ); + } +#endif + return( set_bounds(lp, column, -lp->infinite, lp->infinite) ); +} + +MYBOOL __WINAPI is_free(lprec *lp, int column) +{ + MYBOOL test; + +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "is_free: Column %d out of range\n", column); + return(FALSE); + } +#endif + + test = is_splitvar(lp, column); + if(!test) { + column += lp->rows; + test = (MYBOOL) ((lp->orig_lowbo[column] <= -lp->infinite) && + (lp->orig_upbo[column] >= lp->infinite)); + } + return( test ); +} + +MYBOOL __WINAPI is_negative(lprec *lp, int column) +{ + +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "is_negative: Column %d out of range\n", column); + return( FALSE ); + } +#endif + + column += lp->rows; + return( (MYBOOL) ((lp->orig_upbo[column] <= 0) && + (lp->orig_lowbo[column] < 0)) ); +} + +MYBOOL __WINAPI set_var_weights(lprec *lp, REAL *weights) +{ + if(lp->var_priority != NULL) { + FREE(lp->var_priority); + } + if(weights != NULL) { + int n; + allocINT(lp, &lp->var_priority, lp->columns_alloc, FALSE); + for(n = 0; n < lp->columns; n++) { + lp->var_priority[n] = n+1; + } + n = sortByREAL(lp->var_priority, weights, lp->columns, 0, FALSE); + } + return(TRUE); +} + +MYBOOL set_varpriority(lprec *lp) +/* Template for experimental automatic variable ordering/priority setting */ +{ + MYBOOL Status = FALSE; + + if(FALSE && + (lp->var_priority == NULL) && + (SOS_count(lp) == 0)) { + + REAL *rcost; + REAL holdval; + int i; + + allocREAL(lp, &rcost, lp->columns+1, FALSE); + + /* Measure the distance from being integer */ + for(i = 1; i <= lp->columns; i++) { + holdval = lp->best_solution[lp->rows+i]; + rcost[i] = -fabs(holdval - (floor(holdval)+ceil(holdval)) / 2); +/* rcost[i] *= mat_getitem(lp, 0, i); */ +/* rcost[i] *= lp->upbo[lp->rows+i] - lp->lowbo[lp->rows+i]; */ + } + + /* Establish the MIP variable priorities */ + set_var_weights(lp, rcost+1); + + FREE(rcost); + Status = TRUE; + } + + return( Status ); +} + +int __WINAPI get_var_priority(lprec *lp, int column) +{ +#ifdef Paranoia + if(column > lp->columns || column < 1) { + report(lp, IMPORTANT, "get_var_priority: Column %d out of range\n", column); + return(FALSE); + } +#endif + + if(lp->var_priority == NULL) + return(column); + else + return(lp->var_priority[column-1]); +} + +MYBOOL __WINAPI set_semicont(lprec *lp, int column, MYBOOL must_be_sc) +{ +#ifdef Paranoia + if((column > lp->columns) || (column < 1)) { + report(lp, IMPORTANT, "set_semicont: Column %d out of range\n", column); + return(FALSE); + } +#endif + + if(lp->var_is_sc[column] != 0) { + lp->sc_count--; + lp->must_be_int[column] &= !ISSEMI; + } + lp->var_is_sc[column] = must_be_sc; + if(must_be_sc) { + lp->must_be_int[column] |= ISSEMI; + lp->sc_count++; + } + return(TRUE); +} + +MYBOOL __WINAPI is_semicont(lprec *lp, int column) +{ +#ifdef Paranoia + if((column > lp->columns) || (column < 1)) { + report(lp, IMPORTANT, "is_semicont: Column %d out of range\n", column); + return(FALSE); + } +#endif + + return((lp->must_be_int[column] & ISSEMI) != 0); +} + +MYBOOL __WINAPI lp_solve_set_rh(lprec *lp, int row, REAL value) +{ +#ifdef Paranoia + if((row > lp->rows) || (row < 0)) { + report(lp, IMPORTANT, "lp_solve_set_rh: Row %d out of range\n", row); + return(FALSE); + } +#endif + + if(((row == 0) && (!is_maxim(lp))) || + ((row > 0) && is_chsign(lp, row))) /* setting of RHS of OF IS meaningful */ + value = my_flipsign(value); + if(fabs(value) > lp->infinite) { + if (value < 0) + value = -lp->infinite; + else + value = lp->infinite; + } +#ifdef DoBorderRounding + else + value = roundToPrecision(value, lp->epsvalue); +#endif + value = scaled_value(lp, value, row); + lp->orig_rh[row] = value; + lp->doRecompute = TRUE; + return(TRUE); +} + +REAL __WINAPI get_rh(lprec *lp, int row) +{ + REAL value; + +#ifdef Paranoia + if((row > lp->rows) || (row < 0)) { + report(lp, IMPORTANT, "get_rh: Row %d out of range", row); + return(0.0); + } +#endif + + value = lp->orig_rh[row]; + if (((row == 0) && !is_maxim(lp)) || + ((row > 0) && is_chsign(lp, row))) /* setting of RHS of OF IS meaningful */ + value = my_flipsign(value); + value = unscaled_value(lp, value, row); + return(value); +} + +REAL get_rh_upper(lprec *lp, int row) +{ + REAL value, valueR; + + value = lp->orig_rh[row]; + if(is_chsign(lp, row)) { + valueR = lp->orig_upbo[row]; + if(valueR >= lp->infinite) + return(lp->infinite); + value = my_flipsign(value); + value += valueR; + } + value = unscaled_value(lp, value, row); + return(value); +} + +REAL get_rh_lower(lprec *lp, int row) +{ + REAL value, valueR; + + value = lp->orig_rh[row]; + if(is_chsign(lp, row)) + value = my_flipsign(value); + else { + valueR = lp->orig_upbo[row]; + if(valueR >= lp->infinite) + return(-lp->infinite); + value -= valueR; + } + value = unscaled_value(lp, value, row); + return(value); +} + +MYBOOL set_rh_upper(lprec *lp, int row, REAL value) +{ +#ifdef Paranoia + if(row > lp->rows || row < 1) { + report(lp, IMPORTANT, "set_rh_upper: Row %d out of range", row); + return(FALSE); + } +#endif + + /* First scale the value */ + value = scaled_value(lp, value, row); + + /* orig_rh stores the upper bound assuming a < constraint; + If we have a > constraint, we must adjust the range instead */ + if(is_chsign(lp, row)) { + if(fabs(value) >= lp->infinite) + lp->orig_upbo[row] = lp->infinite; + else { +#ifdef Paranoia + if(value + lp->orig_rh[row] < 0) + report(lp, SEVERE, "set_rh_upper: Invalid negative range in row %d\n", + row); +#endif +#ifdef DoBorderRounding + lp->orig_upbo[row] = roundToPrecision(value + lp->orig_rh[row], lp->epsvalue); +#else + lp->orig_upbo[row] = value + lp->orig_rh[row]; +#endif + } + } + else + lp->orig_rh[row] = value; + return(TRUE); +} + +MYBOOL set_rh_lower(lprec *lp, int row, REAL value) +{ +#ifdef Paranoia + if(row > lp->rows || row < 1) { + report(lp, IMPORTANT, "set_rh_lower: Row %d out of range", row); + return(FALSE); + } +#endif + + /* First scale the value */ + value = scaled_value(lp, value, row); + + /* orig_rh stores the upper bound assuming a < constraint; + If we have a < constraint, we must adjust the range instead */ + if(!is_chsign(lp, row)) { + if(value <= -lp->infinite) + lp->orig_upbo[row] = lp->infinite; + else { +#ifdef Paranoia + if(lp->orig_rh[row] - value < 0) + report(lp, SEVERE, "set_rh_lower: Invalid negative range in row %d\n", + row); +#endif +#ifdef DoBorderRounding + lp->orig_upbo[row] = roundToPrecision(lp->orig_rh[row] - value, lp->epsvalue); +#else + lp->orig_upbo[row] = lp->orig_rh[row] - value; +#endif + } + } + else + lp->orig_rh[row] = my_flipsign(value); + return(TRUE); +} + +MYBOOL __WINAPI set_rh_range(lprec *lp, int row, REAL deltavalue) +{ +#ifdef Paranoia + if(row > lp->rows || row < 1) { + report(lp, IMPORTANT, "set_rh_range: Row %d out of range", row); + return(FALSE); + } +#endif + + deltavalue = scaled_value(lp, deltavalue, row); + if(deltavalue > lp->infinite) + deltavalue = lp->infinite; + else if(deltavalue < -lp->infinite) + deltavalue = -lp->infinite; +#ifdef DoBorderRounding + else + deltavalue = roundToPrecision(deltavalue, lp->epsvalue); +#endif + + if(fabs(deltavalue) < lp->epsprimal) { + /* Conversion to EQ */ + lp_solve_set_constr_type(lp, row, EQ); + } + else if(is_constr_type(lp, row, EQ)) { + /* EQ with a non-zero range */ + if(deltavalue > 0) + lp_solve_set_constr_type(lp, row, GE); + else + lp_solve_set_constr_type(lp, row, LE); + lp->orig_upbo[row] = fabs(deltavalue); + } + else { + /* Modify GE/LE ranges */ + lp->orig_upbo[row] = fabs(deltavalue); + } + + return(TRUE); +} + +REAL __WINAPI get_rh_range(lprec *lp, int row) +{ +#ifdef Paranoia + if(row > lp->rows || row < 0) { + report(lp, IMPORTANT, "get_rh_range: row %d out of range\n", row); + return(FALSE); + } +#endif + if(lp->orig_upbo[row] >= lp->infinite) + return(lp->orig_upbo[row]); + else + return(unscaled_value(lp, lp->orig_upbo[row], row)); +} + +void __WINAPI set_rh_vec(lprec *lp, REAL *rh) +{ + int i; + REAL rhi; + + for(i = 1; i <= lp->rows; i++) { + rhi = rh[i]; +#ifdef DoBorderRounding + rhi = roundToPrecision(rhi, lp->epsvalue); +#endif + lp->orig_rh[i] = my_chsign(is_chsign(lp, i), scaled_value(lp, rhi, i)); + } + lp->doRecompute = TRUE; +} + +MYBOOL __WINAPI str_set_rh_vec(lprec *lp, char *rh_string) +{ + int i; + MYBOOL ret = TRUE; + REAL *newrh; + char *p, *newp; + + allocREAL(lp, &newrh, lp->rows + 1, TRUE); + p = rh_string; + + for(i = 1; i <= lp->rows; i++) { + newrh[i] = (REAL) strtod(p, &newp); + if(p == newp) { + report(lp, IMPORTANT, "str_set_rh_vec: Bad string %s\n", p); + lp->spx_status = DATAIGNORED; + ret = FALSE; + break; + } + else + p = newp; + } + if(!(lp->spx_status == DATAIGNORED)) + set_rh_vec(lp, newrh); + FREE(newrh); + return( ret ); +} + +void __WINAPI set_sense(lprec *lp, MYBOOL maximize) +{ + maximize = (maximize == TRUE); + if(is_maxim(lp) != maximize) { + mat_multrow(lp->matA, 0, -1); + lp->orig_rh[0] = my_flipsign(lp->orig_rh[0]); + lp->doInvert = TRUE; + lp->doRecompute = TRUE; + } + if(maximize) + lp->row_type[0] = ROWTYPE_OFMAX; + else + lp->row_type[0] = ROWTYPE_OFMIN; +} + +void __WINAPI lp_solve_set_maxim(lprec *lp) +{ + set_sense(lp, TRUE); +} + +void __WINAPI lp_solve_set_minim(lprec *lp) +{ + set_sense(lp, FALSE); +} + +MYBOOL __WINAPI is_maxim(lprec *lp) +{ + return( (MYBOOL) ((lp->row_type[0] & ROWTYPE_CHSIGN) == ROWTYPE_GE) ); +} + +MYBOOL __WINAPI lp_solve_set_constr_type(lprec *lp, int row, int con_type) +{ + MYBOOL oldchsign; + +#ifdef Paranoia + if(row > lp->rows+1 || row < 1) { + report(lp, IMPORTANT, "lp_solve_set_constr_type: Row %d out of range\n", row); + return( FALSE ); + } +#endif + /* Prepare for a new row */ + if((row > lp->rows) && !append_rows(lp, row-lp->rows)) + return( FALSE ); + + /* Update the constraint type data */ + if(is_constr_type(lp, row, EQ)) + lp->equalities--; + + if((con_type & ROWTYPE_CONSTRAINT) == EQ) { + lp->equalities++; + lp->orig_upbo[row] = 0; + } + else if(((con_type & LE) > 0) || ((con_type & GE) > 0)) + lp->orig_upbo[row] = lp->infinite; + else { + report(lp, IMPORTANT, "lp_solve_set_constr_type: Constraint type %d not implemented (row %d)\n", + con_type, row); + return( FALSE ); + } + + /* Change the signs of the row, if necessary */ + oldchsign = is_chsign(lp, row); + lp->row_type[row] = con_type; + if(oldchsign != is_chsign(lp, row)) { + mat_multrow(lp->matA, row, -1); + if(lp->orig_rh[row] != 0) + lp->orig_rh[row] *= -1; + lp->doRecompute = TRUE; + } + lp->basis_valid = FALSE; + lp->doInvert = TRUE; + + return( TRUE ); +} + +STATIC MYBOOL is_chsign(lprec *lp, int rownr) +{ + return( (MYBOOL) ((lp->row_type[rownr] & ROWTYPE_CONSTRAINT) == ROWTYPE_CHSIGN) ); +} + +MYBOOL __WINAPI is_constr_type(lprec *lp, int row, int mask) +{ +#ifdef Paranoia + if(row < 0 || row > lp->rows) { + report(lp, IMPORTANT, "is_constr_type: Row %d out of range\n", row); + return( FALSE ); + } +#endif + return( (MYBOOL) ((lp->row_type[row] & ROWTYPE_CONSTRAINT) == mask)); +} + +int __WINAPI get_constr_type(lprec *lp, int row) +{ +#ifdef Paranoia + if(row < 0 || row > lp->rows) { + report(lp, IMPORTANT, "get_constr_type: Row %d out of range\n", row); + return(-1); + } +#endif + return( lp->row_type[row] ); +} + +STATIC REAL get_OF_raw(lprec *lp, int colnr) +{ + REAL holdOF; + + colnr -= lp->rows; + if(colnr <= 0) { + holdOF = 0; +#ifdef Paranoia + if(colnr < 0) + report(lp, SEVERE, "get_OF_raw: Invalid column index %d supplied\n", colnr); +#endif + } + else if(lp->OF_override == NULL) { + holdOF = mat_getitem(lp->matA, 0, colnr); + modifyOF1(lp, colnr+lp->rows, &holdOF, 1.0); + } + else + holdOF = lp->OF_override[colnr]; + + return( holdOF ); +} + +STATIC REAL get_OF_contribution(lprec *lp, int colnr, MYBOOL basic) +{ + REAL holdOF, holdQ; + + if(basic) { + holdQ = lp->rhs[colnr]; + colnr = lp->var_basic[colnr]; + if(!lp->is_lower[colnr]) + holdQ = lp->upbo[colnr] - holdQ; + } + else { + if(lp->is_lower[colnr]) + holdQ = 0; + else + holdQ = lp->upbo[colnr]; + } + + holdOF = get_OF_raw(lp, colnr)*holdQ; + + return( holdOF ); +} + +REAL __WINAPI get_mat(lprec *lp, int row, int column) +{ + REAL value; + int elmnr; + +#ifdef Paranoia + if(row < 0 || row > lp->rows) { + report(lp, IMPORTANT, "get_mat: Row %d out of range", row); + return(0); + } + if(column < 1 || column > lp->columns) { + report(lp, IMPORTANT, "get_mat: Column %d out of range", column); + return(0); + } + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "get_mat: Cannot read a matrix value while in row entry mode.\n"); + return(0); + } +#endif +#ifdef DirectOverrideOF + if((row == 0) && (lp->OF_override != NULL)) { + value = lp->OF_override[column]; + value = my_chsign(is_chsign(lp, row), value); + value = unscaled_mat(lp, value, row, column); + } + else +#endif + { + elmnr = mat_findelm(lp->matA, row, column); + if(elmnr >= 0) { + value = my_chsign(is_chsign(lp, row), lp->matA->col_mat[elmnr].value); + value = unscaled_mat(lp, value, row, column); + } + else + value = 0; + } + return(value); +} + +MATitem *get_mat_item(lprec *lp, int matindex, MYBOOL isrow) +{ + if(isrow) { + MATrec *mat = lp->matA; + return( ROW_MAT_PTR(mat->row_mat[matindex]) ); + } + else + return( &(lp->matA->col_mat[matindex]) ); +} + +REAL get_mat_byindex(lprec *lp, int matindex, MYBOOL isrow) +/* Note that this function does not adjust for sign-changed GT constraints! */ +{ + MATitem *matelem; + matelem = get_mat_item(lp, matindex, isrow); + if(lp->scaling_used) + return( unscaled_mat(lp, (*matelem).value, (*matelem).row_nr, (*matelem).col_nr) ); + else + return( (*matelem).value ); +} + + +MYBOOL __WINAPI get_row(lprec *lp, int row_nr, REAL *row) +{ + int i, ie, j; + MATrec *mat = lp->matA; + +#ifdef Paranoia + if(row_nr <0 || row_nr > lp->rows) { + report(lp, IMPORTANT, "get_row: Row %d out of range\n", row_nr); + return( FALSE ); + } + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "get_row: Cannot return a matrix row while in row entry mode.\n"); + return(FALSE); + } +#endif + + if(!mat_validate(lp->matA)) { + for(i = 1; i <= lp->columns; i++) + row[i] = get_mat(lp,row_nr,i); + } + else { + MYBOOL chsign; + REAL a; + + if(row_nr == 0) + i = 0; + else + i = mat->row_end[row_nr-1]; + ie = mat->row_end[row_nr]; + chsign = is_chsign(lp, row_nr); + MEMCLEAR(row, lp->columns+1); + for(; i < ie; i++) { + j = ROW_MAT_COL(mat->row_mat[i]); + a = get_mat_byindex(lp, i, TRUE); + row[j] = my_chsign(chsign, a); + } + } + return( TRUE ); +} + +MYBOOL __WINAPI get_column(lprec *lp, int col_nr, REAL *column) +{ + int i,ii; + +#ifdef Paranoia + if(col_nr < 1 || col_nr > lp->columns) { + report(lp, IMPORTANT, "get_column: Column %d out of range\n", col_nr); + return( FALSE ); + } + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "get_column: Cannot return a column while in row entry mode.\n"); + return(FALSE); + } +#endif + + MEMCLEAR(column, lp->rows + 1); + for(i = lp->matA->col_end[col_nr - 1]; i < lp->matA->col_end[col_nr]; i++) { + ii = lp->matA->col_mat[i].row_nr; + column[ii] = my_chsign(is_chsign(lp, ii), lp->matA->col_mat[i].value); + column[ii] = unscaled_mat(lp, column[ii], ii, col_nr); + } + return( TRUE ); +} + +STATIC void set_OF_override(lprec *lp, REAL *ofVector) +/* The purpose of this function is to set, or clear if NULL, the + ofVector[0..columns] as the active objective function instead of + the one stored in the A-matrix. See also lag_solve().*/ +{ + lp->OF_override = ofVector; +} + +STATIC void fill_OF_override(lprec *lp) +{ + int i; + REAL *value; + + if(lp->OF_override == NULL) + return; + for(i = 1, value = lp->OF_override+1; i <= lp->columns; i++, value++) { + *value = mat_getitem(lp->matA, 0, i); + modifyOF1(lp, i, value, 1.0); + } +} + +MYBOOL __WINAPI modifyOF1(lprec *lp, int col_nr, REAL *ofValue, REAL mult) +/* Adjust objective function values for primal/dual phase 1, if appropriate */ +{ + static MYBOOL accept; + static int Extrap; + + /* Primal simplex: Set user variables to zero or BigM-scaled */ + accept = TRUE; + Extrap = abs(lp->Extrap); + if(Extrap > 0) { +#ifndef Phase1EliminateRedundant + if(lp->Extrap < 0) { + if(col_nr > lp->sum - Extrap) + accept = FALSE; + } + else +#endif + if((col_nr <= lp->sum - Extrap) || (mult == 0)) { + if((mult == 0) || (lp->bigM == 0)) + accept = FALSE; + else + (*ofValue) /= lp->bigM; + } + } + + /* Dual simplex: Subtract Extrad from objective function values */ + else if((lp->Extrad != 0) && (col_nr > lp->rows)) + *ofValue -= lp->Extrad; + + /* Do scaling and test for zero */ + if(accept) { + (*ofValue) *= mult; + if(fabs(*ofValue) < lp->epsmachine) { + (*ofValue) = 0; + accept = FALSE; + } + } + else + (*ofValue) = 0; + + return( accept ); +} + +STATIC int singleton_column(lprec *lp, int row_nr, REAL *column, int *nzlist, REAL value, int *maxabs) +{ + int nz = 1; + + if(nzlist == NULL) { + MEMCLEAR(column, lp->rows + 1); + column[row_nr] = value; + } + else { + column[nz] = value; + nzlist[nz] = row_nr; + } + + if(maxabs != NULL) + *maxabs = row_nr; + return( nz ); +} + +STATIC int expand_column(lprec *lp, int col_nr, REAL *column, int *nzlist, REAL mult, int *maxabs) +{ + int i, ie, j, maxidx, nzcount; + REAL value, maxval; + MATrec *mat = lp->matA; + MATitem *matelem; + + /* Retrieve a column from the user data matrix A */ + maxval = 0; + maxidx = -1; + if(nzlist == NULL) { + MEMCLEAR(column, lp->rows + 1); + i = mat->col_end[col_nr - 1]; + ie = mat->col_end[col_nr]; + nzcount = i; + for(matelem = mat->col_mat+i; i < ie; i++, matelem++) { + j = (*matelem).row_nr; + value = (*matelem).value; + if(j > 0) { + value *= mult; + if(fabs(value) > maxval) { + maxval = fabs(value); + maxidx = j; + } + } + column[j] = value; + } + nzcount = i - nzcount; + + /* Adjust the objective for phase 1 */ + if(*column != 0) + nzcount--; /* The value could possibly become zero after modifyOF1 */ + if(modifyOF1(lp, lp->rows+col_nr, column, mult)) + nzcount++; + } + else { + nzcount = 0; + i = mat->col_end[col_nr - 1]; + ie = mat->col_end[col_nr]; + matelem = mat->col_mat + i; + + /* Handle possible adjustment to zero OF value */ + if((*matelem).row_nr == 0) { + value = (*matelem).value; + matelem++; + i++; + } + else + value = 0; + if(modifyOF1(lp, lp->rows+col_nr, &value, mult)) { + nzcount++; + nzlist[nzcount] = 0; + column[nzcount] = value; + } + for(; i < ie; i++, matelem++) { + j = (*matelem).row_nr; + value = (*matelem).value * mult; + nzcount++; + nzlist[nzcount] = j; + column[nzcount] = value; + if(fabs(value) > maxval) { + maxval = fabs(value); + maxidx = nzcount; + } + } + } + + if(maxabs != NULL) + *maxabs = maxidx; + return( nzcount ); +} + + +/* Retrieve a column vector from the data matrix [1..rows, rows+1..rows+columns]; + needs __WINAPI call model since it may be called from BFPs */ +int __WINAPI obtain_column(lprec *lp, int varin, REAL *pcol, int *nzlist, int *maxabs) +{ + int colnz; + REAL value; + + value = my_chsign(lp->is_lower[varin], -1); + if(varin > lp->rows) { + colnz = varin - lp->rows; + colnz = expand_column(lp, colnz, pcol, nzlist, value, maxabs); + } + else + colnz = singleton_column(lp, varin, pcol, nzlist, value, maxabs); + + return(colnz); +} + + +/* Collection of wrapper functions for XLI callbacks; these need + a fixed and known call model, defined to be __WINAPI */ +static lprec * __WINAPI _make_lp(int rows, int columns) +{ + return(lp_solve_make_lp(rows, columns)); +} + +static void __WINAPI _delete_lp(lprec *lp) +{ + lp_solve_delete_lp(lp); +} + +static void __WINAPI _set_sense(lprec *lp, MYBOOL maximize) +{ + set_sense(lp, maximize); +} + +static MYBOOL __WINAPI _set_lp_name(lprec *lp, char *lpname) +{ + return( set_lp_name(lp, lpname) ); +} + +static MYBOOL __WINAPI _set_row_name(lprec *lp, int varno, char *varname) +{ + return( set_row_name(lp, varno, varname) ); +} + +static MYBOOL __WINAPI _set_col_name(lprec *lp, int varno, char *varname) +{ + return( set_col_name(lp, varno, varname) ); +} + +static MYBOOL __WINAPI _add_columnex(lprec *lp, int count, REAL *column, int *rowno) +{ + return(add_columnex(lp, count, column, rowno)); +} + +static MYBOOL __WINAPI _set_constr_type(lprec *lp, int row, int con_type) +{ + return(lp_solve_set_constr_type(lp, row, con_type)); +} + +static MYBOOL __WINAPI _set_rh(lprec *lp, int row, REAL value) +{ + return(lp_solve_set_rh(lp, row, value)); +} + +static MYBOOL __WINAPI _set_rh_range(lprec *lp, int row, REAL deltavalue) +{ + return(set_rh_range(lp, row, deltavalue)); +} + +static MYBOOL __WINAPI _set_add_rowmode(lprec *lp, MYBOOL turnon) +{ + return(set_add_rowmode(lp, turnon)); +} + +static MYBOOL __WINAPI _set_obj_fnex(lprec *lp, int count, REAL *row, int *colno) +{ + return(set_obj_fnex(lp, count, row, colno)); +} + +static MYBOOL __WINAPI _add_constraintex(lprec *lp, int count, REAL *row, int *rowno, int constr_type, REAL rh) +{ + return(add_constraintex(lp, count, row, rowno, constr_type, rh)); +} + +static MYBOOL __WINAPI _set_upbo(lprec *lp, int column, REAL value) +{ + return(lp_solve_set_upbo(lp, column, value)); +} + +static MYBOOL __WINAPI _set_lowbo(lprec *lp, int column, REAL value) +{ + return(lp_solve_set_lowbo(lp, column, value)); +} + +static MYBOOL __WINAPI _set_free(lprec *lp, int column) +{ + return(set_free(lp, column)); +} + +static MYBOOL __WINAPI _set_int(lprec *lp, int column, MYBOOL must_be_int) +{ + return(lp_solve_set_int(lp, column, must_be_int)); +} + +static MYBOOL __WINAPI _set_semicont(lprec *lp, int column, MYBOOL must_be_sc) +{ + return(set_semicont(lp, column, must_be_sc)); +} + +/* GENERAL INVARIANT CALLBACK FUNCTIONS */ +MYBOOL set_callbacks(lprec *lp) +{ + /* Assign API functions to lp structure (mainly for XLIs) */ + lp->add_column = add_column; + lp->add_columnex = add_columnex; + lp->add_constraint = add_constraint; + lp->add_constraintex = add_constraintex; + lp->add_lag_con = add_lag_con; + lp->add_SOS = add_SOS; + lp->column_in_lp = column_in_lp; + lp->default_basis = default_basis; + lp->del_column = del_column; + lp->del_constraint = del_constraint; + lp->delete_lp = lp_solve_delete_lp; + lp->free_lp = free_lp; + lp->get_anti_degen = get_anti_degen; + lp->get_basis = get_basis; + lp->get_basiscrash = get_basiscrash; + lp->get_bb_depthlimit = get_bb_depthlimit; + lp->get_bb_floorfirst = get_bb_floorfirst; + lp->get_bb_rule = get_bb_rule; + lp->get_bounds_tighter = get_bounds_tighter; + lp->get_break_at_value = get_break_at_value; + lp->get_col_name = get_col_name; + lp->get_column = get_column; + lp->get_constr_type = get_constr_type; + lp->get_constraints = get_constraints; + lp->get_dual_solution = get_dual_solution; + lp->get_epsb = get_epsb; + lp->get_epsd = get_epsd; + lp->get_epsel = get_epsel; + lp->get_epsilon = get_epsilon; + lp->get_epsperturb = get_epsperturb; + lp->get_epspivot = get_epspivot; + lp->get_improve = get_improve; + lp->get_infinite = get_infinite; + lp->get_lambda = get_lambda; + lp->get_lowbo = get_lowbo; + lp->get_lp_index = get_lp_index; + lp->get_lp_name = get_lp_name; + lp->get_Lrows = get_Lrows; + lp->get_mat = get_mat; + lp->get_max_level = get_max_level; + lp->get_maxpivot = get_maxpivot; + lp->get_mip_gap = get_mip_gap; + lp->get_multiprice = get_multiprice; + lp->get_Ncolumns = get_Ncolumns; + lp->get_negrange = get_negrange; + lp->get_Norig_columns = get_Norig_columns; + lp->get_Norig_rows = get_Norig_rows; + lp->get_Nrows = get_Nrows; + lp->get_obj_bound = get_obj_bound; + lp->get_objective = get_objective; + lp->get_orig_index = get_orig_index; + lp->get_origcol_name = get_origcol_name; + lp->get_origrow_name = get_origrow_name; + lp->get_partialprice = get_partialprice; + lp->get_pivoting = get_pivoting; + lp->get_presolve = get_presolve; + lp->get_primal_solution = get_primal_solution; + lp->get_print_sol = get_print_sol; + lp->get_PseudoCosts = get_PseudoCosts; + lp->get_ptr_constraints = get_ptr_constraints; + lp->get_ptr_dual_solution = get_ptr_dual_solution; + lp->get_ptr_lambda = get_ptr_lambda; + lp->get_ptr_primal_solution = get_ptr_primal_solution; + lp->get_ptr_sensitivity_obj = get_ptr_sensitivity_obj; + lp->get_ptr_sensitivity_rhs = get_ptr_sensitivity_rhs; + lp->get_ptr_variables = get_ptr_variables; + lp->get_rh = get_rh; + lp->get_rh_range = get_rh_range; + lp->get_row = get_row; + lp->get_row_name = get_row_name; + lp->get_scalelimit = get_scalelimit; + lp->get_scaling = get_scaling; + lp->get_sensitivity_obj = get_sensitivity_obj; + lp->get_sensitivity_rhs = get_sensitivity_rhs; + lp->get_simplextype = get_simplextype; + lp->get_solutioncount = get_solutioncount; + lp->get_solutionlimit = get_solutionlimit; + lp->get_splitnegvars = get_splitnegvars; + lp->get_statustext = get_statustext; + lp->get_timeout = get_timeout; + lp->get_total_iter = get_total_iter; + lp->get_total_nodes = get_total_nodes; + lp->get_upbo = get_upbo; + lp->get_var_branch = get_var_branch; + lp->get_var_dualresult = get_var_dualresult; + lp->get_var_primalresult = get_var_primalresult; + lp->get_var_priority = get_var_priority; + lp->get_variables = get_variables; + lp->get_verbose = get_verbose; + lp->get_working_objective = get_working_objective; + lp->has_BFP = has_BFP; + lp->has_XLI = has_XLI; + lp->is_add_rowmode = is_add_rowmode; + lp->is_anti_degen = is_anti_degen; + lp->is_binary = is_binary; + lp->is_break_at_first = is_break_at_first; + lp->is_constr_type = is_constr_type; + lp->is_debug = is_debug; + lp->is_feasible = is_feasible; + lp->is_free = is_free; + lp->is_int = is_int; + lp->is_integerscaling = is_integerscaling; + lp->is_lag_trace = is_lag_trace; + lp->is_maxim = is_maxim; + lp->is_nativeBFP = is_nativeBFP; + lp->is_nativeXLI = is_nativeXLI; + lp->is_negative = is_negative; + lp->is_piv_mode = is_piv_mode; + lp->is_piv_rule = is_piv_rule; + lp->is_presolve = is_presolve; + lp->is_scalemode = is_scalemode; + lp->is_scaletype = is_scaletype; + lp->is_semicont = is_semicont; + lp->is_SOS_var = is_SOS_var; + lp->is_trace = is_trace; + lp->lp_solve_version = lp_solve_version; + lp->make_lp = lp_solve_make_lp; + lp->print_constraints = print_constraints; + lp->print_debugdump = print_debugdump; + lp->print_duals = print_duals; + lp->print_lp = lp_solve_print_lp; + lp->print_objective = print_objective; + lp->print_scales = print_scales; + lp->print_solution = print_solution; + lp->print_str = print_str; + lp->print_tableau = print_tableau; + lp->put_abortfunc = put_abortfunc; + lp->put_logfunc = put_logfunc; + lp->put_msgfunc = put_msgfunc; + lp->read_lp = read_lp; + lp->read_LP = read_LP; + lp->read_lpt = read_lpt; + lp->read_LPT = read_LPT; + lp->read_mps = read_mps; + lp->read_MPS = read_MPS; + lp->read_freemps = read_freemps; + lp->read_freeMPS = read_freeMPS; + lp->read_XLI = read_XLI; + lp->reset_basis = reset_basis; + lp->set_add_rowmode = set_add_rowmode; + lp->set_anti_degen = set_anti_degen; + lp->set_basis = set_basis; + lp->set_basiscrash = set_basiscrash; + lp->set_bb_depthlimit = set_bb_depthlimit; + lp->set_bb_floorfirst = set_bb_floorfirst; + lp->set_bb_rule = set_bb_rule; + lp->set_BFP = set_BFP; + lp->set_binary = set_binary; + lp->set_bounds = set_bounds; + lp->set_bounds_tighter = set_bounds_tighter; + lp->set_break_at_first = set_break_at_first; + lp->set_break_at_value = set_break_at_value; + lp->set_col_name = set_col_name; + lp->set_constr_type = lp_solve_set_constr_type; + lp->set_debug = set_debug; + lp->set_epsb = set_epsb; + lp->set_epsd = set_epsd; + lp->set_epsel = set_epsel; + lp->set_epsilon = set_epsilon; + lp->set_epsperturb = set_epsperturb; + lp->set_epspivot = set_epspivot; + lp->set_free = set_free; + lp->set_improve = set_improve; + lp->set_infinite = set_infinite; + lp->set_int = lp_solve_set_int; + lp->set_lag_trace = set_lag_trace; + lp->set_lowbo = lp_solve_set_lowbo; + lp->set_lp_name = set_lp_name; + lp->set_mat = lp_solve_set_mat; + lp->set_maxim = lp_solve_set_maxim; + lp->set_maxpivot = set_maxpivot; + lp->set_minim = lp_solve_set_minim; + lp->set_mip_gap = set_mip_gap; + lp->set_multiprice = set_multiprice; + lp->set_negrange = set_negrange; + lp->set_obj = set_obj; + lp->set_obj_bound = set_obj_bound; + lp->set_obj_fn = set_obj_fn; + lp->set_obj_fnex = set_obj_fnex; + lp->set_outputfile = set_outputfile; + lp->set_outputstream = set_outputstream; + lp->set_partialprice = set_partialprice; + lp->set_pivoting = set_pivoting; + lp->set_preferdual = set_preferdual; + lp->set_presolve = set_presolve; + lp->set_print_sol = set_print_sol; + lp->set_PseudoCosts = set_PseudoCosts; + lp->set_rh = lp_solve_set_rh; + lp->set_rh_range = set_rh_range; + lp->set_rh_vec = set_rh_vec; + lp->set_row_name = set_row_name; + lp->set_scalelimit = set_scalelimit; + lp->set_scaling = set_scaling; + lp->set_semicont = set_semicont; + lp->set_sense = set_sense; + lp->set_simplextype = set_simplextype; + lp->set_solutionlimit = set_solutionlimit; + lp->set_splitnegvars = set_splitnegvars; + lp->set_timeout = set_timeout; + lp->set_trace = set_trace; + lp->set_upbo = lp_solve_set_upbo; + lp->set_var_branch = set_var_branch; + lp->set_var_weights = set_var_weights; + lp->set_verbose = set_verbose; + lp->set_XLI = set_XLI; + lp->solve = lp_solve_solve; + lp->str_add_column = str_add_column; + lp->str_add_constraint = str_add_constraint; + lp->str_add_lag_con = str_add_lag_con; + lp->str_set_obj_fn = str_set_obj_fn; + lp->str_set_rh_vec = str_set_rh_vec; + lp->time_elapsed = time_elapsed; + lp->unscale = unscale; + lp->write_lp = write_lp; + lp->write_lpt = write_lpt; + lp->write_mps = write_mps; + lp->write_freemps = write_freemps; + lp->write_XLI = write_XLI; + + /* Utility functions (mainly for BFPs) */ + lp->userabort = userabort; + lp->report = report; + lp->get_nonzeros = get_nonzeros; + lp->set_basisvar = setBasisVar; + lp->get_lpcolumn = obtain_column; + lp->get_basiscolumn = getBasisColumn; + lp->modifyOF1 = modifyOF1; + lp->invert = invert; + + return( TRUE ); +} + +/* SUPPORT FUNCTION FOR BASIS FACTORIZATION PACKAGES */ +MYBOOL __WINAPI has_BFP(lprec *lp) +{ + return( is_nativeBFP(lp) +#if LoadInverseLib == TRUE + || (MYBOOL) (lp->hBFP != NULL) +#endif + ); +} + +MYBOOL __WINAPI is_nativeBFP(lprec *lp) +{ +#ifdef ExcludeNativeInverse + return( FALSE ); +#elif LoadInverseLib == TRUE + return( (MYBOOL) (lp->hBFP == NULL) ); +#else + return( TRUE ); +#endif +} + +MYBOOL __WINAPI set_BFP(lprec *lp, char *filename) +/* (Re)mapping of basis factorization variant methods is done here */ +{ + MYBOOL result = TRUE; + +#if LoadInverseLib == TRUE + if(lp->hBFP != NULL) { + #ifdef WIN32 + FreeLibrary(lp->hBFP); + #else + dlclose(lp->hBFP); + #endif + lp->hBFP = NULL; + } +#endif + + if(filename == NULL) { + if(!is_nativeBFP(lp)) + return( FALSE ); +#ifndef ExcludeNativeInverse + lp->bfp_name = bfp_name; + lp->bfp_compatible = bfp_compatible; + lp->bfp_free = bfp_free; + lp->bfp_resize = bfp_resize; + lp->bfp_nonzeros = bfp_nonzeros; + lp->bfp_memallocated = bfp_memallocated; + lp->bfp_restart = bfp_restart; + lp->bfp_mustrefactorize = bfp_mustrefactorize; + lp->bfp_preparefactorization = bfp_preparefactorization; + lp->bfp_factorize = bfp_factorize; + lp->bfp_finishupdate = bfp_finishupdate; + lp->bfp_ftran_normal = bfp_ftran_normal; + lp->bfp_ftran_prepare = bfp_ftran_prepare; + lp->bfp_btran_normal = bfp_btran_normal; + lp->bfp_status = bfp_status; + lp->bfp_indexbase = bfp_indexbase; + lp->bfp_rowoffset = bfp_rowoffset; + lp->bfp_pivotmax = bfp_pivotmax; + lp->bfp_init = bfp_init; + lp->bfp_pivotalloc = bfp_pivotalloc; + lp->bfp_colcount = bfp_colcount; + lp->bfp_canresetbasis = bfp_canresetbasis; + lp->bfp_finishfactorization = bfp_finishfactorization; + lp->bfp_prepareupdate = bfp_prepareupdate; + lp->bfp_pivotRHS = bfp_pivotRHS; + lp->bfp_btran_double = bfp_btran_double; + lp->bfp_efficiency = bfp_efficiency; + lp->bfp_pivotvector = bfp_pivotvector; + lp->bfp_pivotcount = bfp_pivotcount; + lp->bfp_refactcount = bfp_refactcount; + lp->bfp_isSetI = bfp_isSetI; +#endif + } + else { +#if LoadInverseLib == TRUE + #ifdef WIN32 + /* Get a handle to the Windows DLL module. */ + lp->hBFP = LoadLibrary(filename); + + /* If the handle is valid, try to get the function addresses. */ + if(lp->hBFP == NULL) { + set_BFP(lp, NULL); + result = FALSE; + } + else { + lp->bfp_compatible = (BFPbool_lpintint *) + GetProcAddress(lp->hBFP, "bfp_compatible"); + if((lp->bfp_compatible != NULL) && lp->bfp_compatible(lp, BFPVERSION, MAJORVERSION)) { + + lp->bfp_name = (BFPchar *) + GetProcAddress(lp->hBFP, "bfp_name"); + lp->bfp_free = (BFP_lp *) + GetProcAddress(lp->hBFP, "bfp_free"); + lp->bfp_resize = (BFP_lpint *) + GetProcAddress(lp->hBFP, "bfp_resize"); + lp->bfp_nonzeros = (BFPint_lpbool *) + GetProcAddress(lp->hBFP, "bfp_nonzeros"); + lp->bfp_memallocated = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_memallocated"); + lp->bfp_restart = (BFPbool_lp *) + GetProcAddress(lp->hBFP, "bfp_restart"); + lp->bfp_mustrefactorize = (BFPbool_lp *) + GetProcAddress(lp->hBFP, "bfp_mustrefactorize"); + lp->bfp_preparefactorization = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_preparefactorization"); + lp->bfp_factorize = (BFPint_lpintintbool *) + GetProcAddress(lp->hBFP, "bfp_factorize"); + lp->bfp_finishupdate = (BFPbool_lpbool *) + GetProcAddress(lp->hBFP, "bfp_finishupdate"); + lp->bfp_ftran_normal = (BFP_lprealint *) + GetProcAddress(lp->hBFP, "bfp_ftran_normal"); + lp->bfp_ftran_prepare = (BFP_lprealint *) + GetProcAddress(lp->hBFP, "bfp_ftran_prepare"); + lp->bfp_btran_normal = (BFP_lprealint *) + GetProcAddress(lp->hBFP, "bfp_btran_normal"); + lp->bfp_status = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_status"); + lp->bfp_indexbase = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_indexbase"); + lp->bfp_rowoffset = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_rowoffset"); + lp->bfp_pivotmax = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_pivotmax"); + lp->bfp_init = (BFPbool_lpintint *) + GetProcAddress(lp->hBFP, "bfp_init"); + lp->bfp_pivotalloc = (BFP_lpint *) + GetProcAddress(lp->hBFP, "bfp_pivotalloc"); + lp->bfp_colcount = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_colcount"); + lp->bfp_canresetbasis = (BFPbool_lp *) + GetProcAddress(lp->hBFP, "bfp_canresetbasis"); + lp->bfp_finishfactorization = (BFP_lp *) + GetProcAddress(lp->hBFP, "bfp_finishfactorization"); + lp->bfp_prepareupdate = (BFPlreal_lpintintreal *) + GetProcAddress(lp->hBFP, "bfp_prepareupdate"); + lp->bfp_pivotRHS = (BFPreal_lplrealreal *) + GetProcAddress(lp->hBFP, "bfp_pivotRHS"); + lp->bfp_btran_double = (BFP_lprealintrealint *) + GetProcAddress(lp->hBFP, "bfp_btran_double"); + lp->bfp_efficiency = (BFPreal_lp *) + GetProcAddress(lp->hBFP, "bfp_efficiency"); + lp->bfp_pivotvector = (BFPrealp_lp *) + GetProcAddress(lp->hBFP, "bfp_pivotvector"); + lp->bfp_pivotcount = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_pivotcount"); + lp->bfp_refactcount = (BFPint_lp *) + GetProcAddress(lp->hBFP, "bfp_refactcount"); + lp->bfp_isSetI = (BFPbool_lp *) + GetProcAddress(lp->hBFP, "bfp_isSetI"); + } + else + result = FALSE; + } + #else + /* First standardize UNIX .SO library name format. */ + char bfpname[260], *ptr; + + strcpy(bfpname, filename); + if((ptr = strrchr(filename, '/')) == NULL) + ptr = filename; + else + ptr++; + bfpname[(int) (ptr - filename)] = 0; + if(strncmp(ptr, "lib", 3)) + strcat(bfpname, "lib"); + strcat(bfpname, ptr); + if(strcmp(bfpname + strlen(bfpname) - 3, ".so")) + strcat(bfpname, ".so"); + + /* Get a handle to the module. */ + lp->hBFP = dlopen(bfpname, RTLD_LAZY); + + /* If the handle is valid, try to get the function addresses. */ + if(lp->hBFP == NULL) { + set_BFP(lp, NULL); + result = FALSE; + } + else { + lp->bfp_compatible = (BFPbool_lpintint *) + dlsym(lp->hBFP, "bfp_compatible"); + if((lp->bfp_compatible != NULL) && lp->bfp_compatible(lp, BFPVERSION, MAJORVERSION)) { + + lp->bfp_name = (BFPchar *) + dlsym(lp->hBFP, "bfp_name"); + lp->bfp_free = (BFP_lp *) + dlsym(lp->hBFP, "bfp_free"); + lp->bfp_resize = (BFP_lpint *) + dlsym(lp->hBFP, "bfp_resize"); + lp->bfp_nonzeros = (BFPint_lpbool *) + dlsym(lp->hBFP, "bfp_nonzeros"); + lp->bfp_memallocated = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_memallocated"); + lp->bfp_restart = (BFPbool_lp *) + dlsym(lp->hBFP, "bfp_restart"); + lp->bfp_mustrefactorize = (BFPbool_lp *) + dlsym(lp->hBFP, "bfp_mustrefactorize"); + lp->bfp_preparefactorization = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_preparefactorization"); + lp->bfp_factorize = (BFPint_lpintintbool *) + dlsym(lp->hBFP, "bfp_factorize"); + lp->bfp_finishupdate = (BFPbool_lpbool *) + dlsym(lp->hBFP, "bfp_finishupdate"); + lp->bfp_ftran_normal = (BFP_lprealint *) + dlsym(lp->hBFP, "bfp_ftran_normal"); + lp->bfp_ftran_prepare = (BFP_lprealint *) + dlsym(lp->hBFP, "bfp_ftran_prepare"); + lp->bfp_btran_normal = (BFP_lprealint *) + dlsym(lp->hBFP, "bfp_btran_normal"); + lp->bfp_status = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_status"); + lp->bfp_indexbase = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_indexbase"); + lp->bfp_rowoffset = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_rowoffset"); + lp->bfp_pivotmax = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_pivotmax"); + lp->bfp_init = (BFPbool_lpintint *) + dlsym(lp->hBFP, "bfp_init"); + lp->bfp_pivotalloc = (BFP_lpint *) + dlsym(lp->hBFP, "bfp_pivotalloc"); + lp->bfp_colcount = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_colcount"); + lp->bfp_canresetbasis = (BFPbool_lp *) + dlsym(lp->hBFP, "bfp_canresetbasis"); + lp->bfp_finishfactorization = (BFP_lp *) + dlsym(lp->hBFP, "bfp_finishfactorization"); + lp->bfp_prepareupdate = (BFPlreal_lpintintreal *) + dlsym(lp->hBFP, "bfp_prepareupdate"); + lp->bfp_pivotRHS = (BFPreal_lplrealreal *) + dlsym(lp->hBFP, "bfp_pivotRHS"); + lp->bfp_btran_double = (BFP_lprealintrealint *) + dlsym(lp->hBFP, "bfp_btran_double"); + lp->bfp_efficiency = (BFPreal_lp *) + dlsym(lp->hBFP, "bfp_efficiency"); + lp->bfp_pivotvector = (BFPrealp_lp *) + dlsym(lp->hBFP, "bfp_pivotvector"); + lp->bfp_pivotcount = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_pivotcount"); + lp->bfp_refactcount = (BFPint_lp *) + dlsym(lp->hBFP, "bfp_refactcount"); + lp->bfp_isSetI = (BFPbool_lp *) + dlsym(lp->hBFP, "bfp_isSetI"); + } + else + result = FALSE; + } + #endif +#endif + /* Do validation */ + if(result && + ((lp->bfp_name == NULL) || + (lp->bfp_compatible == NULL) || + (lp->bfp_free == NULL) || + (lp->bfp_resize == NULL) || + (lp->bfp_nonzeros == NULL) || + (lp->bfp_memallocated == NULL) || + (lp->bfp_restart == NULL) || + (lp->bfp_mustrefactorize == NULL) || + (lp->bfp_preparefactorization == NULL) || + (lp->bfp_factorize == NULL) || + (lp->bfp_finishupdate == NULL) || + (lp->bfp_ftran_normal == NULL) || + (lp->bfp_ftran_prepare == NULL) || + (lp->bfp_btran_normal == NULL) || + (lp->bfp_status == NULL) || + (lp->bfp_indexbase == NULL) || + (lp->bfp_rowoffset == NULL) || + (lp->bfp_pivotmax == NULL) || + (lp->bfp_init == NULL) || + (lp->bfp_pivotalloc == NULL) || + (lp->bfp_colcount == NULL) || + (lp->bfp_canresetbasis == NULL) || + (lp->bfp_finishfactorization == NULL) || + (lp->bfp_prepareupdate == NULL) || + (lp->bfp_pivotRHS == NULL) || + (lp->bfp_btran_double == NULL) || + (lp->bfp_efficiency == NULL) || + (lp->bfp_pivotvector == NULL) || + (lp->bfp_pivotcount == NULL) || + (lp->bfp_refactcount == NULL) || + (lp->bfp_isSetI == NULL) + )) { + set_BFP(lp, NULL); + result = FALSE; + } + } + return( result ); +} + + +/* External language interface routines */ +/* DON'T MODIFY */ +lprec * __WINAPI read_XLI(char *xliname, char *modelname, char *dataname, char *options, int verbose) +{ + lprec *lp; + + lp = lp_solve_make_lp(0, 0); + if(lp != NULL) { + if(!set_XLI(lp, xliname)) { + free_lp(&lp); + printf("No valid XLI package selected or available"); + } + else { + lp->source_is_file = TRUE; + lp->verbose = verbose; + if(!lp->xli_readmodel(lp, modelname, dataname, options, verbose)) + free_lp(&lp); + } + } + return( lp ); +} + +MYBOOL __WINAPI write_XLI(lprec *lp, char *filename, char *options, MYBOOL results) +{ + return( has_XLI(lp) && lp->xli_writemodel(lp, filename, options, results) ); +} + +MYBOOL __WINAPI has_XLI(lprec *lp) +{ + return( is_nativeXLI(lp) +#if LoadLanguageLib == TRUE + || (MYBOOL) (lp->hXLI != NULL) +#endif + ); +} + +MYBOOL __WINAPI is_nativeXLI(lprec *lp) +{ +#ifdef ExcludeNativeLanguage + return( FALSE ); +#elif LoadLanguageLib == TRUE + return( (MYBOOL) (lp->hXLI == NULL) ); +#else + return( TRUE ); +#endif +} + +MYBOOL __WINAPI set_XLI(lprec *lp, char *filename) +/* (Re)mapping of external language interface variant methods is done here */ +{ + MYBOOL result = TRUE; + +#if LoadLanguageLib == TRUE + if(lp->hXLI != NULL) { + #ifdef WIN32 + FreeLibrary(lp->hXLI); + #else + dlclose(lp->hXLI); + #endif + lp->hXLI = NULL; + } +#endif + + if(filename == NULL) { + if(!is_nativeXLI(lp)) + return( FALSE ); +#ifndef ExcludeNativeLanguage + lp->xli_name = xli_name; + lp->xli_compatible = xli_compatible; + lp->xli_readmodel = xli_readmodel; + lp->xli_writemodel = xli_writemodel; +#endif + } + else { +#if LoadLanguageLib == TRUE + #ifdef WIN32 + /* Get a handle to the Windows DLL module. */ + lp->hXLI = LoadLibrary(filename); + + /* If the handle is valid, try to get the function addresses. */ + if(lp->hXLI == NULL) { + set_XLI(lp, NULL); + result = FALSE; + } + else { + lp->xli_compatible = (XLIbool_lpintint *) + GetProcAddress(lp->hXLI, "xli_compatible"); + if((lp->xli_compatible != NULL) && lp->xli_compatible(lp, XLIVERSION, MAJORVERSION)) { + + lp->xli_name = (XLIchar *) + GetProcAddress(lp->hXLI, "xli_name"); + lp->xli_readmodel = (XLIbool_lpcharcharcharint *) + GetProcAddress(lp->hXLI, "xli_readmodel"); + lp->xli_writemodel = (XLIbool_lpcharcharbool *) + GetProcAddress(lp->hXLI, "xli_writemodel"); + } + else + result = FALSE; + } + #else + /* First standardize UNIX .SO library name format. */ + char xliname[260], *ptr; + + strcpy(xliname, filename); + if((ptr = strrchr(filename, '/')) == NULL) + ptr = filename; + else + ptr++; + xliname[(int) (ptr - filename)] = 0; + if(strncmp(ptr, "lib", 3)) + strcat(xliname, "lib"); + strcat(xliname, ptr); + if(strcmp(xliname + strlen(xliname) - 3, ".so")) + strcat(xliname, ".so"); + + /* Get a handle to the module. */ + lp->hXLI = dlopen(xliname, RTLD_LAZY); + + /* If the handle is valid, try to get the function addresses. */ + if(lp->hXLI == NULL) { + set_XLI(lp, NULL); + result = FALSE; + } + else { + lp->xli_compatible = (XLIbool_lpintint *) + dlsym(lp->hXLI, "xli_compatible"); + if((lp->xli_compatible != NULL) && lp->xli_compatible(lp, XLIVERSION, MAJORVERSION)) { + + lp->xli_name = (XLIchar *) + dlsym(lp->hXLI, "xli_name"); + lp->xli_readmodel = (XLIbool_lpcharcharcharint *) + dlsym(lp->hXLI, "xli_readmodel"); + lp->xli_writemodel = (XLIbool_lpcharcharbool *) + dlsym(lp->hXLI, "xli_writemodel"); + } + else + result = FALSE; + } + #endif +#endif + /* Do validation */ + if(result && + ((lp->xli_name == NULL) || + (lp->xli_compatible == NULL) || + (lp->xli_readmodel == NULL) || + (lp->xli_writemodel == NULL) + )) { + set_XLI(lp, NULL); + result = FALSE; + } + } + return( result ); +} + + +int __WINAPI getBasisColumn(lprec *lp, int j, int rn[], double bj[]) +/* This routine returns sparse vectors for all basis + columns, including the OF dummy (index 0) and slack columns */ +{ + int k, matbase = lp->bfp_indexbase(lp); + + /* Convert index of slack and user columns */ + j -= lp->bfp_rowoffset(lp); + if((j > 0) && !lp->bfp_isSetI(lp)) + j = lp->var_basic[j]; + + /* Process OF dummy and slack columns (always at lower bound) */ + if(j <= lp->rows) { + rn[1] = j + matbase; + bj[1] = 1.0; + k = 1; + } + /* Process user columns (negated if at lower bound) */ + else { + k = obtain_column(lp, j, bj, rn, NULL); + if(matbase != 0) + for(j = 1; j <= k; j++) + rn[j] += matbase; + } + + return( k ); +} + +MYBOOL __WINAPI get_primal_solution(lprec *lp, REAL *pv) +{ + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_primal_solution: Not a valid basis"); + return(FALSE); + } + + MEMCOPY(pv, lp->best_solution, lp->sum + 1); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_primal_solution(lprec *lp, REAL **pv) +{ + *pv = lp->best_solution; + return(TRUE); +} + +MYBOOL __WINAPI get_dual_solution(lprec *lp, REAL *rc) +{ + REAL *duals; + MYBOOL ret; + + if(!lp->basis_valid) { + report(lp, CRITICAL, "get_dual_solution: Not a valid basis"); + return(FALSE); + } + + ret = get_ptr_sensitivity_rhs(lp, &duals, NULL, NULL); + + if(ret) + MEMCOPY(rc, duals - 1, lp->sum + 1); + return(ret); +} + +MYBOOL __WINAPI get_ptr_dual_solution(lprec *lp, REAL **rc) +{ + MYBOOL ret; + + ret = get_ptr_sensitivity_rhs(lp, rc, NULL, NULL); + if(ret) + (*rc)--; + return(ret); +} + +MYBOOL __WINAPI get_lambda(lprec *lp, REAL *lambda) +{ + if(!lp->basis_valid || (get_Lrows(lp) == 0)) { + report(lp, CRITICAL, "get_lambda: Not a valid basis"); + return(FALSE); + } + + MEMCOPY(lambda, lp->lambda+1, get_Lrows(lp)); + return(TRUE); +} + +MYBOOL __WINAPI get_ptr_lambda(lprec *lp, REAL **lambda) +{ + *lambda = lp->lambda; + return(TRUE); +} + +int __WINAPI get_orig_index(lprec *lp, int lp_index) +{ + return(lp->varmap_locked ? lp->var_to_orig[lp_index] : lp_index); +} +int __WINAPI get_lp_index(lprec *lp, int orig_index) +{ + return(lp->varmap_locked ? lp->orig_to_var[orig_index] : orig_index); +} + +MYBOOL __WINAPI is_feasible(lprec *lp, REAL *values, REAL threshold) +/* Recommend to use threshold = lp->epspivot */ +{ + int i, j, elmnr, ie; + REAL *this_rhs, dist; + MATrec *mat = lp->matA; + MATitem *matelem; + + for(i = lp->rows + 1; i <= lp->sum; i++) { + if(values[i - lp->rows] < unscaled_value(lp, lp->orig_lowbo[i], i) + || values[i - lp->rows] > unscaled_value(lp, lp->orig_upbo[i], i)) { + if(!((lp->var_is_sc[i - lp->rows]>0) && (values[i - lp->rows]==0))) + return(FALSE); + } + } + + allocREAL(lp, &this_rhs, lp->rows + 1, TRUE); + for(j = 1; j <= lp->columns; j++) { + elmnr = mat->col_end[j - 1]; + ie = mat->col_end[j]; + for(matelem = mat->col_mat + elmnr; + elmnr < ie; elmnr++, matelem++) { + i = (*matelem).row_nr; + this_rhs[i] += unscaled_mat(lp, (*matelem).value, i, j); + } + } + for(i = 1; i <= lp->rows; i++) { + dist = lp->orig_rh[i] - this_rhs[i]; + my_roundzero(dist, threshold); /* Converted to variable by KE 20030722 */ + if((lp->orig_upbo[i] == 0 && dist != 0) || dist < 0) { + FREE(this_rhs); + return(FALSE); + } + } + FREE(this_rhs); + return(TRUE); +} + + +int __WINAPI column_in_lp(lprec *lp, REAL *testcolumn) +{ + int i, j, colnr = 0; + int nz, ident = 1; + REAL value; + + for(nz = 0, i = 0; i <= lp->rows; i++) + if(fabs(testcolumn[i]) > lp->epsvalue) nz++; + + for(i = 1; (i <= lp->columns) && (ident); i++) { + ident = nz; + for(j = lp->matA->col_end[i - 1]; (j < lp->matA->col_end[i]) && (ident >= 0); j++, ident--) { + value = lp->matA->col_mat[j].value; + if(is_chsign(lp, lp->matA->col_mat[j].row_nr)) + value = my_flipsign(value); + value = unscaled_mat(lp, value, lp->matA->col_mat[j].row_nr, i); + value -= testcolumn[lp->matA->col_mat[j].row_nr]; + if(fabs(value) > lp->epsvalue) + ident = -1; + } + if(ident == 0) + colnr = i; + } + return( colnr ); +} + + +MYBOOL __WINAPI set_lp_name(lprec *lp, char *name) +{ + if (name == NULL) { + FREE(lp->lp_name); + lp->lp_name = NULL; + } + else { + allocCHAR(lp, &lp->lp_name, (int) (strlen(name) + 1), AUTOMATIC); + strcpy(lp->lp_name, name); + } + return(TRUE); +} + +char * __WINAPI get_lp_name(lprec *lp) +{ + return(lp->lp_name); +} + +STATIC MYBOOL init_rowcol_names(lprec *lp) +{ + if(!lp->names_used) { + lp->row_name = (hashelem **) calloc(lp->rows_alloc + 1, sizeof(*lp->row_name)); + lp->col_name = (hashelem **) calloc(lp->columns_alloc + 1, sizeof(*lp->col_name)); + lp->rowname_hashtab = create_hash_table(lp->rows_alloc + 1, 0); + lp->colname_hashtab = create_hash_table(lp->columns_alloc + 1, 1); + lp->names_used = TRUE; + } + return(TRUE); +} + +MYBOOL rename_var(lprec *lp, int varindex, char *new_name, hashelem **list, hashtable **ht) +{ + hashelem *hp; + MYBOOL newitem; + + hp = list[varindex]; + newitem = (MYBOOL) (hp == NULL); + if(newitem) + hp = puthash(new_name, varindex, list, *ht); + else { + hashtable *newht, *oldht; + + allocCHAR(lp, &hp->name, (int) (strlen(new_name) + 1), AUTOMATIC); + strcpy(hp->name, new_name); + oldht = *ht; + newht = copy_hash_table(oldht, list, oldht->size); + *ht = newht; + free_hash_table(oldht); + } + return(newitem); +} + +MYBOOL __WINAPI set_row_name(lprec *lp, int row, char *new_name) +{ +#ifdef Paranoia + if(row < 0 || row > lp->rows+1) { + report(lp, IMPORTANT, "set_row_name: Row %d out of range", row); + return(FALSE); + } +#endif + + /* Prepare for a new row */ + if((row > lp->rows) && !append_rows(lp, row-lp->rows)) + return( FALSE ); + if(!lp->names_used) { + if(!init_rowcol_names(lp)) + return(FALSE); + } + rename_var(lp, row, new_name, lp->row_name, &lp->rowname_hashtab); + + return(TRUE); +} + +char * __WINAPI get_row_name(lprec *lp, int row) +{ +#ifdef Paranoia + if(row < 0 || row > lp->rows+1) { + report(lp, IMPORTANT, "get_row_name: Row %d out of range", row); + return(NULL); + } +#endif + if((lp->var_to_orig != NULL) && lp->wasPresolved) { + if(lp->var_to_orig[row] == 0) + row = -row; + else + row = lp->var_to_orig[row]; + } + return( get_origrow_name(lp, row) ); +} + +char * __WINAPI get_origrow_name(lprec *lp, int row) +{ + MYBOOL newrow; + static char name[50]; + char *ptr; + + newrow = (MYBOOL) (row < 0); + row = abs(row); +#ifdef Paranoia + if(((lp->var_to_orig == NULL) && newrow) || + (row > MAX(lp->rows, lp->orig_rows))) { + report(lp, IMPORTANT, "get_origrow_name: Row %d out of range", row); + return(NULL); + } +#endif + + if(lp->names_used && (lp->row_name[row] != NULL) && + (lp->row_name[row]->name != NULL)) { +#ifdef Paranoia + if(lp->row_name[row]->index != row) + report(lp, SEVERE, "get_origrow_name: Inconsistent row ordinal %d vs %d\n", + row, lp->row_name[row]->index); +#endif + ptr = lp->row_name[row]->name; + } + else { + if(newrow) + sprintf(name, ROWNAMEMASK2, row); + else + sprintf(name, ROWNAMEMASK, row); + ptr = name; + } + return(ptr); +} + +MYBOOL __WINAPI set_col_name(lprec *lp, int column, char *new_name) +{ +#ifdef Paranoia + if(column < 1 || column > lp->columns+1) { + report(lp, IMPORTANT, "set_col_name: Column %d out of range", column); + } +#endif + + if((column > lp->columns) && !append_columns(lp, column-lp->columns)) + return(FALSE); + + if(!lp->names_used) + init_rowcol_names(lp); + rename_var(lp, column, new_name, lp->col_name, &lp->colname_hashtab); + + return(TRUE); +} + +char * __WINAPI get_col_name(lprec *lp, int column) +{ +#ifdef Paranoia + if((column < 1) || (column > lp->columns+1)) { + report(lp, IMPORTANT, "get_col_name: Column %d out of range", column); + return(NULL); + } +#endif + if((lp->var_to_orig != NULL) && lp->wasPresolved) { + if(lp->var_to_orig[lp->rows + column] == 0) + column = -column; + else + column = lp->var_to_orig[lp->rows + column]; + } + return( get_origcol_name(lp, column) ); +} + +char * __WINAPI get_origcol_name(lprec *lp, int column) +{ + MYBOOL newcol; + char *ptr; + static char name[50]; + + newcol = (MYBOOL) (column < 0); + column = abs(column); +#ifdef Paranoia + if(((lp->var_to_orig == NULL) && newcol) || + (column > MAX(lp->columns, lp->orig_columns))) { + report(lp, IMPORTANT, "get_origcol_name: Column %d out of range", column); + return(NULL); + } +#endif + + if(lp->names_used && (lp->col_name[column] != NULL) && (lp->col_name[column]->name != NULL)) { +#ifdef Paranoia + if(lp->col_name[column]->index != column) + report(lp, SEVERE, "get_origcol_name: Inconsistent column ordinal %d vs %d\n", + column, lp->col_name[column]->index); +#endif + ptr = lp->col_name[column]->name; + } + else { + if(newcol) + sprintf((char *) name, COLNAMEMASK2, column); + else + sprintf((char *) name, COLNAMEMASK, column); + ptr = name; + } + return(ptr); +} + +STATIC int MIP_count(lprec *lp) +{ + return( lp->int_count+lp->sc_count+SOS_count(lp) ); +} +STATIC int SOS_count(lprec *lp) +{ + if(lp->SOS == NULL) + return( 0 ); + else + return( lp->SOS->sos_count ); +} +STATIC int GUB_count(lprec *lp) +{ + if(lp->GUB == NULL) + return( 0 ); + else + return( lp->GUB->sos_count ); +} + +STATIC REAL getBoundViolation(lprec *lp, int row_nr) +/* Returns the bound violation of a given basic variable; the return + value is negative if it is below is lower bound, it is positive + if it is greater than the upper bound, and zero otherwise. */ +{ + REAL value, test; + + value = lp->rhs[row_nr]; + row_nr = lp->var_basic[row_nr]; + test = value - my_lowbound(lp->lowbo[row_nr]); + my_roundzero(test, lp->epsprimal); + if(test > 0) { + test = value - lp->upbo[row_nr]; + my_roundzero(test, lp->epsprimal); + if(test < 0) + test = 0; + } + return( test ); +} + +STATIC REAL compute_feasibilitygap(lprec *lp, MYBOOL isdual) +{ + int i; + REAL f; + + f = 0; + if(isdual) { + for(i = 1; i <= lp->rows; i++) + if(lp->rhs[i] < 0) + f -= lp->rhs[i]; + else if(lp->rhs[i] > lp->upbo[lp->var_basic[i]]) + f += lp->rhs[i] - lp->upbo[lp->var_basic[i]]; + } + else { + } + return( f ); +} + +REAL minimumImprovementOF(lprec *lp) +/* This function tries to find a non-zero minimum improvement + if the OF contains all integer variables (logic only applies if we are + looking for a single solution, not possibly several equal-valued ones). +*/ +{ + REAL test, value, multOF, divOF, minABS = lp->infinite; + int OFrow, j, jj, jb, je, pluscount, intcount, OFcount; + MATrec *mat = lp->matA; + + value = 0.0; + if((lp->int_count > 0) && (lp->solutionlimit == 1) && mat_validate(mat)) { + + /* Get OF row starting and ending positions, as well as the first column index */ + jb = 0; + je = mat->row_end[0]; + jj = ROW_MAT_COL(mat->row_mat[0]); + + /* First check if we have an OF hidden in a constraint row; + (this is often done in models defined via branch-and-cut solvers) */ + if((je == 1) && !is_int(lp, jj) && + (mat_collength(mat, jj) == 2) && + (get_constr_type(lp, (OFrow = mat->col_mat[mat->col_end[jj]-1].row_nr)) == EQ)) { + jb = mat->row_end[OFrow-1]; + je = mat->row_end[OFrow]; + multOF = get_mat_byindex(lp, 0, TRUE); + } + /* Otherwise it is a normal OF row */ + else { + OFrow = 0; + jj = -1; + multOF = 1.0; + } + divOF = 1.0; + + /* Find if our (de-facto) OF contains all integers and + pick up the smallest absolute OF value */ + minABS = lp->infinite; + pluscount = 0; + OFcount = 0; + intcount = 0; + for(je--; je >= jb; je--) { + j = ROW_MAT_COL(mat->row_mat[je]); + if(j == jj) { + divOF = get_mat_byindex(lp, je, TRUE); + continue; + } + if(!is_int(lp, j)) + break; + test = get_mat_byindex(lp, je, TRUE); + if(test > 0) + pluscount++; + OFcount++; + test = fabs(test); + minABS = MIN(minABS, test); + + test += test*lp->epsmachine; + test = modf(test, &value); + if(test < lp->epsprimal) + intcount++; + } + value = 0.0; + + /* Do further analysis if the objective is all integer */ + if(je < jb) { + + /* We must check for explicit or implicit ordinality of the OF coefficients */ + je = mat->row_end[OFrow]; + for(je--; je >= jb; je--) { + j = ROW_MAT_COL(mat->row_mat[je]); + if(j == jj) + continue; + test = get_mat_byindex(lp, je, TRUE)/minABS; + test += my_sign(test)*lp->epsmachine; + test = modf(test, &value); + if(fabs(test) > lp->epsprimal) + break; + } + + /* Did we get through all right? */ + if(je < jb) { + if(OFrow == 0) + value = minABS; + else + value = fabs( multOF / divOF * minABS ); + } + else if(intcount == OFcount) + value = 1.0; + else + value = 0.0; + } + } + return( value ); +} + +STATIC REAL feasibilityOffset(lprec *lp, MYBOOL isdual) +{ + int i, j; + REAL f, Extra; + MATrec *mat = lp->matA; + + if(isdual) { +#ifdef UseLegacyExtrad + /* This is the legacy (v3.2-) Extrad logic */ + int varnr, rownr, je; + REAL *drow; + MATrec *mat = lp->matA; + + allocREAL(lp, &drow, lp->rows+1, FALSE); + bsolve(lp, 0, drow, lp->epsprimal, 1.0); + + Extra = 0; + for(i = 1; i <= lp->columns; i++) { + varnr = lp->rows + i; + f = 0; + je = mat->col_end[i]; + for(j = mat->col_end[i - 1]; j < je; j++) + rownr = mat->col_mat[j].row_nr; + if(drow[rownr] != 0) + f += drow[rownr] * mat->col_mat[j].value; + if(f < Extra) + Extra = f; + } + FREE(drow); +#else + /* Set Extra to be the most negative of the objective coefficients. + We effectively subtract Extrad from every element of the objective + row, thereby making the entire objective row non-negative. Note + that this forces dual feasibility! Although it also alters the + objective function, we don't really care about that too much + because we only use the dual algorithm to obtain a primal feasible + solution that we can start the primal algorithm with. Virtually + any non-zero objective function will work with this approach! */ + Extra = lp->obj_mincost; + if(Extra >= lp->infinite) + for(i = 1; i <= lp->columns; i++) { + f = 0; + j = mat->col_end[i - 1]; + /* Since the A-matrix is sorted with the objective function row + at the top we do not need to scan the entire matrix - KE fix 20030801 */ + if((j < mat->col_end[i]) && (mat->col_mat[j].row_nr == 0)) { + f = mat->col_mat[j].value; + if(f < Extra) + Extra = f; + } + } +#endif + +#if 1 + if(Extra >= 0) { + /* Could argue that an OF-shifting could benefit some models via + increased OF sparsity and that setting Extra=0 is not necessary */ + Extra = 0; + } +#endif + lp->obj_mincost = Extra; + } + else { + /* Set Extra to be the index of the most negative of the net RHS coefficients; + this approach can be used in the primal phase 1 when there are no equality + constraints. When there are equality constraints, additional artificial + variables should be introduced when the RHS is negative */ + Extra = 0; + j = 0; + Extra = lp->infinite; + for(i = 1; i <= lp->rows; i++) { + f = lp->rhs[i]; + if(f < Extra) { + Extra = f; + j = i; + } + } + Extra = j; + } + + return(Extra); + +} + +STATIC MYBOOL add_artificial(lprec *lp, int forrownr) +/* This routine is called for each constraint at the start of + primloop and the primal problem is infeasible. Its + purpose is to add artificial variables and associated + objective function values to populate primal phase 1. */ +{ + MYBOOL add; + + /* Make sure we don't add unnecessary artificials, i.e. avoid + cases where the slack variable is enough */ + + add = !isBasisVarFeasible(lp, lp->epspivot, forrownr); + + if(add) { + int *rownr, i, bvar, ii; + REAL *avalue, rhscoef, acoef; + + /* Check the simple case where a slack is basic */ + for(i = 1; i <= lp->rows; i++) { + if(lp->var_basic[i] == forrownr) + break; + } + acoef = 1; + + /* If not, look for any basic user variable that has a + non-zero coefficient in the current constraint row */ + if(i > lp->rows) { + for(i = 1; i <= lp->rows; i++) { + ii = lp->var_basic[i] - lp->rows; + if((ii <= 0) || (ii > (lp->columns-lp->Extrap))) + continue; + ii = mat_findelm(lp->matA, forrownr, ii); + if(ii >= 0) { + acoef = lp->matA->col_mat[ii].value; + break; + } + } + } + bvar = i; + + add = (MYBOOL) (bvar <= lp->rows); + if(add) { + rhscoef = lp->rhs[forrownr]; + + /* Create temporary sparse array storage */ + allocREAL(lp, &avalue, 2, FALSE); + allocINT(lp, &rownr, 2, FALSE); + + /* Set the objective coefficient */ + rownr[0] = 0; + avalue[0] = my_chsign(is_chsign(lp, 0), 1); + + /* Set the constraint row coefficient */ + rownr[1] = forrownr; + avalue[1] = my_chsign(is_chsign(lp, forrownr), my_sign(rhscoef/acoef)); + + /* Add the column of artificial variable data to the user data matrix */ + add_columnex(lp, 2, avalue, rownr); + + /* Free the temporary sparse array storage */ + FREE(rownr); + FREE(avalue); + + /* Now set the artificial variable to be basic */ + lp->is_lower[lp->var_basic[bvar]] = TRUE; + setBasisVar(lp, bvar, lp->sum); + lp->basis_valid = TRUE; + + lp->Extrap++; + } + else { + report(lp, CRITICAL, "add_artificial: Could not find replacement basis variable for row %d\n", + forrownr); + lp->basis_valid = FALSE; + } + + } + + return(add); + +} + +STATIC int get_artificialRow(lprec *lp, int colnr) +{ + colnr = lp->matA->col_end[colnr-1]+1; + colnr = lp->matA->col_mat[colnr].row_nr; + return( colnr ); +} + +STATIC int findAnti_artificial(lprec *lp, int colnr) +/* Find a basic artificial variable to swap against the non-basic slack variable, if possible */ +{ + int i, k, rownr = 0; + + if((lp->Extrap == 0) || (colnr > lp->rows) || !lp->is_basic[colnr]) + return( rownr ); + + for(i = 1; i <= lp->rows; i++) { + k = lp->var_basic[i]; + if((k > lp->sum-abs(lp->Extrap)) && (lp->rhs[i] == 0)) { + k -= lp->rows; + k = lp->matA->col_end[k] + 1; + rownr = lp->matA->col_mat[k].row_nr; +#if 0 + /* Should we find the artificial's slack direct "antibody"? */ + if(rownr == colnr) + break; + rownr = 0; +#endif + } + } + return( rownr ); +} + +STATIC int findBasicArtificial(lprec *lp, int before) +{ + int i = 0; + + if(abs(lp->Extrap) > 0) { + if(before > lp->rows || before <= 1) + i = lp->rows; + else + i = before; + + while((i > 0) && (lp->var_basic[i] <= lp->sum-abs(lp->Extrap))) + i--; + } + + return(i); +} + +STATIC int findBasicFixedvar(lprec *lp, int before) +{ + int i; + + if(before > lp->rows || before <= 1) + i = lp->rows; + else + i = before; + + while((i > 0) && + (((lp->var_basic[i] <= lp->rows) && !is_constr_type(lp, i, EQ)) || + ((lp->var_basic[i] > lp->rows) && !is_fixedvar(lp, lp->var_basic[i])))) + i--; + return(i); +} + +STATIC int find_artificialReplacement(lprec *lp, int rownr, REAL *prow) +/* The logic in this section generally follows Vacek Chvatal: Linear Programming, p. 130 */ +{ + int i, bestindex; + REAL bestvalue; + + /* Solve for "local reduced cost" */ + compute_reducedcosts(lp, TRUE, rownr, prow, NULL, NULL, NULL); + + /* Find a suitably non-singular variable to enter ("most orthogonal") */ + bestindex = 0; + bestvalue = 0; + for(i = 1; i <= lp->sum-abs(lp->Extrap); i++) { + if(!lp->is_basic[i] && !is_fixedvar(lp, i) && + (fabs(prow[i]) > bestvalue)) { + bestindex = i; + bestvalue = fabs(prow[i]); + } + } + + /* Prepare to update inverse and pivot/iterate (compute Bw=a) */ + if(i > lp->sum-abs(lp->Extrap)) + i = 0; + else + fsolve(lp, i, prow, lp->workrowsINT, lp->epsmachine, 1.0, TRUE); + + return( i ); +} +STATIC void eliminate_artificials(lprec *lp, REAL *prow) +{ + int i, j, colnr, rownr, Extrap; + LREAL theta; + + Extrap = abs(lp->Extrap); + for(i = 1; (i <= lp->rows) && (Extrap > 0); i++) { + j = lp->var_basic[i]; + if(j <= lp->sum-Extrap) + continue; + j -= lp->rows; + rownr = get_artificialRow(lp, j); + colnr = find_artificialReplacement(lp, rownr, prow); + theta = 0; +#if 0 + performiteration(lp, rownr, colnr, theta, TRUE); +#else + setBasisVar(lp, rownr, colnr); +#endif + del_column(lp, j); + Extrap--; + } + lp->Extrap = 0; +} +STATIC void clear_artificials(lprec *lp) +{ + int i, j, n, Extrap; + + /* Substitute any basic artificial variable for its slack counterpart */ + n = 0; + Extrap = abs(lp->Extrap); + for(i = 1; (i <= lp->rows) && (n < Extrap); i++) { + j = lp->var_basic[i]; + if(j <= lp->sum-Extrap) + continue; + j = get_artificialRow(lp, j-lp->rows); + setBasisVar(lp, i, j); + n++; + } +#ifdef Paranoia + if(n != lp->Extrap) + report(lp, SEVERE, "clear_artificials: Unable to clear all basic artificial variables\n"); +#endif + + /* Delete any remaining non-basic artificial variables */ + while(Extrap > 0) { + i = lp->sum-lp->rows; + del_column(lp, i); + Extrap--; + } + lp->Extrap = 0; + if(n > 0) { + lp->doInvert = TRUE; + lp->basis_valid = TRUE; + } +} + +STATIC MYBOOL isPhase1(lprec *lp) +{ +#if 0 + return(((lp->simplex_mode & SIMPLEX_PRIMAL_Phase1 != 0) && (lp->Extrap > 0)) || + ((lp->simplex_mode & SIMPLEX_DUAL_Phase1 != 0) && (lp->Extrad != 0))); +#else + return((MYBOOL) ((lp->Extrap > 0) || (lp->Extrad != 0))); +#endif +} + +STATIC MYBOOL feasiblePhase1(lprec *lp, REAL epsvalue) +{ + REAL gap; + MYBOOL test; + + gap = fabs(lp->rhs[0] - lp->orig_rh[0]); + test = (MYBOOL) (gap < epsvalue); + return( test) ; +} + +STATIC MYBOOL isDegenerateBasis(lprec *lp, int basisvar) +{ + int varindex; + + varindex = lp->var_basic[basisvar]; + if((fabs(lp->rhs[basisvar]) < lp->epsprimal) || + (fabs(lp->upbo[varindex]-lp->rhs[basisvar]) < lp->epsprimal)) + return( TRUE ); + else + return( FALSE ); +} + +STATIC MYBOOL isBasisVarFeasible(lprec *lp, REAL tol, int basis_row) +{ + int col; + REAL x; + MYBOOL Ok = TRUE; + MYBOOL doSC = FALSE; + + col = lp->var_basic[basis_row]; + x = lp->rhs[basis_row]; /* The current solution of basic variables stored here! */ + if((x < -tol) || (x > lp->upbo[col]+tol)) + Ok = FALSE; + else if(doSC && (col > lp->rows) && (fabs(lp->var_is_sc[col - lp->rows]) > 0)) { + if((x > tol) && (x < fabs(lp->var_is_sc[col - lp->rows])-tol)) + Ok = FALSE; + } + return( Ok ); +} +STATIC MYBOOL isPrimalFeasible(lprec *lp, REAL tol) +{ + int i; + MYBOOL feasible = TRUE; + +#if 0 + /* Traditional indexing style */ + for(i = 1; (i <= lp->rows) && feasible; i++) + feasible = isBasisVarFeasible(lp, tol, i); +#else + /* Fast array pointer style */ + LREAL *rhsptr; + int *idxptr; + MYBOOL doSC = FALSE; + + for(i = 1, rhsptr = lp->rhs+1, idxptr = lp->var_basic+1; + feasible && (i <= lp->rows); i++, rhsptr++, idxptr++) { +/* if(((*rhsptr) < lp->lowbo[(*idxptr)]-tol) || ((*rhsptr) > lp->upbo[(*idxptr)]+tol)) */ + if(((*rhsptr) < -tol) || ((*rhsptr) > lp->upbo[(*idxptr)]+tol)) + feasible = FALSE; + else if(doSC && ((*idxptr) > lp->rows) && (fabs(lp->var_is_sc[(*idxptr)-lp->rows]) > 0)) { + if(((*rhsptr) > tol) && ((*rhsptr) < fabs(lp->var_is_sc[(*idxptr) - lp->rows])-tol)) + feasible = FALSE; + } + } +#endif + + return(feasible); +} + +STATIC MYBOOL isDualFeasible(lprec *lp, REAL tol) +{ + int i; + MYBOOL feasible = TRUE; + REAL *values, test; + + allocREAL(lp, &values, lp->sum+1, TRUE); + construct_solution(lp, values); + for(i = 1; feasible && (i <= lp->rows); i++) { + test = get_rh_lower(lp, i); + feasible = (MYBOOL) (values[i] > test-tol); + if(feasible) { + test = get_rh_upper(lp, i); + feasible = (MYBOOL) (values[i] < test+tol); + } + } + FREE(values); + + return(feasible); +} + +void __WINAPI default_basis(lprec *lp) +{ + int i; + + /* Set the slack variables to be basic; note that the is_basic[] array + is a helper array filled in presolve() to match var_basic[]. */ + for(i = 1; i <= lp->rows; i++) { + lp->var_basic[i] = i; + lp->is_basic[i] = TRUE; + lp->is_lower[i] = TRUE; + } + lp->var_basic[0] = TRUE; /* Set to signal that this is the default basis */ + + /* Set user variables at their lower bound, including the + dummy slack for the objective "constraint" */ + for(; i <= lp->sum; i++) { + lp->is_basic[i] = FALSE; + lp->is_lower[i] = TRUE; + } + lp->is_lower[0] = TRUE; + + lp->doRebase = TRUE; + lp->doInvert = TRUE; + lp->doRecompute = TRUE; + lp->basis_valid = TRUE; /* Do not re-initialize basis on entering Solve */ +} + +MYBOOL __WINAPI set_basis(lprec *lp, int *bascolumn, MYBOOL nonbasic) /* Added by KE */ +{ + int i,s,k,n; + + /* Initialize (lp->is_basic is set in preprocess); Note that as of v5 and before + it is an lp_solve convention that basic variables are at their lower bounds! + This routine provides for the a possible future case that basic variables + can be upper-bounded. */ + lp->is_lower[0] = TRUE; + for(i = 1; i <= lp->sum; i++) { + lp->is_lower[i] = TRUE; + lp->var_basic[i] = FALSE; + } + + /* Set basic and optionally non-basic variables; + negative index means at lower bound, positive at upper bound */ + if(nonbasic) + n = lp->sum; + else + n = lp->rows; + for(i = 1; i <= n; i++) { + s = bascolumn[i]; + k = abs(s); + if(k <= 0 || k > lp->sum) + return( FALSE ); + if(i <= lp->rows) { + lp->var_basic[i] = k; + lp->is_basic[k] = TRUE; + } + else /* Remove this test if basic variables can be upper-bounded */ + if(s > 0) + lp->is_lower[k] = FALSE; + } + + /* Invalidate basis */ + lp->doInvert = TRUE; + lp->doRebase = TRUE; + lp->doRecompute = TRUE; + lp->basis_valid = TRUE; /* Do not re-initialize basis on entering Solve */ + lp->var_basic[0] = FALSE; /* Set to signal that this is a non-default basis */ + + return( TRUE ); +} + +int __WINAPI get_basiscrash(lprec *lp) +{ + return(lp->crashmode); +} + +void __WINAPI set_basiscrash(lprec *lp, int mode) +{ + lp->crashmode = mode; +} + +void __WINAPI reset_basis(lprec *lp) +{ + lp->basis_valid = FALSE; /* Causes reinversion at next opportunity */ +} + +void __WINAPI get_basis(lprec *lp, int *bascolumn, MYBOOL nonbasic) +{ + int k, i; + + if(!lp->basis_valid) + return; + + *bascolumn = 0; + + /* First save basic variable indexes */ + for(i = 1; i <= lp->rows; i++) { + k = lp->var_basic[i]; + bascolumn[i] = my_chsign(lp->is_lower[k], k); + } + + /* Then optionally save non-basic variable indeces */ + if(nonbasic) { + + for(k = 1; k <= lp->sum; k++) { + if(lp->is_basic[k]) + continue; + bascolumn[i] = my_chsign(lp->is_lower[k], k); + i++; + } + } +} + +STATIC MYBOOL is_BasisReady(lprec *lp) +{ + return( (MYBOOL) (lp->var_basic[0] != AUTOMATIC) ); +} + +STATIC MYBOOL verifyBasis(lprec *lp) +{ + int i, ii, k = 0; + MYBOOL result = FALSE; + + for(i = 1; i <= lp->rows; i++) { + ii = lp->var_basic[i]; + if((ii < 1) || (ii > lp->sum) || !lp->is_basic[ii]) { + k = i; + ii = 0; + goto Done; + } + } + + ii = lp->rows; + for(i = 1; i <= lp->sum; i++) { + if(lp->is_basic[i]) + ii--; + } + result = (MYBOOL) (ii == 0); + +Done: +#if 0 /* For testing */ + if(!result) + ii = 0; +#endif + return(result); +} + +STATIC int __WINAPI setBasisVar(lprec *lp, int basisPos, int enteringCol) +{ + int leavingCol; + + leavingCol = lp->var_basic[basisPos]; + +#ifdef Paranoia + if((basisPos < 1) || (basisPos > lp->rows)) + report(lp, SEVERE, "setBasisVar: Invalid leaving basis position %d specified at iteration %d\n", + basisPos, get_total_iter(lp)); + if((leavingCol < 1) || (leavingCol > lp->sum)) + report(lp, SEVERE, "setBasisVar: Invalid leaving column %d referenced at iteration %d\n", + leavingCol, get_total_iter(lp)); + if((enteringCol < 1) || (enteringCol > lp->sum)) + report(lp, SEVERE, "setBasisVar: Invalid entering column %d specified at iteration %d\n", + enteringCol, get_total_iter(lp)); +#endif + +#ifdef ParanoiaXY + if(!lp->is_basic[leavingCol]) + report(lp, IMPORTANT, "setBasisVar: Leaving variable %d is not basic at iteration %d\n", + leavingCol, get_total_iter(lp)); + if(enteringCol > lp->rows && lp->is_basic[enteringCol]) + report(lp, IMPORTANT, "setBasisVar: Entering variable %d is already basic at iteration %d\n", + enteringCol, get_total_iter(lp)); +#endif + + lp->var_basic[0] = FALSE; /* Set to signal that this is a non-default basis */ + lp->var_basic[basisPos] = enteringCol; + lp->is_basic[leavingCol] = FALSE; + lp->is_basic[enteringCol] = TRUE; + + return(leavingCol); +} + +/* Bounds updating and unloading routines; requires that the + current values for upbo and lowbo are in the original base. */ +STATIC int perturb_bounds(lprec *lp, BBrec *perturbed, MYBOOL includeNONBASIC, MYBOOL includeFIXED) +{ + int i, j, n = 0; + REAL tmpreal, *upbo, *lowbo; + + if(perturbed == NULL) + return( n ); + + /* Compute expanded variable results for comparison purposes */ + construct_solution(lp, NULL); + + /* Map reference bounds to previous state, i.e. cumulate + perturbations in case of persistent problems */ + upbo = perturbed->upbo; + lowbo = perturbed->lowbo; + + /* Set appropriate target variable range */ + if(includeNONBASIC) + i = lp->sum; + else + i = lp->rows; + + /* Perturb (expand) finite basic variable bounds randomly */ + for(; i > 0; i--) { + + /* Direct to actual target variable */ + if(includeNONBASIC) + j = i; + else + j = lp->var_basic[i]; + + /* Don't perturb regular slack variables */ + if((j <= lp->rows) && (lowbo[j] == 0) && (upbo[j] >= lp->infinite)) + continue; + + /* Don't perturb fixed variables if not specified */ + if(!includeFIXED && (upbo[j] == lowbo[j])) + continue; + + /* Lower bound */ + tmpreal = lp->solution[j]-lowbo[j]; + if(tmpreal < lp->epsperturb*RANDSCALE) { + tmpreal = (rand() % RANDSCALE) + 1; /* Added 1 by KE */ + tmpreal *= lp->epsperturb; + lowbo[j] -= tmpreal; + n++; + } + /* Upper bound */ + tmpreal = lp->solution[j]-upbo[j]; + if(tmpreal > -lp->epsperturb*RANDSCALE) { + tmpreal = (rand() % RANDSCALE) + 1; /* Added 1 by KE */ + tmpreal *= lp->epsperturb; + upbo[j] += tmpreal; + n++; + } + } + + /* Make sure we start from scratch */ + lp->doRebase = TRUE; + lp->doInvert = TRUE; + + return( n ); +} + +STATIC MYBOOL restore_bounds(lprec *lp, MYBOOL doupbo, MYBOOL dolowbo) +{ + MYBOOL ok; + BBrec *savedbounds = lp->bb_bounds; + + ok = (MYBOOL) (savedbounds != NULL); + if(ok) { + if(doupbo) + MEMCOPY(savedbounds->upbo, savedbounds->UB_base, lp->sum + 1); + if(dolowbo) + MEMCOPY(savedbounds->lowbo, savedbounds->LB_base, lp->sum + 1); + if(doupbo || dolowbo) { + lp->doRebase = savedbounds->UBzerobased; + lp->doRecompute = TRUE; + } + } + return( ok ); +} + +STATIC MYBOOL impose_bounds(lprec *lp, REAL *upbo, REAL *lowbo) +/* Explicitly set working bounds to given vectors without pushing or popping */ +{ + MYBOOL ok; + + ok = (MYBOOL) ((upbo != NULL) || (lowbo != NULL)); + if(ok) { + if((upbo != NULL) && (upbo != lp->upbo)) + MEMCOPY(lp->upbo, upbo, lp->sum + 1); + if((lowbo != NULL) && (lowbo != lp->lowbo)) + MEMCOPY(lp->lowbo, lowbo, lp->sum + 1); + if(lp->bb_bounds != NULL) + lp->bb_bounds->UBzerobased = FALSE; + lp->doRebase = TRUE; + } + lp->doRecompute = TRUE; + return( ok ); +} + +STATIC MYBOOL validate_bounds(lprec *lp, REAL *upbo, REAL *lowbo) +/* Check if all bounds are Explicitly set working bounds to given vectors without pushing or popping */ +{ + MYBOOL ok; + int i; + + ok = (MYBOOL) ((upbo != NULL) || (lowbo != NULL)); + if(ok) { + for(i = 1; i <= lp->sum; i++) + if((lowbo[i] > upbo[i]) || (lowbo[i] < lp->orig_lowbo[i]) || (upbo[i] > lp->orig_upbo[i])) + break; + ok = (MYBOOL) (i > lp->sum); + } + return( ok ); +} + +STATIC int unload_BB(lprec *lp) +{ + int levelsunloaded = 0; + + if(lp->bb_bounds != NULL) + while(pop_BB(lp->bb_bounds)) + levelsunloaded++; + return( levelsunloaded ); +} + +STATIC basisrec *push_basis(lprec *lp, int *basisvar, MYBOOL *isbasic, MYBOOL *islower) +/* Save the ingoing basis and push it onto the stack */ +{ + basisrec *newbasis = NULL; + + newbasis = (basisrec *) malloc(sizeof(*newbasis)); + if((newbasis != NULL) && + allocMYBOOL(lp, &newbasis->bound_is_lower, lp->sum + 1, FALSE) && + allocMYBOOL(lp, &newbasis->var_is_basic, lp->sum + 1, FALSE) && + allocINT(lp, &newbasis->basis_var, lp->rows + 1, FALSE)) { + + if(islower == NULL) + islower = lp->is_lower; + if(isbasic == NULL) + isbasic = lp->is_basic; + if(basisvar == NULL) + basisvar = lp->var_basic; + + MEMCOPY(newbasis->bound_is_lower, islower, lp->sum + 1); + MEMCOPY(newbasis->var_is_basic, isbasic, lp->sum + 1); + MEMCOPY(newbasis->basis_var, basisvar, lp->rows + 1); + + newbasis->previous = lp->bb_basis; + if(lp->bb_basis == NULL) + newbasis->level = 0; + else + newbasis->level = lp->bb_basis->level + 1; + + lp->bb_basis = newbasis; + } + return( newbasis ); +} + +STATIC MYBOOL compare_basis(lprec *lp) +/* Compares the last pushed basis with the currently active basis */ +{ + int i, j; + MYBOOL same_basis = TRUE; + + if(lp->bb_basis == NULL) + return( FALSE ); + + /* Loop over basis variables until a mismatch (order can be different) */ + i = 1; + while(same_basis && (i <= lp->rows)) { + j = 1; + while(same_basis && (j <= lp->rows)) { + same_basis = (MYBOOL) (lp->bb_basis->basis_var[i] != lp->var_basic[j]); + j++; + } + same_basis = !same_basis; + i++; + } + /* Loop over bound status indicators until a mismatch */ + i = 1; + while(same_basis && (i <= lp->sum)) { + same_basis = (lp->bb_basis->bound_is_lower[i] && lp->is_lower[i]); + i++; + } + + return( same_basis ); +} + +STATIC MYBOOL restore_basis(lprec *lp) +/* Restore values from the previously pushed / saved basis without popping it */ +{ + MYBOOL ok; + + ok = (MYBOOL) (lp->bb_basis != NULL); + if(ok) { + MEMCOPY(lp->var_basic, lp->bb_basis->basis_var, lp->rows + 1); + MEMCOPY(lp->is_basic, lp->bb_basis->var_is_basic, lp->sum + 1); + MEMCOPY(lp->is_lower, lp->bb_basis->bound_is_lower, lp->sum + 1); + lp->doRebase = TRUE; + lp->doInvert = TRUE; + } + return( ok ); +} + +STATIC MYBOOL pop_basis(lprec *lp, MYBOOL restore) +/* Pop / free, and optionally restore the previously "pushed" / saved basis */ +{ + MYBOOL ok; + basisrec *oldbasis; + + ok = (MYBOOL) (lp->bb_basis != NULL); + if(ok) { + oldbasis = lp->bb_basis; + if(oldbasis != NULL) { + lp->bb_basis = oldbasis->previous; + FREE(oldbasis->basis_var); + FREE(oldbasis->var_is_basic); + FREE(oldbasis->bound_is_lower); + FREE(oldbasis); + } + if(restore && (lp->bb_basis != NULL)) + restore_basis(lp); + } + return( ok ); +} + +STATIC int unload_basis(lprec *lp, MYBOOL restorelast) +{ + int levelsunloaded = 0; + + if(lp->bb_basis != NULL) + while(pop_basis(lp, restorelast)) + levelsunloaded++; + return( levelsunloaded ); +} + +STATIC REAL scaled_floor(lprec *lp, int column, REAL value, REAL epsscale) +{ + value = floor(value); + if(value != 0) + if(lp->columns_scaled && is_integerscaling(lp)) { + value = scaled_value(lp, value, column); + if(epsscale != 0) + value += epsscale*lp->epsmachine; +/* value += epsscale*lp->epsprimal; */ +/* value = restoreINT(value, lp->epsint); */ + } + return(value); +} + +STATIC REAL scaled_ceil(lprec *lp, int column, REAL value, REAL epsscale) +{ + value = ceil(value); + if(value != 0) + if(lp->columns_scaled && is_integerscaling(lp)) { + value = scaled_value(lp, value, column); + if(epsscale != 0) + value -= epsscale*lp->epsmachine; +/* value -= epsscale*lp->epsprimal; */ +/* value = restoreINT(value, lp->epsint); */ + } + return(value); +} + +/* Branch and bound variable selection functions */ + +STATIC MYBOOL is_sc_violated(lprec *lp, int column) +{ + int varno; + REAL tmpreal; + + varno = lp->rows+column; + tmpreal = unscaled_value(lp, lp->var_is_sc[column], varno); + return( (MYBOOL) ((tmpreal > 0) && /* it is an (inactive) SC variable... */ + (lp->solution[varno] < tmpreal) && /* ...and the NZ lower bound is violated */ + (lp->solution[varno] > 0)) ); /* ...and the Z lowerbound is violated */ +} +STATIC int find_sc_bbvar(lprec *lp, int *count) +{ + int i, ii, n, bestvar; + int firstsc, lastsc; + REAL hold, holdINT, bestval, OFval, randval, scval; + MYBOOL reversemode, greedymode, randomizemode, + pseudocostmode, pseudocostsel; + + bestvar = 0; + if((lp->sc_count == 0) || (*count > 0)) + return(bestvar); + + reversemode = is_bb_mode(lp, NODE_WEIGHTREVERSEMODE); + greedymode = is_bb_mode(lp, NODE_GREEDYMODE); + randomizemode = is_bb_mode(lp, NODE_RANDOMIZEMODE); + pseudocostmode = is_bb_mode(lp, NODE_PSEUDOCOSTMODE); + pseudocostsel = is_bb_rule(lp, NODE_PSEUDOCOSTSELECT) || + is_bb_rule(lp, NODE_PSEUDONONINTSELECT) || + is_bb_rule(lp, NODE_PSEUDORATIOSELECT); + + bestvar = 0; + bestval = -lp->infinite; + hold = 0; + randval = 1; + firstsc = 0; + lastsc = lp->columns; + + for(n = 1; n <= lp->columns; n++) { + ii = get_var_priority(lp, n); + i = lp->rows + ii; + if(!lp->bb_varactive[ii] && is_sc_violated(lp, ii) && !SOS_is_marked(lp->SOS, 0, ii)) { + + /* Do tallies */ + (*count)++; + lastsc = i; + if(firstsc <= 0) + firstsc = i; + scval = get_PseudoRange(lp, ii, BB_SC); + + /* Select default pricing/weighting mode */ + if(pseudocostmode) + OFval = get_PseudoCost(lp, ii, BB_SC, lp->solution[i]); + else + OFval = my_chsign(is_maxim(lp), get_mat(lp, 0, ii)); + + if(randomizemode) + randval = exp(1.0*rand()/RAND_MAX); + + /* Find the maximum pseudo-cost of a variable (don't apply pseudocostmode here) */ + if(pseudocostsel) { + if(pseudocostmode) + hold = OFval; + else + hold = get_PseudoCost(lp, ii, BB_SC, lp->solution[i]); + hold *= randval; + if(greedymode) { + if(pseudocostmode) /* Override! */ + OFval = my_chsign(is_maxim(lp), get_mat(lp, 0, ii)); + hold *= OFval; + } + hold = my_chsign(reversemode, hold); + } + else + /* Find the variable with the largest sc gap (closest to the sc mean) */ + if(is_bb_rule(lp, NODE_FRACTIONSELECT)) { + hold = modf(lp->solution[i]/scval, &holdINT); + holdINT = hold-1; + if(fabs(holdINT) > hold) + hold = holdINT; + if(greedymode) + hold *= OFval; + hold = my_chsign(reversemode, hold)*scval*randval; + } + else + /* Do first or last violated sc index selection (default) */ + /* if(is_bb_rule(lp, NODE_FIRSTSELECT)) */ + { + if(reversemode) + continue; + else { + bestvar = i; + break; + } + } + + /* Select better, check for ties, and split by proximity to 0.5*var_is_sc */ + if(hold > bestval) { + if( (bestvar == 0) || + (hold > bestval+lp->epsprimal) || + (fabs(modf(lp->solution[i]/scval, &holdINT) - 0.5) < + fabs(modf(lp->solution[bestvar]/get_PseudoRange(lp, bestvar-lp->rows, BB_SC), &holdINT) - 0.5)) ) { + bestval = hold; + bestvar = i; + } + } + } + } + + if(is_bb_rule(lp, NODE_FIRSTSELECT) && reversemode) + bestvar = lastsc; + + return(bestvar); +} + +STATIC int find_sos_bbvar(lprec *lp, int *count, MYBOOL intsos) +{ + int k, i, j, var; + + var = 0; + if((lp->SOS == NULL) || (*count > 0)) + return(var); + + /* Check if the SOS'es happen to already be satisified */ + i = SOS_is_satisfied(lp->SOS, 0, lp->solution); + if(i == 0 || i == -1) + return(-1); + + /* Otherwise identify a SOS variable to enter B&B */ + for(k = 0; k < lp->sos_vars; k++) { + i = lp->sos_priority[k]; +#ifdef Paranoia + if((i < 1) || (i > lp->columns)) + report(lp, SEVERE, "find_sos_bbvar: Invalid SOS variable map %d at %d\n", + i, k); +#endif + j = lp->rows + i; + if(!SOS_is_marked(lp->SOS, 0, i) && !SOS_is_full(lp->SOS, 0, i, FALSE)) { + if(!intsos || is_int(lp, i)) { + (*count)++; + if(var == 0) { + var = j; + break; + } + } + } + } + return(var); +} + +STATIC int find_int_bbvar(lprec *lp, int *count, REAL *lowbo, REAL*upbo, int *firstint, int *lastint) +{ + int i, ii, n, bestvar; + REAL hold, holdINT, bestval, OFval, randval; + MYBOOL reversemode, greedymode, depthfirstmode, randomizemode, + pseudocostmode, pseudocostsel; + + bestvar = 0; + if((lp->int_count == 0) || (*count > 0)) + return(bestvar); + + reversemode = is_bb_mode(lp, NODE_WEIGHTREVERSEMODE); + greedymode = is_bb_mode(lp, NODE_GREEDYMODE); + randomizemode = is_bb_mode(lp, NODE_RANDOMIZEMODE); + depthfirstmode = is_bb_mode(lp, NODE_DEPTHFIRSTMODE) && + (MYBOOL) (lp->bb_level <= lp->int_count); + pseudocostmode = is_bb_mode(lp, NODE_PSEUDOCOSTMODE); + pseudocostsel = is_bb_rule(lp, NODE_PSEUDOCOSTSELECT) || + is_bb_rule(lp, NODE_PSEUDONONINTSELECT) || + is_bb_rule(lp, NODE_PSEUDORATIOSELECT); + + bestvar = 0; + bestval = -lp->infinite; + hold = 0; + randval = 1; + + for(n = 1; n <= lp->columns; n++) { + ii = get_var_priority(lp, n); + i = lp->rows + ii; + if(is_int(lp,ii) && !solution_is_int(lp, i, FALSE)) { + + /* Check if the variable is already fixed */ + if(lowbo[i] == upbo[i]) { + report(lp, IMPORTANT, + "find_int_bbvar: INT var %d is already fixed at %d, but has non-INT value " MPSVALUEMASK "\n", + ii, (int) lowbo[i], lp->solution[i]); + continue; + } + + /* Check if we should skip node candidates that are B&B active already */ + if(depthfirstmode && (lp->bb_varactive[n] != 0)) + continue; + + /* Do the naive detection */ + if((*count) == 0) { /* Pick up the index of the first non-integer INT */ + bestvar = i; + (*firstint) = n; + } + (*count)++; + (*lastint) = n; /* Pick up index of the last non-integer INT */ + + /* Select default pricing/weighting mode */ + if(pseudocostmode) + OFval = get_PseudoCost(lp, ii, BB_INT, lp->solution[i]); + else + OFval = my_chsign(is_maxim(lp), get_mat(lp, 0, ii)); + + if(randomizemode) + randval = exp(1.0*rand()/RAND_MAX); + + /* Find the maximum pseudo-cost of a variable (don't apply pseudocostmode here) */ + if(pseudocostsel) { + if(pseudocostmode) + hold = OFval; + else + hold = get_PseudoCost(lp, ii, BB_INT, lp->solution[i]); + hold *= randval; + if(greedymode) { + if(pseudocostmode) /* Override! */ + OFval = my_chsign(is_maxim(lp), get_mat(lp, 0, ii)); + hold *= OFval; + } + hold = my_chsign(reversemode, hold); + } + else + /* Find the variable with the largest gap to its bounds (distance from being fixed) */ + if(is_bb_rule(lp, NODE_GAPSELECT)) { + hold = lp->solution[i]; + holdINT = hold-unscaled_value(lp, upbo[i], i); + hold -= unscaled_value(lp, lowbo[i], i); + if(fabs(holdINT) > hold) + hold = holdINT; + if(greedymode) + hold *= OFval; + hold = my_chsign(reversemode, hold)*randval; + } + else + /* Find the variable with the largest integer gap (closest to 0.5) */ + if(is_bb_rule(lp, NODE_FRACTIONSELECT)) { + hold = modf(lp->solution[i], &holdINT); + holdINT = hold-1; + if(fabs(holdINT) > hold) + hold = holdINT; + if(greedymode) + hold *= OFval; + hold = my_chsign(reversemode, hold)*randval; + } + else + /* Find the "range", most flexible variable */ + if(is_bb_rule(lp, NODE_RANGESELECT)) { + hold = unscaled_value(lp, upbo[i]-lowbo[i], i); + if(greedymode) + hold *= OFval; + hold = my_chsign(reversemode, hold)*randval; + } + else + /* Do first or last non-int index selection (default) */ + /* if(is_bb_rule(lp, NODE_FIRSTSELECT)) */ + { + if(reversemode) + continue; + else { + bestval = 0; + bestvar = i; + break; + } + } + + /* Select better, check for ties, and split by proximity to 0.5 */ + if(hold > bestval) { + if( (hold > bestval+lp->epsprimal) || + (fabs(modf(lp->solution[i], &holdINT) - 0.5) < + fabs(modf(lp->solution[bestvar], &holdINT) - 0.5)) ) { + bestval = hold; + bestvar = i; + } + } + } + } + + if(is_bb_rule(lp, NODE_FIRSTSELECT) && reversemode) + bestvar = (*lastint); + + return(bestvar); +} + +STATIC BBPSrec *init_PseudoCost(lprec *lp) +{ + int i; + REAL PSinitUP, PSinitLO; + BBPSrec *newitem; + MYBOOL isPSCount; + + /* Allocate memory */ + newitem = (BBPSrec*) malloc(sizeof(*newitem)); + newitem->LOcost = (MATitem*) malloc((lp->columns+1) * sizeof(*newitem->LOcost)); + newitem->UPcost = (MATitem*) malloc((lp->columns+1) * sizeof(*newitem->UPcost)); + + /* Initialize with OF values */ + isPSCount = is_bb_rule(lp, NODE_PSEUDONONINTSELECT); + for(i = 1; i <= lp->columns; i++) { + newitem->LOcost[i].row_nr = 1; + newitem->UPcost[i].row_nr = 1; + + /* Initialize with the plain OF value as conventional usage suggests, or + override in case of pseudo-nonint count strategy */ + PSinitUP = my_chsign(is_maxim(lp), get_mat(lp, 0, i)); + PSinitLO = -PSinitUP; + if(isPSCount) { + /* Set default assumed reduction in the number of non-ints by choosing this variable; + KE changed from 0 on 30 June 2004 and made two-sided selectable. Note that the + typical value range is <0..1>, with a positive bias for an "a priori" assumed + fast-converging (low "MIP-complexity") model. Very hard models may require + negative initialized values for one or both. */ + PSinitUP = 0.1*0; +#if 0 + PSinitUP = my_chsign(PSinitUP < 0, PSinitUP); + PSinitLO = -PSinitUP; +#else + PSinitLO = PSinitUP; +#endif + } + newitem->UPcost[i].value = PSinitUP; + newitem->LOcost[i].value = PSinitLO; + } + newitem->updatelimit = DEF_PSEUDOCOSTUPDATES; + newitem->updatesfinished = 0; + newitem->restartlimit = DEF_PSEUDOCOSTRESTART; + + /* Let the user have an opportunity to initialize pseudocosts */ + if(userabort(lp, MSG_INITPSEUDOCOST)) + lp->spx_status = USERABORT; + + return( newitem ); +} + +STATIC void free_PseudoCost(lprec *lp) +{ + if((lp != NULL) && (lp->bb_PseudoCost != NULL)) { + FREE(lp->bb_PseudoCost->LOcost); + FREE(lp->bb_PseudoCost->UPcost); + FREE(lp->bb_PseudoCost); + } +} + +MYBOOL __WINAPI set_PseudoCosts(lprec *lp, REAL *clower, REAL *cupper, int *updatelimit) +{ + int i; + if((lp->bb_PseudoCost == NULL) || ((clower == NULL) && (cupper == NULL))) + return(FALSE); + for(i = 1; i <= lp->columns; i++) { + if(clower != NULL) + lp->bb_PseudoCost->LOcost[i].value = clower[i]; + if(cupper != NULL) + lp->bb_PseudoCost->UPcost[i].value = cupper[i]; + } + if(updatelimit != NULL) + lp->bb_PseudoCost->updatelimit = *updatelimit; + return(TRUE); +} + +MYBOOL __WINAPI get_PseudoCosts(lprec *lp, REAL *clower, REAL *cupper, int *updatelimit) +{ + int i; + if((lp->bb_PseudoCost == NULL) || ((clower == NULL) && (cupper == NULL))) + return(FALSE); + for(i = 1; i <= lp->columns; i++) { + if(clower != NULL) + clower[i] = lp->bb_PseudoCost->LOcost[i].value; + if(cupper != NULL) + cupper[i] = lp->bb_PseudoCost->UPcost[i].value; + } + if(updatelimit != NULL) + *updatelimit = lp->bb_PseudoCost->updatelimit; + return(TRUE); +} + +STATIC REAL get_PseudoRange(lprec *lp, int mipvar, int varcode) +{ + if(varcode == BB_SC) + return( unscaled_value(lp, lp->var_is_sc[mipvar], lp->rows+mipvar) ); + else + return( 1.0 ); +} + +STATIC void update_PseudoCost(lprec *lp, int mipvar, int varcode, MYBOOL capupper, REAL varsol) +{ + REAL OFsol, uplim; + MATitem *PS; + MYBOOL nonIntSelect = is_bb_rule(lp, NODE_PSEUDONONINTSELECT); + + /* Establish input values; + Note: The pseudocosts are normalized to the 0-1 range! */ + uplim = get_PseudoRange(lp, mipvar, varcode); + varsol = modf(varsol/uplim, &OFsol); + + /* Set reference value according to pseudocost mode */ + if(nonIntSelect) + OFsol = lp->bb_bounds->lastvarcus; /* The count of MIP infeasibilities */ + else + OFsol = lp->solution[0]; /* The problem's objective function value */ + + if(_isnan(varsol)) { + lp->bb_parentOF = OFsol; + return; + } + + /* Point to the applicable (lower or upper) bound */ + if(capupper) { + PS = &lp->bb_PseudoCost->LOcost[mipvar]; + } + else { + PS = &lp->bb_PseudoCost->UPcost[mipvar]; + varsol = 1-varsol; + } + + /* Make adjustment to divisor if we are using the ratio pseudo-cost approach */ + if(is_bb_rule(lp, NODE_PSEUDORATIOSELECT)) + varsol *= capupper; + + /* Compute the update (consider weighting in favor of most recent) */ + mipvar = lp->bb_PseudoCost->updatelimit; + if(((mipvar <= 0) || (PS->row_nr < mipvar)) && + (fabs(varsol) > lp->epspivot)) { + /* We are interested in the change in the MIP measure (contribution to increase + or decrease, as the case may be) and not its last value alone. */ + PS->value = PS->value*PS->row_nr + (lp->bb_parentOF-OFsol) / (varsol*uplim); + PS->row_nr++; + PS->value /= PS->row_nr; + /* Check if we have enough information to restart */ + if(PS->row_nr == mipvar) { + lp->bb_PseudoCost->updatesfinished++; + if(is_bb_mode(lp, NODE_RESTARTMODE) && + (lp->bb_PseudoCost->updatesfinished/(2.0*lp->int_count) > + lp->bb_PseudoCost->restartlimit)) { + lp->bb_break = AUTOMATIC; + lp->bb_PseudoCost->restartlimit *= 2.681; /* Who can figure this one out? */ + if(lp->bb_PseudoCost->restartlimit > 1) + lp->bb_rule -= NODE_RESTARTMODE; + report(lp, NORMAL, "update_PseudoCost: Restarting with updated pseudocosts\n"); + } + } + } + lp->bb_parentOF = OFsol; +} + +STATIC REAL get_PseudoCost(lprec *lp, int mipvar, int vartype, REAL varsol) +{ + REAL hold, uplim; + + uplim = get_PseudoRange(lp, mipvar, vartype); + varsol = modf(varsol/uplim, &hold); + if(_isnan(varsol)) + varsol = 0; + + hold = lp->bb_PseudoCost->LOcost[mipvar].value*varsol + + lp->bb_PseudoCost->UPcost[mipvar].value*(1-varsol); + + return( hold*uplim ); +} + +STATIC int compute_theta(lprec *lp, int rownr, LREAL *theta, REAL HarrisScalar, MYBOOL primal) +/* The purpose of this routine is to compute the non-basic + bound state / value of the leaving variable. */ +{ + static LREAL x; + static REAL lb, ub, eps; + static int colnr; + + colnr = lp->var_basic[rownr]; + x = lp->rhs[rownr]; + lb = 0; + ub = lp->upbo[colnr]; + eps = lp->epsprimal; + + /* Compute theta for the primal simplex */ + if(primal) { + if(*theta > 0) + x += eps*HarrisScalar; + else if(ub < lp->infinite) + x = (x - ub - eps*HarrisScalar); + else { + *theta = lp->infinite * my_sign(*theta); + return( colnr ); + } + } + /* Compute theta for the dual simplex */ + else { + + /* Current value is below or equal to its lower bound */ + if(x < lb+eps) + x = (x - lb + eps*HarrisScalar); + + /* Current value is above or equal to its upper bound */ + else if(x > ub-eps) { + if(ub >= lp->infinite) { + *theta = lp->infinite * my_sign(*theta); + return( colnr ); + } + else + x = (x - ub - eps*HarrisScalar); + } + } + my_roundzero(x, eps); + *theta = x / *theta; + +#ifdef EnforcePositiveTheta + /* Check if we have negative theta due to rounding or an internal error */ + if(*theta < 0) { + if(primal && (ub == lb)) + lp->rhs[rownr] = lb; + else +#ifdef Paranoia + if(*theta < -lp->epspivot) { + report(lp, DETAILED, "compute_theta: Negative theta (%g) not allowed in base-0 version of lp_solve\n", + *theta); + } +#endif + *theta = 0; + } +#endif + + return( colnr ); +} + +STATIC MYBOOL check_degeneracy(lprec *lp, REAL *pcol, int *degencount) +/* Check if the entering column Pi=Inv(B)*a is likely to produce improvement; + (cfr. Istvan Maros: CTOTSM p. 233) */ +{ + int i, ndegen; + REAL *rhs, sdegen, epsmargin = lp->epsprimal; + + sdegen = 0; + ndegen = 0; + rhs = lp->rhs; + for(i = 1; i <= lp->rows; i++) { + rhs++; + pcol++; + if(fabs(*rhs) < epsmargin) { + sdegen += *pcol; + ndegen++; + } + else if(fabs((*rhs)-lp->upbo[lp->var_basic[i]]) < epsmargin) { + sdegen -= *pcol; + ndegen++; + } + } + if(degencount != NULL) + *degencount = ndegen; +/* sdegen += epsmargin*ndegen; */ + return( (MYBOOL) (sdegen <= 0) ); +} + +STATIC MYBOOL performiteration(lprec *lp, int row_nr, int varin, LREAL theta, MYBOOL primal, REAL *prow, int *nzprow) +{ + static int varout; + static REAL pivot, epsmargin, leavingValue, leavingUB, enteringUB; + static MYBOOL leavingToUB, enteringFromUB, enteringIsFixed, leavingIsFixed; + MYBOOL *islower = &(lp->is_lower[varin]); + MYBOOL minitNow = FALSE, minitStatus = ITERATE_MAJORMAJOR; + + if(userabort(lp, MSG_ITERATION)) + return( minitNow ); + +#ifdef Paranoia + if(row_nr > lp->rows) { + if (lp->spx_trace) + report(lp, IMPORTANT, "performiteration: Numeric instability encountered!\n"); + lp->spx_status = NUMFAILURE; + return( FALSE ); + } +#endif + varout = lp->var_basic[row_nr]; +#ifdef Paranoia + if(!lp->is_lower[varout]) + report(lp, SEVERE, "performiteration: Leaving variable %d was at its upper bound at iteration %d\n", + varout, get_total_iter(lp)); +#endif + + /* Theta is the largest change possible (strictest constraint) for the entering + variable (Theta is Chvatal's "t", ref. Linear Programming, pages 124 and 156) */ + lp->current_iter++; + + /* Test if it is possible to do a cheap "minor iteration"; i.e. set entering + variable to its opposite bound, without entering the basis - which is + obviously not possible for fixed variables! */ + epsmargin = lp->epsprimal; + enteringFromUB = !(*islower); + enteringUB = lp->upbo[varin]; + leavingUB = lp->upbo[varout]; + enteringIsFixed = (MYBOOL) (fabs(enteringUB) < epsmargin); + leavingIsFixed = (MYBOOL) (fabs(leavingUB) < epsmargin); +#ifdef Paranoia + if(enteringUB < 0) + report(lp, SEVERE, "performiteration: Negative range for entering variable %d at iteration %d\n", + varin, get_total_iter(lp)); + if(leavingUB < 0) + report(lp, SEVERE, "performiteration: Negative range for leaving variable %d at iteration %d\n", + varout, get_total_iter(lp)); +#endif + + if(!enteringIsFixed) { +/*#define AvoidDegenerateEntering*/ /* KE development code, not ready for production */ +#ifdef AvoidDegenerateEntering + /* Avoid that the entering variable enters at one of its bounds; + simply change its bound and find another entering variable. */ + if((theta == 0) || (fabs(enteringUB - theta) lp->rows) || (fabs(lp->upbo[varin]) < epsmargin)) + continue; + if(prow[ix] < 0) + break; + } + /* Check if we found a valid candidate */ + if(i <= nzprow[0]) { + /* Swap bound on initial entering variables */ + lp->pivotRHS, lp, theta, NULL); + *islower = !(*islower); + /* Then prepare for normal pivoting with alternative entering column */ + varin = ix; + islower = &(lp->is_lower[varin]); + enteringFromUB = !(*islower); + enteringUB = lp->upbo[varin]; + enteringIsFixed = (MYBOOL) (fabs(enteringUB) < epsmargin); + fsolve(lp, varin, pcol, lp->workrowsINT, lp->epsmachine, 1.0, TRUE); + theta = lp->bfp_prepareupdate(lp, row_nr, varin, pcol); + theta = my_chsign(!(*islower), prow[varin]); + compute_theta(lp, row_nr, &theta, 0, primal); + } + } +#endif + +/*#define RelativeBoundTest*/ /* Use relative rather than absolute tests */ +#ifdef RelativeBoundTest + if(my_reldiff(enteringUB,theta) < -epsmargin) { +#else + if(enteringUB - theta < -epsmargin) { +#endif + minitNow = TRUE; + minitStatus = ITERATE_MINORRETRY; + } + } + + /* Process for minor iteration */ + if(minitNow) { + + /* Set the new values */ + theta = my_chsign(theta < 0, MIN(fabs(theta), enteringUB)); + + /* Update the RHS / variable values and do bound-swap */ + pivot = lp->bfp_pivotRHS(lp, theta, NULL); + *islower = !(*islower); + + lp->current_bswap++; + + } + + /* Process for major iteration */ + else { + + /* Update the active pricer for the current pivot */ + updatePricer(lp, row_nr, varin, lp->bfp_pivotvector(lp), prow, nzprow); + + /* Update the current basic variable values */ + pivot = lp->bfp_pivotRHS(lp, theta, NULL); + + /* See if the leaving variable goes directly to its upper bound. */ + leavingValue = lp->rhs[row_nr]; + leavingToUB = (MYBOOL) (leavingValue > leavingUB / 2); + lp->is_lower[varout] = leavingIsFixed || !leavingToUB; + + /* Set the value of the entering varible */ + if(enteringFromUB) { + lp->rhs[row_nr] = enteringUB - theta; + *islower = TRUE; + } + else + lp->rhs[row_nr] = theta; + my_roundzero(lp->rhs[row_nr], epsmargin); + + /* Update basis indeces */ + varout = setBasisVar(lp, row_nr, varin); + + /* Finalize the update in preparation for next major iteration */ + lp->bfp_finishupdate(lp, enteringFromUB); + + } + + /* Show pivot tracking information, if specified */ + if((lp->verbose > NORMAL) && (MIP_count(lp) == 0) && + ((lp->current_iter % MAX(2, lp->rows / 10)) == 0)) + report(lp, NORMAL, "Objective value at iteration %7d is %12g\n", + get_total_iter(lp), lp->rhs[0]); + +#if 0 + if(verify_solution(lp, FALSE, my_if(minitNow, "MINOR", "MAJOR")) >= 0) { + if(minitNow) + pivot = get_OF_raw(lp, varin); + else + pivot = get_OF_raw(lp, varout); + } +#endif +#if 0 + if(minitNow) + report(lp, NORMAL, "I:%5d - minor - %5d ignored, %5d flips from %s with THETA=%g and RHS=%g\n", + get_total_iter(lp), varout, varin, (enteringFromUB ? "UPPER" : "LOWER"), theta, lp->rhs[0]); + else + report(lp, NORMAL, "I:%5d - MAJOR - %5d leaves to %s, %5d enters from %s with THETA=%g and RHS=%g\n", + get_total_iter(lp), varout, (leavingToUB ? "UPPER" : "LOWER"), + varin, (enteringFromUB ? "UPPER" : "LOWER"), theta, lp->rhs[0]); +#endif + + if(lp->spx_trace) { + report(lp, DETAILED, "Theta = " MPSVALUEMASK "\n", theta); + if(minitNow) { + if(!lp->is_lower[varin]) + report(lp, DETAILED, + "performiteration: Variable %d changed to its lower bound at iteration %d (from %g)\n", + varin, get_total_iter(lp), enteringUB); + else + report(lp, DETAILED, + "performiteration: Variable %d changed to its upper bound at iteration %d (to %g)\n", + varin, get_total_iter(lp), enteringUB); + } + else + report(lp, NORMAL, + "performiteration: Variable %d entered basis at iteration %d at " MPSVALUEMASK "\n", + varin, get_total_iter(lp), lp->rhs[row_nr]); + if(!primal) { + pivot = compute_feasibilitygap(lp, (MYBOOL)!primal); + report(lp, NORMAL, "performiteration: Feasibility gap at iteration %d is " MPSVALUEMASK "\n", + get_total_iter(lp), pivot); + } + else + report(lp, NORMAL, + "performiteration: Current objective function value at iteration %d is " MPSVALUEMASK "\n", + get_total_iter(lp), lp->rhs[0]); + } + + return( minitStatus ); + +} /* performiteration */ + +STATIC REAL get_refactfrequency(lprec *lp, MYBOOL final) +{ + int iters, refacts; + + /* Get numerator and divisor information */ + iters = (lp->total_iter+lp->current_iter) - (lp->total_bswap+lp->current_bswap); + refacts = lp->bfp_refactcount(lp); + + /* Return frequency for different cases: + 1) Actual frequency in case final statistic is desired + 2) Dummy if we are in a B&B process + 3) Frequency with added initialization offsets which + are diluted in course of the solution process */ + if(final) + return( (REAL) (iters) / MAX(1,refacts) ); + else if(lp->bb_totalnodes > 0) + return( (REAL) lp->bfp_pivotmax(lp) ); + else + return( (REAL) (lp->bfp_pivotmax(lp)+iters) / (1+refacts) ); +} + +MYBOOL is_fixedvar(lprec *lp, int variable) +{ + return( (MYBOOL) (lp->upbo[variable]-lp->lowbo[variable] < lp->epsprimal) ); +} /* is_fixedvar */ + +STATIC MYBOOL solution_is_int(lprec *lp, int i, MYBOOL checkfixed) +{ +#if 1 + return( (MYBOOL) (isINT(lp, lp->solution[i]) && (!checkfixed || is_fixedvar(lp, i))) ); +#else + if(isINT(lp, lp->solution[i])) { + if(checkfixed) + return(is_fixedvar(lp, i)); + else + return(TRUE); + } + return(FALSE); +#endif +} /* solution_is_int */ + + +MYBOOL __WINAPI set_multiprice(lprec *lp, int multiblockdiv) +{ + /* See if we are resetting multiply priced column structures */ + if(multiblockdiv != lp->multiblockdiv) { + if(multiblockdiv < 1) + multiblockdiv = 1; + lp->multiblockdiv = multiblockdiv; + FREE(lp->multivar); + } + return( TRUE ); +} + +int __WINAPI get_multiprice(lprec *lp, MYBOOL getabssize) +{ + if(lp->multiused == 0) + return( 0 ); + if(getabssize) + return( lp->multisize ); + else + return( lp->multiblockdiv ); +} + +MYBOOL __WINAPI set_partialprice(lprec *lp, int blockcount, int *blockstart, MYBOOL isrow) +{ + int ne, i, items; + partialrec **blockdata; + + /* Determine partial target (rows or columns) */ + if(isrow) + blockdata = &(lp->rowblocks); + else + blockdata = &(lp->colblocks); + + /* See if we are resetting partial blocks */ + ne = 0; + items = IF(isrow, lp->rows, lp->columns); + if(blockcount == 1) + partial_freeBlocks(blockdata); + + /* Set a default block count if this was not specified */ + else if(blockcount <= 0) { + blockstart = NULL; + if(items < DEF_PARTIALBLOCKS*DEF_PARTIALBLOCKS) + blockcount = items / DEF_PARTIALBLOCKS + 1; + else + blockcount = DEF_PARTIALBLOCKS; + ne = items / blockcount; + if(ne * blockcount < items) + ne++; + } + + /* Fill partial block arrays; + Note: These will be modified during preprocess to reflect + presolved columns and the handling of slack variables. */ + if(blockcount > 1) { + + /* Provide for extra block with slack variables in the column mode */ + i = 0; + if(!isrow) + i++; + + /* (Re)-allocate memory */ + if(*blockdata == NULL) + *blockdata = partial_createBlocks(lp, isrow); + allocINT(lp, &((*blockdata)->blockend), blockcount+i+1, AUTOMATIC); + allocINT(lp, &((*blockdata)->blockpos), blockcount+i+1, AUTOMATIC); + + /* Copy the user-provided block start positions */ + if(blockstart != NULL) { + MEMCOPY((*blockdata)->blockend+i, blockstart, blockcount+i+1); + if(!isrow) { + blockcount++; + (*blockdata)->blockend[0] = 1; + for(i = 1; i < blockcount; i++) + (*blockdata)->blockend[i] += lp->rows; + } + } + + /* Fill the block ending positions if they were not specified */ + else { + (*blockdata)->blockpos[0] = 1; + if(ne == 0) { + ne = items / blockcount; + if(ne * blockcount < items) + ne++; + } + i = 1; + if(!isrow) { + (*blockdata)->blockend[i] = (*blockdata)->blockend[i-1]+lp->rows; + blockcount++; + i++; + items += lp->rows; + } + for(; i < blockcount; i++) + (*blockdata)->blockend[i] = (*blockdata)->blockend[i-1]+ne; + + /* Handle terminal block as residual */ + (*blockdata)->blockend[blockcount] = items+1; + if((*blockdata)->blockend[blockcount] >= (*blockdata)->blockend[blockcount-1]) { + (*blockdata)->blockend[blockcount-1] = (*blockdata)->blockend[blockcount]; + blockcount--; + } + } + + /* Fill starting positions */ + for(i = 1; i <= blockcount; i++) + (*blockdata)->blockpos[i] = (*blockdata)->blockend[i-1]; + + /* Reset starting position */ + (*blockdata)->blockcount = blockcount; + (*blockdata)->blocknow = 1; + } + + return( TRUE ); +} /* set_partialprice */ + +void __WINAPI get_partialprice(lprec *lp, int *blockcount, int *blockstart, MYBOOL isrow) +{ + partialrec *blockdata; + + /* Determine partial target (rows or columns) */ + if(isrow) + blockdata = lp->rowblocks; + else + blockdata = lp->colblocks; + + *blockcount = partial_countBlocks(lp, isrow); + if((blockdata != NULL) && (blockstart != NULL)) { + int i = 0, k = *blockcount; + if(!isrow) + i++; + MEMCOPY(blockstart, blockdata->blockend + i, k - i); + if(!isrow) { + k -= i; + for(i = 0; i < k; i++) + blockstart[i] -= lp->rows; + } + } +} + + +/* Solution-related function */ +STATIC void update_reducedcosts(lprec *lp, MYBOOL isdual, int leave_nr, int enter_nr, REAL *prow, REAL *drow) +{ + /* "Fast" update of the dual reduced cost vector; note that it must be called + after the pivot operation and only applies to a major "true" iteration */ + int i; + REAL hold; + + if(isdual) { + hold = -drow[enter_nr]/prow[enter_nr]; + for(i=1; i <= lp->sum; i++) + if(!lp->is_basic[i]) { + if(i == leave_nr) + drow[i] = hold; + else { + drow[i] += hold*prow[i]; + my_roundzero(drow[i], lp->epsmachine); + } + } + } + else + report(lp, SEVERE, "update_reducedcosts: Cannot update primal reduced costs!\n"); +} + +STATIC void compute_reducedcosts(lprec *lp, MYBOOL isdual, int row_nr, REAL *prow, int *nzprow, + REAL *drow, int *nzdrow) +{ +/* REAL epsvalue = lp->epsmachine; */ + REAL epsvalue = lp->epsdual; + + if(isdual) { + bsolve_xA2(lp, row_nr, prow, epsvalue, nzprow, /* Calculate net sensitivity given a leaving variable */ + 0, drow, epsvalue, nzdrow); /* Calculate the net objective function values */ + } + else { + int rc_range = XRESULT_RC; + /* Do the phase 1 logic */ + if(lp->Extrap != 0) { + REAL *vtemp; + allocREAL(lp, &vtemp, lp->sum+1, FALSE); + bsolve(lp, 0, vtemp, NULL, epsvalue*DOUBLEROUND, 1.0); + prod_xA(lp, SCAN_SLACKVARS+SCAN_USERVARS+ +/* SCAN_PARTIALBLOCK+ */ + USE_NONBASICVARS+ + OMIT_FIXED, + vtemp, NULL, rc_range, epsvalue, 1.0, + drow, nzdrow); + FREE(vtemp); + } + /* Do the phase 2 logic */ + else { + bsolve(lp, 0, drow, NULL, epsvalue*DOUBLEROUND, 1.0); +#ifdef UseSparseReducedCost + prod_xA(lp, SCAN_SLACKVARS+SCAN_USERVARS+ +#else + prod_xA(lp, SCAN_USERVARS+ +#endif +/* SCAN_PARTIALBLOCK+ */ + USE_NONBASICVARS+ + OMIT_FIXED, + drow, NULL, rc_range, epsvalue, 1.0, + drow, nzdrow); + } + } +#ifdef Paranoia + if(FALSE && lp->spx_trace) { + if(prow != NULL) + blockWriteREAL(lp->outstream, "compute_reducedcosts: prow", prow, 1, lp->sum); + if(drow != NULL) + blockWriteREAL(lp->outstream, "compute_reducedcosts: drow", drow, 1, lp->sum); + } +#endif +} + +STATIC void construct_solution(lprec *lp, REAL *target) +{ + int i, j, basi; + REAL f, epsvalue = lp->epsprimal; + REAL *solution; + MATitem *matelm; + + if(target == NULL) + solution = lp->solution; + else + solution = target; + + /* Initialize OF and slack variables. */ + for(i = 0; i <= lp->rows; i++) { +#ifdef LegacySlackDefinition + if(i == 0) + solution[i] = unscaled_value(lp, -lp->orig_rh[i], i); + else + solution[i] = 0; +#else + solution[i] = lp->orig_rh[i]; + if((i > 0) && !lp->is_basic[i] && !lp->is_lower[i]) + solution[i] -= my_chsign(is_chsign(lp, i), fabs(lp->upbo[i])); + solution[i] = unscaled_value(lp, -solution[i], i); +#endif + } + + /* Initialize user variables to their lower bounds. */ + for(i = lp->rows+1; i <= lp->sum; i++) + solution[i] = lp->lowbo[i]; + + /* Add values of user basic variables. */ + for(i = 1; i <= lp->rows; i++) { + basi = lp->var_basic[i]; + if(basi > lp->rows) + solution[basi] += lp->rhs[i]; + } + + /* 1. Adjust non-basic variables at their upper bounds, + 2. Unscale all user variables, + 3. Optionally do precision management. */ + for(i = lp->rows + 1; i <= lp->sum; i++) { + if(!lp->is_basic[i] && !lp->is_lower[i]) + solution[i] += lp->upbo[i]; + solution[i] = unscaled_value(lp, solution[i], i); +#ifdef xImproveSolutionPrecision + if(is_int(lp, i-lp->rows)) + solution[i] = restoreINT(solution[i], lp->epsint); + else + solution[i] = restoreINT(solution[i], lp->epsprimal); +#endif + } + + /* Compute the OF and slack values "in extentio" */ + for(j = 1; j <= lp->columns; j++) { + f = solution[lp->rows + j]; + if(f != 0) { + i = lp->matA->col_end[j-1]; + basi = lp->matA->col_end[j]; + for(matelm = lp->matA->col_mat+i; i < basi; i++, matelm++) + solution[(*matelm).row_nr] += f * unscaled_mat(lp, (*matelm).value, (*matelm).row_nr, j); + } + } + + /* Do slack precision management and sign reversal if necessary */ + for(i = 0; i <= lp->rows; i++) { +#ifdef ImproveSolutionPrecision + my_roundzero(solution[i], epsvalue); +#endif + if(is_chsign(lp, i)) + solution[i] = my_flipsign(solution[i]); + } + + /* Record the best real-valued solution and compute a simple MIP solution limit */ + if(target == NULL) { + if(fabs(lp->real_solution) >= lp->infinite) { + lp->real_solution = solution[0]; + lp->bb_limitOF = lp->real_solution; + + /* Do MIP-related tests and computations */ + if((lp->int_count > 0) && mat_validate(lp->matA)) { + REAL fixedOF = unscaled_value(lp, lp->orig_rh[0], 0); + MATrec *mat = lp->matA; + + /* Check if we have an all-integer OF */ + basi = mat->row_end[0]; + for(i = 0; i < basi; i++) { + j = ROW_MAT_COL(mat->row_mat[i]); + f = get_mat_byindex(lp, i, TRUE)+lp->epsint/2; + f = fabs(f-floor(f)); + if(!is_int(lp, j) || (f > lp->epsint)) + break; + } + + /* If so, we can round up the fractional OF */ + if(i == basi) { + f = my_chsign(is_maxim(lp), lp->real_solution) + fixedOF; + f = floor(f+(1-epsvalue)); + lp->bb_limitOF = my_chsign(is_maxim(lp), f - fixedOF); + } + + /* Check that a user limit on the OF is feasible */ + if(my_chsign(is_maxim(lp), my_reldiff(lp->best_solution[0],lp->bb_limitOF)) < -epsvalue) { + lp->spx_status = INFEASIBLE; + lp->bb_break = TRUE; + } + } + } + } + +} /* construct_solution */ + +STATIC int check_solution(lprec *lp, int lastcolumn, REAL *solution, + REAL *upbo, REAL *lowbo, REAL tolerance) +{ + REAL test, value, diff, maxerr = 0.0; + int i,n, errlevel = IMPORTANT, errlimit = 10; + + report(lp, NORMAL, " \n"); + report(lp, NORMAL, "lp_solve solution " MPSVALUEMASK " final at iteration %7d, %6d nodes explored\n", + solution[0], lp->total_iter, lp->bb_totalnodes); + + /* Check if solution values are within the bounds; allowing a margin for numeric errors */ + n = 0; + for(i = lp->rows + 1; i <= lp->rows+lastcolumn; i++) { + + value = solution[i]; + + /* Check for case where we are testing an intermediate solution + (variables shifted to the origin) */ + if(lowbo == NULL) + test = 0; + else + test = unscaled_value(lp, lowbo[i], i); + + diff = my_reldiff(value, test); + if((diff < -tolerance) && !(fabs(lp->var_is_sc[i - lp->rows]) > 0)) { + maxerr = MAX(maxerr, fabs(value-test)); + if(n < errlimit) + report(lp, errlevel, + "check_solution: Variable %s = " MPSVALUEMASK " is below its lower bound " MPSVALUEMASK "\n", + get_col_name(lp, i-lp->rows), (double)value, (double)test); + n++; + } + + test = unscaled_value(lp, upbo[i], i); + diff = my_reldiff(value, test); + if(diff > tolerance) { + maxerr = MAX(maxerr, fabs(value-test)); + if(n < errlimit) + report(lp, errlevel, + "check_solution: Variable %s = " MPSVALUEMASK " is above its upper bound " MPSVALUEMASK "\n", + get_col_name(lp, i-lp->rows), (double)value, (double)test); + n++; + } + } + + /* Check if constraint values are within the bounds; allowing a margin for numeric errors */ + for(i = 1; i <= lp->rows; i++) { + + value = solution[i]; + + test = lp->orig_rh[i]; + if(is_chsign(lp, i)) { + test = my_flipsign(test); + test += fabs(upbo[i]); + } + test = unscaled_value(lp, test, i); +#ifndef LegacySlackDefinition + value += test; +#endif + diff = my_reldiff(value, test); + if(diff > tolerance) { + maxerr = MAX(maxerr, fabs(value-test)); + if(n < errlimit) + report(lp, errlevel, + "check_solution: Constraint %s = " MPSVALUEMASK " is above its %s " MPSVALUEMASK "\n", + get_row_name(lp, i), (double)value, + (is_constr_type(lp, i, EQ) ? "equality of" : "upper bound"), (double)test); + n++; + } + + value = solution[i]; + + if(is_chsign(lp, i)) { + test = lp->orig_rh[i]; + test = my_flipsign(test); + } + else { + test = lp->orig_rh[i]-fabs(upbo[i]); +#ifndef LegacySlackDefinition + value = fabs(upbo[i]) - value; +#endif + } + test = unscaled_value(lp, test, i); +#ifndef LegacySlackDefinition + value += test; +#endif + diff = my_reldiff(value, test); + if(diff < -tolerance) { + maxerr = MAX(maxerr, fabs(value-test)); + if(n < errlimit) + report(lp, errlevel, + "check_solution: Constraint %s = " MPSVALUEMASK " is below its %s " MPSVALUEMASK "\n", + get_row_name(lp, i), (double)value, + (is_constr_type(lp, i, EQ) ? "equality of" : "lower bound"), (double)test); + n++; + } + } + + if(n > 0) { + report(lp, IMPORTANT, "check_solution: %d accuracy errors (|max|=%g, invpivot=%d)\n", + n, maxerr, lp->bfp_pivotcount(lp)); + return(NUMFAILURE); + } + else + return(OPTIMAL); + +} /* check_solution */ + +STATIC void transfer_solution_var(lprec *lp, int uservar) +{ + if(lp->varmap_locked && (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)) { + uservar += lp->rows; + lp->full_solution[lp->orig_rows+lp->var_to_orig[uservar]] = lp->best_solution[uservar]; + } +} +STATIC void transfer_solution(lprec *lp, MYBOOL dofinal) +{ + int i; + + MEMCOPY(lp->best_solution, lp->solution, lp->sum + 1); + + /* Round integer solution values to actual integers */ + if(is_integerscaling(lp)) + for(i = 1; i <= lp->columns; i++) + if(is_int(lp, i)) + lp->best_solution[lp->rows + i] = floor(lp->best_solution[lp->rows + i] + 0.5); + + /* Transfer to full solution vector in the case of presolved eliminations */ + if(dofinal && lp->varmap_locked && + (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)) { + lp->full_solution[0] = lp->best_solution[0]; + for(i = 1; i <= lp->rows; i++) + lp->full_solution[lp->var_to_orig[i]] = lp->best_solution[i]; + for(i = 1; i <= lp->columns; i++) + lp->full_solution[lp->orig_rows+lp->var_to_orig[lp->rows+i]] = lp->best_solution[lp->rows+i]; + } + +} + +STATIC MYBOOL calculate_duals(lprec *lp) +{ + int i; + REAL scale0; + + if(lp->duals != NULL) { + FREE(lp->duals); + } + + if(lp->doRebase || lp->doInvert || (!lp->basis_valid)) + return(FALSE); + + allocREAL(lp, &lp->duals, lp->sum + 1, AUTOMATIC); + if(lp->duals == NULL) { + lp->spx_status = NOMEMORY; + return(FALSE); + } + + /* Initialize */ + bsolve(lp, 0, lp->duals, NULL, lp->epsmachine*DOUBLEROUND, 1.0); + prod_xA(lp, SCAN_USERVARS+USE_NONBASICVARS, lp->duals, NULL, XRESULT_FREE, lp->epsmachine, 1.0, + lp->duals, NULL); + + /* The dual values are the reduced costs of the slacks */ + /* When the slack is at its upper bound, change the sign. */ + for(i = 1; i <= lp->rows; i++) { + if(lp->is_basic[i]) + lp->duals[i] = 0; + /* Added a test if variable is different from 0 because sometime you get -0 and this + is different from 0 on for example INTEL processors (ie 0 != -0 on INTEL !) PN */ + else if((is_chsign(lp, 0) == is_chsign(lp, i)) && lp->duals[i]) + lp->duals[i] = my_flipsign(lp->duals[i]); + } + if (is_maxim(lp)) + for(i = lp->rows + 1; i <= lp->sum; i++) + lp->duals[i] = my_flipsign(lp->duals[i]); + + /* Scaling-related changes for v4.0 */ + if (lp->scaling_used) + scale0 = lp->scalars[0]; + else + scale0 = 1; + for(i = 1; i <= lp->sum; i++) { + lp->duals[i] /= scale0; + lp->duals[i] = scaled_value(lp, lp->duals[i], i); + my_roundzero(lp->duals[i], lp->epsprimal); + } + + return(TRUE); +} /* calculate_duals */ + +/* Calculate sensitivity duals */ +STATIC MYBOOL calculate_sensitivity_duals(lprec *lp) +{ + int k,varnr, ok = TRUE; + REAL *pcol,a,infinite,epsvalue,from,till; + + /* one column of the matrix */ + allocREAL(lp, &pcol, lp->rows + 1, TRUE); + if(lp->dualsfrom != NULL) { + FREE(lp->dualsfrom); + } + if(lp->dualstill != NULL) { + FREE(lp->dualstill); + } + allocREAL(lp, &lp->dualsfrom, lp->sum + 1, AUTOMATIC); + allocREAL(lp, &lp->dualstill, lp->sum + 1, AUTOMATIC); + if((pcol == NULL) || + (lp->dualsfrom == NULL) || + (lp->dualstill == NULL)) { + if(pcol != NULL) + FREE(pcol); + if(lp->dualsfrom != NULL) + FREE(lp->dualsfrom); + if(lp->dualstill != NULL) + FREE(lp->dualstill); + lp->spx_status = NOMEMORY; + ok = FALSE; + } + else { + infinite=lp->infinite; + epsvalue=lp->epsmachine; + for(varnr=1; varnr<=lp->sum; varnr++) { + from=infinite; + till=infinite; + if ((!lp->is_basic[varnr]) /* && ((varnr<=lp->rows) || (fabs(lp->solution[varnr])>epsvalue)) */) { + if (!fsolve(lp, varnr, pcol, lp->workrowsINT, epsvalue, 1.0, FALSE)) { /* construct one column of the tableau */ + ok = FALSE; + break; + } + /* Search for the rows(s) which first result in further iterations */ + for (k=1; k<=lp->rows; k++) { + if (fabs(pcol[k])>epsvalue) { + a = unscaled_value(lp, lp->rhs[k]/pcol[k], varnr); + if ((a<=0.0) && (pcol[k]<0.0) && (-a=0.0) && (pcol[k]>0.0) && ( aupbo[lp->var_basic[k]] < infinite) { + a = (REAL) ((lp->rhs[k]-lp->upbo[lp->var_basic[k]])/pcol[k]); + a = unscaled_value(lp, a, varnr); + if ((a<=0.0) && (pcol[k]>0.0) && (-a=0.0) && (pcol[k]<0.0) && ( ais_lower[varnr]) { + a=from; + from=till; + till=a; + } + if ((varnr<=lp->rows) && (!is_chsign(lp, varnr))) { + a=from; + from=till; + till=a; + } + } + if (from!=infinite) + lp->dualsfrom[varnr]=lp->solution[varnr]-from; + else + lp->dualsfrom[varnr]=-infinite; + if (till!=infinite) + lp->dualstill[varnr]=lp->solution[varnr]+till; + else + lp->dualstill[varnr]=infinite; + } + FREE(pcol); + } + return((MYBOOL) ok); +} /* calculate_sensitivity_duals */ + +/* Calculate sensitivity objective function */ +STATIC MYBOOL calculate_sensitivity_obj(lprec *lp) +{ + int i,l,varnr,row_nr,ok = TRUE; + REAL *OrigObj = NULL,*drow = NULL,*prow = NULL,a,min1,min2,infinite,epsvalue,from,till; + + /* objective function */ + allocREAL(lp, &drow, lp->sum + 1, TRUE); + allocREAL(lp, &OrigObj, lp->columns + 1, FALSE); + allocREAL(lp, &prow, lp->sum + 1, TRUE); + if(lp->objfrom != NULL) { + FREE(lp->objfrom); + } + if(lp->objtill != NULL) { + FREE(lp->objtill); + } + allocREAL(lp, &lp->objfrom, lp->columns + 1, AUTOMATIC); + allocREAL(lp, &lp->objtill, lp->columns + 1, AUTOMATIC); + if ((drow == NULL) || + (OrigObj == NULL) || + (prow == NULL)) { + if(drow != NULL) + FREE(drow); + if(OrigObj != NULL) + FREE(OrigObj); + if(prow != NULL) + FREE(prow); + if(lp->objfrom != NULL) + FREE(lp->objfrom); + if(lp->objtill != NULL) + FREE(lp->objtill); + lp->spx_status = NOMEMORY; + ok = FALSE; + } + else { + infinite=lp->infinite; + epsvalue=lp->epsmachine; + + bsolve(lp, 0, drow, NULL, epsvalue*DOUBLEROUND, 1.0); + prod_xA(lp, SCAN_USERVARS+USE_NONBASICVARS, drow, NULL, XRESULT_FREE, epsvalue, 1.0, + drow, NULL); + + /* original (unscaled) objective function */ + get_row(lp, 0, OrigObj); + for(i = 1; i <= lp->columns; i++) { + from=-infinite; + till= infinite; + varnr = lp->rows + i; + if (!lp->is_basic[varnr]) { + /* only the coeff of the objective function of column i changes. */ + a = unscaled_mat(lp, drow[varnr], 0, i); + if (lp->upbo[varnr] == 0.0) + /* ignore, because this case doesn't results in further iterations */ ; + else if (lp->is_lower[varnr]) + from = OrigObj[i] - a; /* less than this value gives further iterations */ + else + till = OrigObj[i] - a; /* bigger than this value gives further iterations */ + } + else { + /* all the coeff of the objective function change. Search the minimal change needed for further iterations */ + for (row_nr=1; + (row_nr<=lp->rows) && (lp->var_basic[row_nr]!=varnr); row_nr++) + /* Search on which row the variable exists in the basis */ ; + if (row_nr<=lp->rows) { /* safety test; should always be found ... */ + /* Construct one row of the tableau */ + bsolve(lp, row_nr, prow, NULL, epsvalue*DOUBLEROUND, 1.0); + prod_xA(lp, SCAN_USERVARS+USE_NONBASICVARS, prow, NULL, XRESULT_FREE, epsvalue, 1.0, + prow, NULL); + + min1=infinite; + min2=infinite; + for (l=1; l<=lp->sum; l++) /* search for the column(s) which first results in further iterations */ + if ((!lp->is_basic[l]) && (lp->upbo[l]>0.0) && + (fabs(prow[l])>epsvalue) && (drow[l]*(lp->is_lower[l] ? -1 : 1)is_lower[l] ? 1 : -1)<0.0) { + if (ais_lower[varnr]) { + a = min1; + min1 = min2; + min2 = a; + } + if (min1solution[varnr]==0.0) + till = 0.0; /* if value is 0 then there can't be an upper range */ + } + } + lp->objfrom[i]=from; + lp->objtill[i]=till; + } + } + if (prow !=NULL) FREE(prow); + if (OrigObj != NULL) FREE(OrigObj); + if (drow != NULL) FREE(drow); + return((MYBOOL) ok); +} /* calculate_sensitivity_obj */ + +STATIC MYBOOL check_if_less(lprec *lp, REAL x, REAL y, int variable) +{ + if(y < x-scaled_value(lp, lp->epsint, variable)) { + if(lp->bb_trace) + report(lp, NORMAL, "check_if_less: Invalid new bound %g should be < %g for %s\n", + x, y, get_col_name(lp, variable)); + return(FALSE); + } + else + return(TRUE); +} + +/* Various basis utility routines */ + +STATIC int findNonBasicSlack(lprec *lp, MYBOOL *is_basic) +{ + int i; + + for(i = lp->rows; i > 0; i--) + if(!is_basic[i]) + break; + return( i ); +} + +STATIC int findBasisPos(lprec *lp, int notint, int *var_basic) +{ + int i; + + if(var_basic == NULL) + var_basic = lp->var_basic; + for(i = lp->rows; i > 0; i--) + if(var_basic[i] == notint) + break; + return( i ); +} + +STATIC void replaceBasisVar(lprec *lp, int rownr, int var, int *var_basic, MYBOOL *is_basic) +{ + int out; + + out = var_basic[rownr]; + var_basic[rownr] = var; + is_basic[out] = FALSE; + is_basic[var] = TRUE; +} + +/* Transform RHS by adjusting for the bound state of variables; + optionally rebase upper bound, and account for this in later calls */ +STATIC void initialize_solution(lprec *lp, MYBOOL shiftbounds) +{ + int i, k1, k2, rownr, varnr; + REAL theta, value, loB, upB; + MATitem *matentry; + + /* Set bounding status indicators */ + if(lp->bb_bounds != NULL) { + if(shiftbounds == INITSOL_SHIFTZERO) { +#ifdef Paranoia + if(lp->bb_bounds->UBzerobased) + report(lp, SEVERE, "initialize_solution: The upper bounds are already zero-based at refactorization %d\n", + lp->bfp_refactcount(lp)); +#endif + lp->bb_bounds->UBzerobased = TRUE; + } +#ifdef Paranoia + else if(!lp->bb_bounds->UBzerobased) + report(lp, SEVERE, "initialize_solution: The upper bounds are not zero-based at refactorization %d\n", + lp->bfp_refactcount(lp)); +#endif + } + + if(sizeof(*lp->rhs) == sizeof(*lp->orig_rh)) { + MEMCOPY(lp->rhs, lp->orig_rh, lp->rows+1); + } + else + for(i = 0; i <= lp->rows; i++) + lp->rhs[i] = lp->orig_rh[i]; + +/* Adjust active RHS for variables at their active upper/lower bounds */ + for(i = 1; i <= lp->sum; i++) { + + upB = lp->upbo[i]; + loB = lp->lowbo[i]; + + /* Do user and artificial variables */ + if(i > lp->rows) { + + varnr = i - lp->rows; + + /* Shift to "ranged" upper bound, tantamount to defining zero-based variables */ + if(shiftbounds == INITSOL_SHIFTZERO) { + if((loB > -lp->infinite) && (upB < lp->infinite)) + lp->upbo[i] -= loB; + if(lp->upbo[i] < 0) + report(lp, SEVERE, "initialize_solution: Invalid rebounding; variable %d at refactorization %d\n", + i, lp->bfp_refactcount(lp)); + } + + /* Use "ranged" upper bounds */ + else if (shiftbounds == INITSOL_USEZERO) { + if((loB > -lp->infinite) && (upB < lp->infinite)) + upB += loB; + } + + /* Shift upper bound back to original value */ + else if(shiftbounds == INITSOL_ORIGINAL) { + if((loB > -lp->infinite) && (upB < lp->infinite)) { + lp->upbo[i] += loB; + upB += loB; + } + continue; + } + else + report(lp, SEVERE, "initialize_solution: Invalid option value '%d'\n", + shiftbounds); + + /* Set the applicable adjustment */ + if(lp->is_lower[i]) + theta = loB; + else + theta = upB; + + + /* Check if we need to pass through the matrix; + remember that basis variables are always lower-bounded */ + if(theta == 0) + continue; + + /* Get starting and ending indeces in the NZ vector */ + k1 = lp->matA->col_end[varnr - 1]; + k2 = lp->matA->col_end[varnr]; + + /* Handle simplex Phase 1 offsets */ + if(k1 < k2) { + matentry = lp->matA->col_mat + k1; + rownr = (*matentry).row_nr; + value = (*matentry).value; + if(rownr > 0) { + value = 0; + if(modifyOF1(lp, i, &value, theta)) + lp->rhs[0] -= value; + } + else + k1++; + } + else { + rownr = 0; + value = 0; + } + if((rownr == 0) && modifyOF1(lp, i, &value, theta)) + lp->rhs[rownr] -= value; + + /* Do the normal case */ + for(matentry = lp->matA->col_mat + k1; k1 < k2; k1++, matentry++) { + rownr = (*matentry).row_nr; + value = (*matentry).value; + lp->rhs[rownr] -= theta * value; + } + } + else { + /* Note that finite-bounded constraints are "natively" stored in the range format */ + if(!lp->is_lower[i]) + lp->rhs[i] -= upB; + } + } + if(shiftbounds == INITSOL_SHIFTZERO) + lp->doRebase = FALSE; + +} + +/* This routine recomputes the basic variables using the full inverse */ +STATIC void recompute_solution(lprec *lp, MYBOOL shiftbounds) +{ + REAL roundzero = lp->epsmachine; + + /* Compute RHS = b - A(n)*x(n) */ + initialize_solution(lp, shiftbounds); + + /* Compute x(b) = Inv(B)*RHS (Ref. lp_solve inverse logic and Chvatal p. 121) */ + lp->bfp_ftran_normal(lp, lp->rhs, NULL); + + /* Round the values (should not be greater than the factor used in inv_pivotRHS) */ + roundVector(lp->rhs, lp->rows, roundzero); + + lp->doRecompute = FALSE; +} + +/* This routine compares an existing basic solution to a recomputed one; + Note that the routine must provide for the possibility that the order of the + basis variables can be changed by the inversion engine. */ +STATIC int verify_solution(lprec *lp, MYBOOL reinvert, char *info) +{ + int i, ii, n, *oldmap, *newmap, *refmap = NULL; + REAL *oldrhs, err, errmax; + + allocINT(lp, &oldmap, lp->rows+1, FALSE); + allocINT(lp, &newmap, lp->rows+1, FALSE); + allocREAL(lp, &oldrhs, lp->rows+1, FALSE); + + /* Get sorted mapping of the old basis */ + for(i = 0; i <= lp->rows; i++) + oldmap[i] = i; + if(reinvert) { + allocINT(lp, &refmap, lp->rows+1, FALSE); + MEMCOPY(refmap, lp->var_basic, lp->rows+1); + sortByINT(oldmap, refmap, lp->rows, 1, TRUE); + } + + /* Save old and calculate the new RHS vector */ + MEMCOPY(oldrhs, lp->rhs, lp->rows+1); + if(reinvert) { + lp->doInvert = TRUE; + invert(lp, INITSOL_USEZERO); + } + else + recompute_solution(lp, INITSOL_USEZERO); + + /* Get sorted mapping of the new basis */ + for(i = 0; i <= lp->rows; i++) + newmap[i] = i; + if(reinvert) { + MEMCOPY(refmap, lp->var_basic, lp->rows+1); + sortByINT(newmap, refmap, lp->rows, 1, TRUE); + } + + /* Identify any gap */ + errmax = 0; + ii = -1; + n = 0; + for(i = lp->rows; i > 0; i--) { + err = fabs(my_reldiff(oldrhs[oldmap[i]], lp->rhs[newmap[i]])); + if(err > lp->epsprimal) { + n++; + if(err > errmax) { + ii = i; + errmax = err; + } + } + } + err = fabs(my_reldiff(oldrhs[i], lp->rhs[i])); + if(err < lp->epspivot) { + i--; + err = 0; + } + else { + n++; + if(ii < 0) { + ii = 0; + errmax = err; + } + } + if(n > 0) { + report(lp, IMPORTANT, "verify_solution: Iteration %d %s - %d errors; OF %g, Max @row %d %g\n", + get_total_iter(lp), my_if(info == NULL, "", info), n, err, newmap[ii], errmax); + } + /* Copy old results back (not possible for inversion) */ + if(!reinvert) + MEMCOPY(lp->rhs, oldrhs, lp->rows+1); + + FREE(oldmap); + FREE(newmap); + FREE(oldrhs); + if(reinvert) + FREE(refmap); + + return( ii ); + +} + +/* Preprocessing and postprocessing functions */ +STATIC int identify_GUB(lprec *lp, MYBOOL mark) +{ + int i, j, jb, je, k, knint; + REAL rh, mv, tv, bv; + MATrec *mat = lp->matA; + + if((lp->equalities == 0) || !mat_validate(mat)) + return( 0 ); + + k = 0; + for(i = 1; i <= lp->rows; i++) { + + /* Check if it is an equality constraint */ + if(get_constr_type(lp, i) != EQ) + continue; + + rh = get_rh(lp, i); + knint = 0; + je = mat->row_end[i]; + for(jb = mat->row_end[i-1]; jb < je; jb++) { + j = ROW_MAT_COL(mat->row_mat[jb]); + + /* Check for validity of the equation elements */ + if(!is_int(lp, j)) + knint++; + if(knint > 1) + break; + + mv = get_mat_byindex(lp, jb, TRUE); + if(fabs(my_reldiff(mv, rh)) > lp->epsprimal) + break; + + tv = get_upbo(lp, j); + bv = get_lowbo(lp, j); + if((fabs(my_reldiff(mv*tv, rh)) > lp->epsprimal) || (bv != 0)) + break; + } + + /* Update GUB count and optionally mark the GUB */ + if(jb == je) { + k++; + if(mark) + lp->row_type[i] |= ROWTYPE_GUB; + } + + } + return( k ); +} + +STATIC int prepare_GUB(lprec *lp) +{ + int i, j, jb, je, k, *members = NULL; + REAL rh; + char GUBname[16]; + MATrec *mat = lp->matA; + + if((lp->equalities == 0) || + !allocINT(lp, &members, lp->columns+1, TRUE) || + !mat_validate(mat)) + return( 0 ); + + for(i = 1; i <= lp->rows; i++) { + + /* Check if it has been marked as a GUB */ + if(!(lp->row_type[i] & ROWTYPE_GUB)) + continue; + + /* Pick up the GUB column indeces */ + k = 0; + je = mat->row_end[i]; + for(jb = mat->row_end[i-1], k = 0; jb < je; jb++) { + members[k] = ROW_MAT_COL(mat->row_mat[jb]); + k++; + } + + /* Add the GUB */ + j = GUB_count(lp) + 1; + sprintf(GUBname, "GUB_%d", i); + add_GUB(lp, GUBname, j, k, members); + + /* Standardize coefficients to 1 if necessary */ + rh = get_rh(lp, i); + if(fabs(my_reldiff(rh, 1)) > lp->epsprimal) { + lp_solve_set_rh(lp, i, 1); + for(jb = mat->row_end[i-1]; jb < je; jb++) { + j = ROW_MAT_COL(mat->row_mat[jb]); + lp_solve_set_mat(lp, i,j, 1); + } + } + + } + FREE(members); + return(GUB_count(lp)); +} + +int preprocess(lprec *lp) +{ + int i, j, k = 0, ok=TRUE; + REAL *new_column, hold; + MYBOOL scaled; + + /* do not process if already preprocessed */ + if(lp->wasPreprocessed) + return(ok); + + /* Write model statistics */ + if(lp->lag_status != RUNNING) { + report(lp, NORMAL, "Model run %4d for name: %s\n", lp->solvecount, lp->lp_name); + +#ifdef EnableBranchingOnGUB + if(is_bb_mode(lp, NODE_GUBMODE)) + k = identify_GUB(lp, TRUE); +#endif + + j = 0; + for(i = 1; i <= lp->rows; i++) + if(is_constr_type(lp,i, EQ)) + j++; + + report(lp, NORMAL, "Objective: %simize(%s)\n", + my_if(is_maxim(lp), "Max", "Min"), get_row_name(lp, 0)); + report(lp, NORMAL, "\nModel size: %5d constraints, %5d variables, %8d non-zeros.\n", + lp->rows, lp->columns, get_nonzeros(lp)); + report(lp, NORMAL, "Constraints: %5d equality, %5d GUB, %5d SOS.\n", + j, k, SOS_count(lp)); + report(lp, NORMAL, "Variables: %5d integer, %5d semi-cont., %5d SOS.\n", + lp->int_count, lp->sc_count, lp->sos_vars); + report(lp, NORMAL, " \n"); + +#ifdef EnablePartialOptimization + if(is_piv_mode(lp, PRICE_AUTOPARTIALCOLS) && (partial_findBlocks(lp, TRUE, FALSE) > 1)) { + i = partial_countBlocks(lp, FALSE); + if(i > 1) + report(lp, NORMAL, "The model is estimated to have %d column blocks/stages.\n", + i); + } + if(is_piv_mode(lp, PRICE_AUTOPARTIALROWS) && (partial_findBlocks(lp, TRUE, TRUE) > 1)) { + i = partial_countBlocks(lp, TRUE); + if(i > 1) + report(lp, NORMAL, "The model is estimated to have %d row blocks/stages.\n", + i); + } +#endif + i = (lp->simplex_strategy & SIMPLEX_Phase1_DUAL); + j = (lp->simplex_strategy & SIMPLEX_Phase2_DUAL); + report(lp, NORMAL, "Using %s simplex for phase 1 and %s simplex for phase 2.\n", + my_if(i == 0, "PRIMAL", "DUAL"), my_if(j == 0, "PRIMAL", "DUAL")); + report(lp, NORMAL, " \n"); + } + + /* Compute a minimum step improvement step requirement */ + new_column = NULL; + lp->bb_deltaOF = minimumImprovementOF(lp); + + /* First create extra columns for FR variables or flip MI variables */ + for (j = 1; j <= lp->columns; j++) { + +#ifdef Paranoia + if((lp->rows != lp->matA->rows) || (lp->columns != lp->matA->columns)) + report(lp, SEVERE, "preprocess: Inconsistent variable counts found\n"); +#endif + + /* First handle variables with a negative Inf-bound by changing signs (multiply column by -1) */ + i = lp->rows + j; + hold = lp->orig_upbo[i]; + if((hold <= 0) || ((hold < lp->infinite) && + (lp->splitnegvars == AUTOMATIC) && + (lp->orig_lowbo[i] == -lp->infinite)) ) { + /* See if this should be handled via normal bound shifting */ + if((hold < 0) && (lp->orig_lowbo[i] >= lp->negrange)) + continue; + /* Delete split sibling variable if one existed from before */ + if((lp->var_is_free != NULL) && (lp->var_is_free[j] > 0)) + del_column(lp, lp->var_is_free[j]); + mat_multcol(lp->matA, j, -1); + if(lp->var_is_free == NULL) { + allocINT(lp, &lp->var_is_free, MAX(lp->columns,lp->columns_alloc) + 1, TRUE); + if (lp->var_is_free == NULL) + return(FALSE); + } + lp->var_is_free[j] = -j; /* Indicator UB and LB are switched, with no helper variable added */ + lp->orig_upbo[i] = my_flipsign(lp->orig_lowbo[i]); + lp->orig_lowbo[i] = my_flipsign(hold); + /* Check for presence of negative ranged SC variable */ + if(lp->var_is_sc[j] > 0) { + lp->var_is_sc[j] = lp->orig_lowbo[i]; + lp->orig_lowbo[i] = 0; + } + } + /* Then deal with -+, full-range/FREE variables */ + else if(((lp->splitnegvars != AUTOMATIC) || (lp->orig_lowbo[i] == -lp->infinite)) && + (lp->orig_lowbo[i] < lp->negrange)) { + if(lp->var_is_free == NULL) { + allocINT(lp, &lp->var_is_free, MAX(lp->columns,lp->columns_alloc) + 1, TRUE); + if (lp->var_is_free == NULL) + return(FALSE); + } + if(lp->var_is_free[j] <= 0) { /* If this variable wasn't split yet ... */ + if(SOS_is_member(lp->SOS, 0, i - lp->rows)) { /* Added */ + report(lp, IMPORTANT, "preprocess: Converted negative bound for SOS variable %d to zero", + i - lp->rows); + lp->orig_lowbo[i] = 0; + continue; + } + if(new_column == NULL) { + allocREAL(lp, &new_column, lp->rows + 1, FALSE); + if (new_column == NULL) { + lp->spx_status = NOMEMORY; + ok = FALSE; + break; + } + } + /* Avoid precision loss by turning off unscaling and rescaling */ + /* in get_column and add_column operations; also make sure that */ + /* full scaling information is preserved */ + scaled = lp->scaling_used; + lp->scaling_used = FALSE; + get_column(lp, j, new_column); + if(!add_columnex(lp, lp->rows, new_column, NULL)) { + ok = FALSE; + break; + } + mat_multcol(lp->matA, lp->columns, -1); + if(scaled) + lp->scalars[lp->rows+lp->columns] = lp->scalars[i]; + lp->scaling_used = (MYBOOL) scaled; + if(lp->names_used) { + char fieldn[50]; + + sprintf(fieldn, "__AntiBodyOf(%d)__", j); + if(!set_col_name(lp, lp->columns, fieldn)) { +/* if (!set_col_name(lp, lp->columns, get_col_name(lp, j))) { */ + ok = FALSE; + break; + } + } + /* Set (positive) index to the original column's split / helper and back */ + lp->var_is_free[j] = lp->columns; + } + lp->orig_upbo[lp->rows + lp->var_is_free[j]] = my_flipsign(lp->orig_lowbo[i]); + lp->orig_lowbo[i] = 0; + + /* Negative index indicates x is split var and -var_is_free[x] is index of orig var */ + lp->var_is_free[lp->var_is_free[j]] = -j; + lp->must_be_int[lp->var_is_free[j]] = lp->must_be_int[j]; + } + /* Check for positive ranged SC variables */ + else if(lp->var_is_sc[j] > 0) { + lp->var_is_sc[j] = lp->orig_lowbo[i]; + lp->orig_lowbo[i] = 0; + } + + /* Tally integer variables in SOS'es */ + if(SOS_is_member(lp->SOS, 0, j) && is_int(lp, j)) + lp->sos_ints++; + } + + if(new_column != NULL) + FREE(new_column); + + /* Fill lists of GUB constraints, if appropriate */ +#ifdef EnableBranchingOnGUB + if(k > 0) + prepare_GUB(lp); +#endif + + lp->wasPreprocessed = TRUE; + + return(ok); +} + +void postprocess(lprec *lp) +{ + int i,ii,j; + REAL hold; + + /* Check if the problem actually was preprocessed */ + if(!lp->wasPreprocessed) + return; + + /* Must compute duals here in case we have free variables; note that in + this case sensitivity analysis is not possible unless done here */ + if((MIP_count(lp) == 0) && + (is_presolve(lp, PRESOLVE_DUALS) || (lp->var_is_free != NULL))) + calculate_duals(lp); + if(is_presolve(lp, PRESOLVE_SENSDUALS)) { + if(!calculate_sensitivity_duals(lp) || !calculate_sensitivity_obj(lp)) + report(lp, IMPORTANT, "postprocess: Unable to allocate working memory for duals.\n"); + } + + /* Loop over all columns */ + for (j = 1; j <= lp->columns; j++) { + i = lp->rows + j; + /* Reconstruct strictly negative values */ + if((lp->var_is_free != NULL) && (lp->var_is_free[j] < 0)) { + /* Check if we have the simple case where the UP and LB are negated and switched */ + if(-lp->var_is_free[j] == j) { + mat_multcol(lp->matA, j, -1); + hold = lp->orig_upbo[i]; + lp->orig_upbo[i] = my_flipsign(lp->orig_lowbo[i]); + lp->orig_lowbo[i] = my_flipsign(hold); + lp->best_solution[i] = my_flipsign(lp->best_solution[i]); + transfer_solution_var(lp, j); + + /* hold = lp->objfrom[j]; + lp->objfrom[j] = my_flipsign(lp->objtill[j]); + lp->objtill[j] = my_flipsign(hold); */ /* under investigation */ + + /* lp->duals[i] = my_flipsign(lp->duals[i]); + hold = lp->dualsfrom[i]; + lp->dualsfrom[i] = my_flipsign(lp->dualstill[i]); + lp->dualstill[i] = my_flipsign(hold); */ /* under investigation */ + /* Bound switch undone, so clear the status */ + lp->var_is_free[j] = 0; + /* Adjust negative ranged SC */ + if(lp->var_is_sc[j] > 0) + lp->orig_lowbo[lp->rows + j] = -lp->var_is_sc[j]; + } + /* Ignore the split / helper columns (will be deleted later) */ + } + /* Condense values of extra columns of quasi-free variables split in two */ + else if((lp->var_is_free != NULL) && (lp->var_is_free[j] > 0)) { + ii = lp->var_is_free[j]; /* Index of the split helper var */ + /* if(lp->objfrom[j] == -lp->infinite) + lp->objfrom[j] = -lp->objtill[ii]; + lp->objtill[ii] = lp->infinite; + if(lp->objtill[j] == lp->infinite) + lp->objtill[j] = my_flipsign(lp->objfrom[ii]); + lp->objfrom[ii] = -lp->infinite; */ /* under investigation */ + + ii += lp->rows; + lp->best_solution[i] -= lp->best_solution[ii]; /* join the solution again */ + transfer_solution_var(lp, j); + lp->best_solution[ii] = 0; + + /* if(lp->duals[i] == 0) + lp->duals[i] = my_flipsign(lp->duals[ii]); + lp->duals[ii] = 0; + if(lp->dualsfrom[i] == -lp->infinite) + lp->dualsfrom[i] = my_flipsign(lp->dualstill[ii]); + lp->dualstill[ii] = lp->infinite; + if(lp->dualstill[i] == lp->infinite) + lp->dualstill[i] = my_flipsign(lp->dualsfrom[ii]); + lp->dualsfrom[ii] = -lp->infinite; */ /* under investigation */ + + /* Reset to original bound */ + lp->orig_lowbo[i] = my_flipsign(lp->orig_upbo[ii]); + } + /* Adjust for semi-continuous variables */ + else if(lp->var_is_sc[j] > 0) { + lp->orig_lowbo[i] = lp->var_is_sc[j]; + } + } + + /* Remove any split column helper variables */ + del_splitvars(lp); + + /* Do extended reporting, if specified */ + if(lp->verbose > NORMAL) { + REPORT_extended(lp); + + } + + lp->wasPreprocessed = FALSE; +} + Index: src/tools/solver/lp_solve/lp_lib.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_lib.h diff -N src/tools/solver/lp_solve/lp_lib.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_lib.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1997 @@ +#ifndef HEADER_lp_lib +#define HEADER_lp_lib + +/* -------------------------------------------------------------------------- + + This is the main library header file for the lp_solve v5.0 release + + Starting at version 3.0, LP_Solve is released under the LGPL license. + For full information, see the enclosed file LGPL.txt. + + Original developer: Michel Berkelaar - michel@ics.ele.tue.nl + Most changes 1.5-2.0: Jeroen Dirks - jeroend@tor.numetrix.com + Changes 3.2-4.0: Kjell Eikland - kjell.eikland@broadpark.no + (Simplex code, SOS, SC, code optimization) + Peter Notebaert - peno@mailme.org + (Sensitivity analysis, documentation) + Changes 5.0: Kjell Eikland - kjell.eikland@broadpark.no + (BFP, XLI, simplex, B&B, code modularization) + Peter Notebaert - peno@mailme.org + (New lp parser, VB/.NET interface, documentation) + + + Release notes: + + Version 4.0 enhances version 3.2 in terms of internal program/simplex + architecture, call level interfaces, data layout, features and contains + several bug fixes. There is now complete support for semi-continuous + variables and SOS constructions. In the process, a complete API + was added. The MPS parser has been amended to support this. + Sensitivity analysis and variouse bug fixes was provided by Peter + Notebaert in 4.0 sub-releases. Peter also wrote a complete + documentation of the API and contributed a VB interface, both of which + significantly enhanced the accessibility of lp_solve. + + Version 5.0 is a major rewrite and code cleanup. The main additions that + drove forward this cleanup were the modular inversion logic with optimal + column ordering, addition of primal phase 1 and dual phase 2 logic for + full flexibility in the selection of primal and dual simplex modes, + DEVEX and steepest edge pivot selection, along with dynamic cycling + detection and prevention. This cleanup made it possible to harmonize the + internal rounding principles, contributing to increased numerical stability. + + -------------------------------------------------------------------------- */ +/* Define user program feature option switches */ +/* ------------------------------------------------------------------------- */ + +#if !defined _WINDOWS && !defined _WIN32 && !defined WIN32 +# define _isnan(x) FALSE +#endif + +/* Utility/system settings */ +/* ------------------------------------------------------------------------- */ +/*#define INTEGERTIME */ /* Set use of lower-resolution timer */ + + +/* New v5.0+ simplex/optimization features and settings */ +/* ------------------------------------------------------------------------- */ +/*#define NoRowScaleOF */ /* Optionally skip row-scaling of the OF */ +#define PostScale /* Scale after presolve, rather than before */ +#define DoMatrixRounding /* Round A matrix elements to precision */ +/*#define DoBorderRounding */ /* Round RHS, bounds and ranges to precision */ +/*#define DoPresolveRoundingTight */ /* Use tighter rounding factor in presolve */ +/*#define AggressiveRowPresolve */ /* Extra row presolve (beware of accuracy) */ +#define DeferredCompactA /* Defer matrix row compacting to speed presolve */ +#define EnablePrimalPhase1 +#define Phase1EliminateRedundant /* Remove rows of redundant artificials */ +/*#define UseHarrisTwoPass */ /* Use two-pass primal Harris pivot selection */ +#define FixViolatedOptimal +#define ImproveSolutionPrecision /* Round optimal solution values */ +#define IncreasePivotOnReducedAccuracy /* Increase epspivot on instability */ +/*#define FinalOptimalErrorLimitation */ /* Refactorize on optimal solutions */ +/*#define FixInaccurateDualMinit */ /* Reinvert on inaccuracy in dual minits */ +/*#define EnforcePositiveTheta */ /* Ensure that the theta range is valid */ +#define ResetMinitOnReinvert +#define UseSparseReducedCost /* Typically slightly faster than dense version */ +#define PrimalPivotStickiness 0 /* 0 = v5 Beta distribution setting */ +#define DualPivotStickiness 1 /* 1 = v5 Beta distribution setting */ +/*#define UsePrimalReducedCostUpdate */ /* Not tested */ +/*#define UseDualReducedCostUpdate */ /* Seems Ok, but slower than expected */ +/*#ifdef UseLegacyExtrad */ /* Use v3.2- style Extrad method */ +#define UseRejectionList +/*#define EnableRandomizedPricing */ /* Add randomization factor to pricers */ +#define LegacySlackDefinition /* Slack as the "value of the constraint" */ +/*#define EnablePartialOptimization */ /* Detect and scan multi-stage models */ + + +/* Active inverse logic (default is optimized original etaPFI) */ +/* ------------------------------------------------------------------------- */ +#if !defined LoadInverseLib +# define LoadInverseLib TRUE /* Enable alternate inverse libraries */ +#endif +/* #define ExcludeNativeInverse */ /* Disable INVERSE_ACTIVE inverse engine */ + +#define INVERSE_NONE -1 +#define INVERSE_LEGACY 0 +#define INVERSE_ETAPFI 1 +#define INVERSE_LUMOD 2 +#define INVERSE_LUSOL 3 +#define INVERSE_GLPKLU 4 + +#ifndef RoleIsExternalInvEngine /* Defined in inverse DLL drivers */ + #ifdef ExcludeNativeInverse + #define INVERSE_ACTIVE INVERSE_NONE /* Disable native engine */ + #else + #define INVERSE_ACTIVE INVERSE_LEGACY /* User or DLL-selected */ + #endif +#endif + + +/* Active external language interface logic (default is none) */ +/* ------------------------------------------------------------------------- */ +#if !defined LoadLanguageLib +# define LoadLanguageLib TRUE /* Enable alternate language libraries */ +#endif +#define ExcludeNativeLanguage /* Disable LANGUAGE_ACTIVE XLI */ + +#define LANGUAGE_NONE -1 +#define LANGUAGE_LEGACYLP 0 +#define LANGUAGE_CPLEXLP 1 +#define LANGUAGE_MPSX 2 +#define LANGUAGE_LPFML 3 +#define LANGUAGE_MATHPROG 4 +#define LANGUAGE_AMPL 5 +#define LANGUAGE_GAMS 6 +#define LANGUAGE_MATLAB 7 + +#ifndef RoleIsExternalLanguageEngine /* Defined in XLI driver libraries */ + #ifdef ExcludeNativeLanguage + #define LANGUAGE_ACTIVE LANGUAGE_NONE /* Disable native engine */ + #else + #define LANGUAGE_ACTIVE LANGUAGE_CPLEXLP /* User or DLL-selected */ + #endif +#endif + + +/* Development features */ +/* ------------------------------------------------------------------------- */ +#define Prod_xA_RoundRelative /* Typically increases performance */ +/*#define DirectOverrideOF */ /* Enable experimental ability to override the OF */ +#define ForceEarlyBlandRule +/*#define EnableStallAntiDegen */ /* Do bound shifting in case of stalling */ +#define EnableAntiDegen /* Do bound shifting in case of degeneracies */ +/*#define EnableBranchingOnGUB */ /* Generalized upper bounded unit constraints */ +/*#define FastMIP */ /* Experimental acceleration of B&B logic with no OF row */ +#define FULLYBOUNDEDSIMPLEX FALSE /* WARNING: Activate at your own risk! */ + + +/* Default parameters and tolerances */ +/* ------------------------------------------------------------------------- */ +#define OriginalPARAM 0 +#define ProductionPARAM 1 +#define ChvatalPARAM 2 +#define TestPARAM 3 +#define ActivePARAM ProductionPARAM + + +/* Miscellaneous settings */ +/* ------------------------------------------------------------------------- */ +#ifndef Paranoia + #ifdef _DEBUG + #define Paranoia + #endif +#endif + + +/* Program version data */ +/* ------------------------------------------------------------------------- */ +#define MAJORVERSION 5 +#define MINORVERSION 0 +#define RELEASE 10 +#define BUILD 0 +#define BFPVERSION 5 /* Checked against bfp_compatible() */ +#define XLIVERSION 5 /* Checked against xli_compatible() */ + + +/* Include/header files */ +/* ------------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include + +#if (LoadInverseLib==TRUE) || (LoadLanguageLib == TRUE) + #ifdef WIN32 + #include + #else + #include + #endif +#endif + +#ifndef BFP_CALLMODEL + #ifdef WIN32 + #define BFP_CALLMODEL __stdcall /* "Standard" call model */ +/* #pragma pack(1) */ /* Need to do more checking of this */ + #else + #define BFP_CALLMODEL + #endif +#endif +#ifndef XLI_CALLMODEL + #define XLI_CALLMODEL BFP_CALLMODEL +#endif + +#define REGISTER register /* Speed up certain operations */ + +#include "lp_types.h" +#include "lp_utils.h" + +/* Definition of program constrants */ +/* ------------------------------------------------------------------------- */ +#define SIMPLEX_Phase1_PRIMAL 1 +#define SIMPLEX_Phase1_DUAL 2 +#define SIMPLEX_Phase2_PRIMAL 4 +#define SIMPLEX_Phase2_DUAL 8 +#define SIMPLEX_DYNAMIC 16 + +#define SIMPLEX_PRIMAL_PRIMAL SIMPLEX_Phase1_PRIMAL + SIMPLEX_Phase2_PRIMAL +#define SIMPLEX_DUAL_PRIMAL SIMPLEX_Phase1_DUAL + SIMPLEX_Phase2_PRIMAL +#define SIMPLEX_PRIMAL_DUAL SIMPLEX_Phase1_PRIMAL + SIMPLEX_Phase2_DUAL +#define SIMPLEX_DUAL_DUAL SIMPLEX_Phase1_DUAL + SIMPLEX_Phase2_DUAL +#define SIMPLEX_DEFAULT SIMPLEX_DUAL_PRIMAL + +#define ISREAL 0 +#define ISINTEGER 1 +#define ISSEMI 2 +#define ISSOS 4 +#define ISSOSTEMPINT 8 +#define ISGUB 16 + +#define ROWNAMEMASK "R%d" +#define ROWNAMEMASK2 "r%d" +#define COLNAMEMASK "C%d" +#define COLNAMEMASK2 "c%d" + +/* Presolve defines */ +#define PRESOLVE_NONE 0 +#define PRESOLVE_ROWS 1 +#define PRESOLVE_COLS 2 +#define PRESOLVE_LINDEP 4 +#define PRESOLVE_AGGREGATE 8 +#define PRESOLVE_SPARSER 16 +#define PRESOLVE_SOS 32 +#define PRESOLVE_REDUCEMIP 64 +#define PRESOLVE_LASTMASKMODE (PRESOLVE_DUALS - 1) +#define PRESOLVE_DUALS 128 +#define PRESOLVE_SENSDUALS 256 + +/* Basis crash options */ +#define CRASH_NOTHING 0 +#define CRASH_NONBASICBOUNDS 1 +#define CRASH_MOSTFEASIBLE 2 +#define CRASH_LEASTDEGENERATE 3 + +/* Solution recomputation options */ +#define INITSOL_SHIFTZERO 0 +#define INITSOL_USEZERO 1 +#define INITSOL_ORIGINAL 2 + +/* Strategy codes to avoid or recover from degenerate pivots, + infeasibility or numeric errors via randomized bound relaxation */ +#define ANTIDEGEN_NONE 0 +#define ANTIDEGEN_FIXEDVARS 1 +#define ANTIDEGEN_COLUMNCHECK 2 +#define ANTIDEGEN_STALLING 4 +#define ANTIDEGEN_NUMFAILURE 8 +#define ANTIDEGEN_LOSTFEAS 16 +#define ANTIDEGEN_INFEASIBLE 32 +#define ANTIDEGEN_DYNAMIC 64 +#define ANTIDEGEN_DURINGBB 128 +#define ANTIDEGEN_DEFAULT (ANTIDEGEN_FIXEDVARS | ANTIDEGEN_STALLING | ANTIDEGEN_INFEASIBLE) + +/* REPORT defines */ +#define NEUTRAL 0 +#define CRITICAL 1 +#define SEVERE 2 +#define IMPORTANT 3 +#define NORMAL 4 +#define DETAILED 5 +#define FULL 6 + +/* MESSAGE defines */ +#define MSG_NONE 0 +#define MSG_PRESOLVE 1 +#define MSG_ITERATION 2 +#define MSG_INVERT 4 +#define MSG_LPFEASIBLE 8 +#define MSG_LPOPTIMAL 16 +#define MSG_LPEQUAL 32 +#define MSG_LPBETTER 64 +#define MSG_MILPFEASIBLE 128 +#define MSG_MILPEQUAL 256 +#define MSG_MILPBETTER 512 +#define MSG_MILPSTRATEGY 1024 +#define MSG_MILPOPTIMAL 2048 +#define MSG_PERFORMANCE 4096 +#define MSG_INITPSEUDOCOST 8192 + +/* MPS defines */ +#define MPSUNDEF -2 +#define MPSNAME -1 +#define MPSROWS 0 +#define MPSCOLUMNS 1 +#define MPSRHS 2 +#define MPSBOUNDS 3 +#define MPSRANGES 4 +#define MPSSOS 5 +#define MPSVARMASK "%-8s" +#define MPSVALUEMASK "%12g" + +/* Constraint type codes */ +#define ROWTYPE_EMPTY 0 +#define ROWTYPE_LE 1 +#define ROWTYPE_GE 2 +#define ROWTYPE_EQ 3 +#define ROWTYPE_CONSTRAINT ROWTYPE_EQ /* This is the mask for modes */ +#define ROWTYPE_OF 4 +#define ROWTYPE_INACTIVE 8 +#define ROWTYPE_RELAX 16 +#define ROWTYPE_GUB 32 +#define ROWTYPE_OFMAX (ROWTYPE_OF + ROWTYPE_GE) +#define ROWTYPE_OFMIN (ROWTYPE_OF + ROWTYPE_LE) +#define ROWTYPE_CHSIGN ROWTYPE_GE +#define LE ROWTYPE_LE +#define GE ROWTYPE_GE +#define EQ ROWTYPE_EQ +#define OF ROWTYPE_OF + +/* Column subsets */ +#define SCAN_USERVARS 1 +#define SCAN_SLACKVARS 2 +#define SCAN_ARTIFICIALVARS 4 +#define SCAN_PARTIALBLOCK 8 +#define USE_BASICVARS 16 +#define USE_NONBASICVARS 32 +#define SCAN_NORMALVARS (SCAN_USERVARS + SCAN_ARTIFICIALVARS) +#define SCAN_ALLVARS (SCAN_SLACKVARS + SCAN_USERVARS + SCAN_ARTIFICIALVARS) +#define USE_ALLVARS (USE_BASICVARS + USE_NONBASICVARS) +#define OMIT_FIXED 64 +#define OMIT_NONFIXED 128 + +/* Improvement defines */ +#define IMPROVE_NONE 0 +#define IMPROVE_FTRAN 1 +#define IMPROVE_BTRAN 2 +#define IMPROVE_SOLVE (IMPROVE_FTRAN + IMPROVE_BTRAN) +#define IMPROVE_INVERSE 4 + +/* Scaling types */ +#define SCALE_NONE 0 +#define SCALE_EXTREME 1 +#define SCALE_RANGE 2 +#define SCALE_MEAN 3 +#define SCALE_GEOMETRIC 4 +#define SCALE_FUTURE1 5 +#define SCALE_FUTURE2 6 +#define SCALE_CURTISREID 7 /* Override to Curtis-Reid "optimal" scaling */ + +/* Alternative scaling weights */ +#define SCALE_LINEAR 0 +#define SCALE_QUADRATIC 8 +#define SCALE_LOGARITHMIC 16 +#define SCALE_USERWEIGHT 31 +#define SCALE_MAXTYPE (SCALE_QUADRATIC-1) + +/* Scaling modes */ +#define SCALE_POWER2 32 /* As is or rounded to power of 2 */ +#define SCALE_EQUILIBRATE 64 /* Make sure that no scaled number is above 1 */ +#define SCALE_INTEGERS 128 /* Apply to integer columns/variables */ + +/* Standard defines for typical scaling models (no Lagrangeans) */ +#define SCALEMODEL_EQUILIBRATED (SCALE_LINEAR+SCALE_EXTREME+SCALE_INTEGERS) +#define SCALEMODEL_GEOMETRIC (SCALE_LINEAR+SCALE_GEOMETRIC+SCALE_INTEGERS) +#define SCALEMODEL_ARITHMETIC (SCALE_LINEAR+SCALE_MEAN+SCALE_INTEGERS) +#define SCALEMODEL_DYNAMIC (SCALEMODEL_GEOMETRIC+SCALE_EQUILIBRATE) +#define SCALEMODEL_CURTISREID (SCALE_CURTISREID+SCALE_INTEGERS+SCALE_POWER2) + +/* Iteration status and strategies */ +#define ITERATE_MAJORMAJOR 0 +#define ITERATE_MINORMAJOR 1 +#define ITERATE_MINORRETRY 2 + +/* Pricing methods */ +#define PRICER_FIRSTINDEX 0 +#define PRICER_DANTZIG 1 +#define PRICER_DEVEX 2 +#define PRICER_STEEPESTEDGE 3 +#define PRICER_LASTOPTION PRICER_STEEPESTEDGE + +/* Additional settings for DEVEX */ +#define DEVEX_ENHANCED FALSE /* Set to FALSE to always restart at unit weights */ +#define DEVEX_RESTARTLIMIT 1.0e+09 /* Reet the norms if any value exceeds this limit */ +#define DEVEX_MINVALUE 0.000 /* Minimum weight [0..1] for entering variable, consider 0.01 */ + +/* Pricing strategies */ +#define PRICE_METHODDEFAULT 0 +#define PRICE_PRIMALFALLBACK 4 /* In case of Steepest Edge, fall back to DEVEX in primal */ +#define PRICE_MULTIPLE 8 /* Enable multiple pricing */ +#define PRICE_PARTIAL 16 /* Enable partial pricing */ +#define PRICE_ADAPTIVE 32 /* Temporarily use First Index if cycling is detected */ +#define PRICE_HYBRID 64 /* NOT IMPLEMENTED */ +#define PRICE_RANDOMIZE 128 /* Adds a small randomization effect to the selected pricer */ +#define PRICE_AUTOPARTIALCOLS 256 /* Detect and use data on the block structure of the model */ +#define PRICE_AUTOPARTIALROWS 512 /* Detect and use data on the block structure of the model */ +#define PRICE_LOOPLEFT 1024 /* Scan entering/leaving columns left rather than right */ +#define PRICE_LOOPALTERNATE 2048 /* Scan entering/leaving columns alternatingly left/right */ + +#define PRICE_AUTOPARTIAL (PRICE_AUTOPARTIALCOLS + PRICE_AUTOPARTIALROWS) +#define PRICE_STRATEGYMASK (PRICE_METHODDEFAULT + PRICE_PRIMALFALLBACK + \ + PRICE_MULTIPLE + PRICE_PARTIAL + \ + PRICE_ADAPTIVE + PRICE_HYBRID + \ + PRICE_RANDOMIZE + PRICE_AUTOPARTIAL + \ + PRICE_LOOPLEFT + PRICE_LOOPALTERNATE) +#define PRICER_RANDFACT 0.1 + +/* B&B active variable codes */ +#define BB_REAL 0 +#define BB_INT 1 +#define BB_SC 2 +#define BB_SOS 3 +#define BB_GUB 4 + +/* B&B strategies */ +#define NODE_FIRSTSELECT 0 +#define NODE_GAPSELECT 1 +#define NODE_RANGESELECT 2 +#define NODE_FRACTIONSELECT 3 +#define NODE_PSEUDOCOSTSELECT 4 +#define NODE_PSEUDONONINTSELECT 5 /* Kjell Eikland #1 - Minimize B&B depth */ +#define NODE_PSEUDORATIOSELECT 6 /* Kjell Eikland #2 - Minimize a "cost/benefit" ratio */ +#define NODE_USERSELECT 7 +#define NODE_STRATEGYMASK (NODE_WEIGHTREVERSEMODE-1) /* Mask for B&B strategies */ +#define NODE_WEIGHTREVERSEMODE 8 +#define NODE_BRANCHREVERSEMODE 16 +#define NODE_GREEDYMODE 32 +#define NODE_PSEUDOCOSTMODE 64 +#define NODE_DEPTHFIRSTMODE 128 +#define NODE_RANDOMIZEMODE 256 +#define NODE_GUBMODE 512 +#define NODE_DYNAMICMODE 1024 +#define NODE_RESTARTMODE 2048 +#define NODE_PSEUDOFEASSELECT (NODE_PSEUDONONINTSELECT+NODE_WEIGHTREVERSEMODE) + +#define BRANCH_CEILING 0 +#define BRANCH_FLOOR 1 +#define BRANCH_AUTOMATIC 2 +#define BRANCH_DEFAULT 3 + +/* Action constrants, mainly for B&B node-solving */ +#define ACTION_NONE 0 +#define ACTION_ACTIVE 1 +#define ACTION_REBASE 2 +#define ACTION_RECOMPUTE 4 +#define ACTION_REPRICE 8 +#define ACTION_REINVERT 16 +#define ACTION_RESTART 255 + +/* Solver status values */ +#define UNKNOWNERROR -5 +#define DATAIGNORED -4 +#define NOBFP -3 +#define NOMEMORY -2 +#define NOTRUN -1 +#define OPTIMAL 0 +#define SUBOPTIMAL 1 +#define INFEASIBLE 2 +#define UNBOUNDED 3 +#define DEGENERATE 4 +#define NUMFAILURE 5 +#define USERABORT 6 +#define TIMEOUT 7 +#define RUNNING 8 +#define FUTURESTATUS 9 + +/* Branch & Bound and Lagrangean extra status values */ +#define PROCFAIL 10 +#define PROCBREAK 11 +#define FEASFOUND 12 +#define NOFEASFOUND 13 + +/* Status values internal to the solver */ +#define SWITCH_TO_PRIMAL 20 +#define SWITCH_TO_DUAL 21 +#define SINGULAR_BASIS 22 +#define LOSTFEAS 23 + + +/* Name list and sparse matrix storage parameters */ +#define MAT_START_SIZE 10000 +#define DELTACOLALLOC 50 +#define DELTAROWALLOC 20 +#define RESIZEFACTOR 4 /* Fractional increase in selected memory allocations */ + +/* Solver parameters and tolerances */ +#define DEF_PARTIALBLOCKS 10 /* The default number of blocks for partial pricing */ +#define DEF_MAXRELAX 4 /* Maximum number of non-BB relaxations in MILP */ +#define DEF_MAXPIVOTRETRY 10 /* Maximum number of times to retry a div-0 situation */ +#define DEF_MAXSINGULARITIES 10 /* Maximum number of singularities in refactorization */ +#define MAX_MINITUPDATES 60 /* Maximum number of bound swaps between refactorizations + without recomputing the whole vector - contain errors */ +#define MIN_REFACTFREQUENCY 5 /* Refactorization frequency indicating an inherent + numerical instability of the basis */ +#define LAG_SINGULARLIMIT 5 /* Number of times the objective does not change + before it is assumed that the Lagrangean constraints + are non-binding, and therefore impossible to converge; + upper iteration limit is divided by this threshold */ +#define MIN_TIMEPIVOT 5.0e-02 /* Minimum time per pivot for reinversion optimization + purposes; use active monitoring only if a pivot + takes more than MINTIMEPIVOT seconds. 5.0e-2 is + roughly suitable for a 1GHz system. */ +#define ANTICYCLEBLAND TRUE /* Decide if the primal and dual simplex algorithms should + detect and counteract cycling by using the smallest + subscript (Bland's) rule */ +#define MAX_STALLCOUNT 12 /* The absolute upper limit to the number of stalling or + cycling iterations before switching to the smallest + subscript (Bland's) rule */ +#define MAX_BLANDSWITCH 5 /* The maximum number of times to try Bland's rule to + recover from stalling situations; set negative for no limit. */ + +#define DEF_SCALINGLIMIT 5 /* The default maximum number of scaling iterations */ + +#define DEF_NEGRANGE -1.0e+06 /* Downward limit for expanded variable range before the + variable is split into positive and negative components */ +#ifndef DEF_SPLITNEGVARS +#define DEF_SPLITNEGVARS AUTOMATIC /* split negative variables in a positive and negative component + Can be TRUE or AUTOMATIC + TRUE: Always split negative variables + AUTOMATIC: Only split when negative lower bound and positive + upper bound */ +#endif +#define DEF_BB_LIMITLEVEL -50 /* Relative B&B limit to protect against very deep, + memory-consuming trees */ +#define MAX_BB_FULLBOUNDSAVE 101 /* MIP variable count above which it is attempted to do + compression of lower and upper bounds in B&B phases */ + +#define RANDSCALE 100 /* Randomization scaling range */ +#define DOUBLEROUND 0.0e-02 /* Extra rounding scalar used in btran/ftran calculations; the + rationale for 0.0 is that prod_xA() uses rounding as well */ +#define DEF_EPSMACHINE 2.22e-16 /* Machine relative precision (doubles) */ + + +#if ActivePARAM==ProductionPARAM /* PARAMETER SET FOR PRODUCTION */ +/* -------------------------------------------------------------------------------------- */ +#define DEF_INFINITE 1.0e+30 /* Limit for dynamic range */ +#define DEF_EPSVALUE 1.0e-12 /* For rounding other values (vectors) to 0 */ +#define DEF_EPSPRIMAL 1.0e-10 /* For rounding primal/RHS values to 0 */ +#define DEF_EPSDUAL 1.0e-09 /* 1.0e-10 For rounding reduced costs to 0 */ +#define DEF_EPSPIVOT 5.0e-06 /* Pivot reject threshold (must be <= epsint) */ +#define DEF_PERTURB 1.0e-05 /* Perturbation scalar for degenerate problems; + must at least be RANDSCALE greater than EPSPRIMAL */ +#define DEF_EPSSOLUTION 1.0e-05 /* Margin of error for solution bounds */ +#define DEF_EPSINT 1.0e-06 /* Accuracy for considering a float value as integer + (must be >= epspivot) */ + +#elif ActivePARAM==OriginalPARAM /* PARAMETER SET FOR LEGACY VERSIONS */ +/* -------------------------------------------------------------------------------------- */ +#define DEF_INFINITE 1.0e+24 /* Limit for dynamic range */ +#define DEF_EPSVALUE 1.0e-08 /* For rounding other values (vectors) to 0 */ +#define DEF_EPSPRIMAL 5.01e-07 /* For rounding primal/RHS values to 0, infeasibility */ +#define DEF_EPSDUAL 1.0e-06 /* For rounding reduced costs to 0 */ +#define DEF_EPSPIVOT 1.0e-04 /* Pivot reject threshold (must be <= epsint) */ +#define DEF_PERTURB 1.0e-05 /* Perturbation scalar for degenerate problems; + must at least be RANDSCALE greater than EPSPRIMAL */ +#define DEF_EPSSOLUTION 1.0e-02 /* Margin of error for solution bounds */ +#define DEF_EPSINT 1.0e-03 /* Accuracy for considering a float value as integer + (must be >= epspivot) */ + +#elif ActivePARAM==ChvatalPARAM /* PARAMETER SET EXAMPLES FROM Vacek Chvatal */ +/* -------------------------------------------------------------------------------------- */ +#define DEF_INFINITE 1.0e+24 /* Limit for dynamic range */ +#define DEF_EPSVALUE 1.0e-10 /* For rounding other values (vectors) to 0 */ +#define DEF_EPSPRIMAL 10e-07 /* For rounding primal/RHS values to 0 */ +#define DEF_EPSDUAL 10e-05 /* For rounding reduced costs to 0 */ +#define DEF_EPSPIVOT 10e-05 /* Pivot reject threshold (must be <= epsint) */ +#define DEF_PERTURB 10e-03 /* Perturbation scalar for degenerate problems; + must at least be RANDSCALE greater than EPSPRIMAL */ +#define DEF_EPSSOLUTION 1.0e-05 /* Margin of error for solution bounds */ +#define DEF_EPSINT 1.0e-05 /* Accuracy for considering a float value as integer + (must be >= epspivot) */ + +#elif ActivePARAM==TestPARAM /* PARAMETER SET FOR PERFORMANCE TESTING */ +/* -------------------------------------------------------------------------------------- */ +#define DEF_INFINITE 1.0e+24 /* Limit for dynamic range */ +#define DEF_EPSVALUE 1.0e-10 /* For rounding other values (vectors) to 0 */ +#define DEF_EPSPRIMAL 5.01e-08 /* For rounding primal/RHS values to 0 */ +#define DEF_EPSDUAL 1.0e-07 /* For rounding reduced costs to 0 */ +#define DEF_EPSPIVOT 1.0e-05 /* Pivot reject threshold (must be <= epsint) */ +#define DEF_PERTURB 1.0e-05 /* Perturbation scalar for degenerate problems; + must at least be RANDSCALE greater than EPSPRIMAL */ +#define DEF_EPSSOLUTION 1.0e-05 /* Margin of error for solution bounds */ +#define DEF_EPSINT 1.0e-04 /* Accuracy for considering a float value as integer + (must be >= epspivot) */ + +#endif + + +#define DEF_MIP_GAP 1.0e-09 /* The default absolute and relative MIP gap */ +#define SCALEDINTFIXRANGE 1.6 /* Epsilon range multiplier < 2 for collapsing bounds to fix */ + +#define MIN_SCALAR 1.0e-10 /* Smallest allowed scaling adjustment */ +#define MAX_SCALAR 1.0e+10 /* Largest allowed scaling adjustment */ +#define DEF_SCALINGEPS 1.0e-02 /* Relative scaling convergence criterion for auto_scale */ + +#define DEF_LAGACCEPT 1.0e-03 /* Default Lagrangean convergence acceptance criterion */ +#define DEF_LAGCONTRACT 0.90 /* The contraction parameter for Lagrangean iterations */ +#define DEF_LAGMAXITERATIONS 100 /* The maximum number of Lagrangean iterations */ + +#define DEF_PSEUDOCOSTUPDATES 7 /* The default number of times pseudo-costs are recalculated; + experiments indicate that costs tend to stabilize */ +#define DEF_PSEUDOCOSTRESTART 0.15 /* The fraction of price updates required for B&B restart + when the mode is NODE_RESTARTMODE */ + + +/* Hashing prototypes and function headers */ +/* ------------------------------------------------------------------------- */ +#include "lp_Hash.h" + + +/* Sparse matrix prototypes */ +/* ------------------------------------------------------------------------- */ +#include "lp_matrix.h" + + +/* Basis storage (mainly for B&B) */ +typedef struct _basisrec +{ + int level; + int *basis_var; + MYBOOL *var_is_basic; + MYBOOL *bound_is_lower; + struct _basisrec *previous; +} basisrec; + +/* Pseudo-cost arrays used during B&B */ +typedef struct _BBPSrec +{ + int updatelimit; + int updatesfinished; + REAL restartlimit; + MATitem *UPcost; + MATitem *LOcost; +} BBPSrec; + +#include "lp_mipbb.h" + + +/* Partial pricing block data */ +typedef struct _partialrec { + lprec *lp; + int blockcount; /* ## The number of logical blocks or stages in the model */ + int blocknow; /* The currently active block */ + int *blockend; /* Array of column indeces giving the start of each block */ + int *blockpos; /* Array of column indeces giving the start scan position */ + MYBOOL isrow; +} partialrec; + + +/* Specially Ordered Sets (SOS) prototypes and settings */ +/* ------------------------------------------------------------------------- */ +/* SOS storage structure (LINEARSEARCH is typically in the 0-10 range) */ +#ifndef LINEARSEARCH +#define LINEARSEARCH 0 +#endif + +#include "lp_SOS.h" + + +/* Prototypes for call-back functions */ +/* ------------------------------------------------------------------------- */ +typedef int (__WINAPI ctrlcfunc)(lprec *lp, void *userhandle); +typedef void (__WINAPI logfunc)(lprec *lp, void *userhandle, char *buf); +typedef void (__WINAPI debugfunc)(lprec *lp, void *userhandle, char *buf); +typedef void (__WINAPI msgfunc)(lprec *lp, void *userhandle, int message); +typedef MYBOOL (__WINAPI abortfunc)(lprec *lp, int message); + + +/* API typedef definitions */ +/* ------------------------------------------------------------------------- */ +typedef MYBOOL (__WINAPI add_column_func)(lprec *lp, REAL *column); +typedef MYBOOL (__WINAPI add_columnex_func)(lprec *lp, int count, REAL *column, int *rowno); +typedef MYBOOL (__WINAPI add_constraint_func)(lprec *lp, REAL *row, int constr_type, REAL rh); +typedef MYBOOL (__WINAPI add_constraintex_func)(lprec *lp, int count, REAL *row, int *colno, int constr_type, REAL rh); +typedef MYBOOL (__WINAPI add_lag_con_func)(lprec *lp, REAL *row, int con_type, REAL rhs); +typedef int (__WINAPI add_SOS_func)(lprec *lp, char *name, int sostype, int priority, int count, int *sosvars, REAL *weights); +typedef int (__WINAPI column_in_lp_func)(lprec *lp, REAL *column); +typedef void (__WINAPI default_basis_func)(lprec *lp); +typedef MYBOOL (__WINAPI del_column_func)(lprec *lp, int column); +typedef MYBOOL (__WINAPI del_constraint_func)(lprec *lp,int del_row); +typedef void (__WINAPI delete_lp_func)(lprec *lp); +typedef void (__WINAPI free_lp_func)(lprec **plp); +typedef int (__WINAPI get_anti_degen_func)(lprec *lp); +typedef void (__WINAPI get_basis_func)(lprec *lp, int *bascolumn, MYBOOL nonbasic); +typedef int (__WINAPI get_basiscrash_func)(lprec *lp); +typedef int (__WINAPI get_bb_depthlimit_func)(lprec *lp); +typedef int (__WINAPI get_bb_floorfirst_func)(lprec *lp); +typedef int (__WINAPI get_bb_rule_func)(lprec *lp); +typedef MYBOOL (__WINAPI get_bounds_tighter_func)(lprec *lp); +typedef REAL (__WINAPI get_break_at_value_func)(lprec *lp); +typedef char * (__WINAPI get_col_name_func)(lprec *lp, int column); +typedef MYBOOL (__WINAPI get_column_func)(lprec *lp, int col_nr, REAL *column); +typedef int (__WINAPI get_constr_type_func)(lprec *lp, int row); +typedef MYBOOL (__WINAPI get_constraints_func)(lprec *lp, REAL *constr); +typedef MYBOOL (__WINAPI get_dual_solution_func)(lprec *lp, REAL *rc); +typedef REAL (__WINAPI get_epsb_func)(lprec *lp); +typedef REAL (__WINAPI get_epsd_func)(lprec *lp); +typedef REAL (__WINAPI get_epsel_func)(lprec *lp); +typedef REAL (__WINAPI get_epsilon_func)(lprec *lp); +typedef REAL (__WINAPI get_epsperturb_func)(lprec *lp); +typedef REAL (__WINAPI get_epspivot_func)(lprec *lp); +typedef int (__WINAPI get_improve_func)(lprec *lp); +typedef REAL (__WINAPI get_infinite_func)(lprec *lp); +typedef MYBOOL (__WINAPI get_lambda_func)(lprec *lp, REAL *lambda); +typedef REAL (__WINAPI get_lowbo_func)(lprec *lp, int column); +typedef int (__WINAPI get_lp_index_func)(lprec *lp, int orig_index); +typedef char * (__WINAPI get_lp_name_func)(lprec *lp); +typedef int (__WINAPI get_Lrows_func)(lprec *lp); +typedef REAL (__WINAPI get_mat_func)(lprec *lp, int row, int column); +typedef int (__WINAPI get_max_level_func)(lprec *lp); +typedef int (__WINAPI get_maxpivot_func)(lprec *lp); +typedef REAL (__WINAPI get_mip_gap_func)(lprec *lp, MYBOOL absolute); +typedef int (__WINAPI get_multiprice_func)(lprec *lp, MYBOOL getabssize); +typedef int (__WINAPI get_Ncolumns_func)(lprec *lp); +typedef REAL (__WINAPI get_negrange_func)(lprec *lp); +typedef int (__WINAPI get_Norig_columns_func)(lprec *lp); +typedef int (__WINAPI get_Norig_rows_func)(lprec *lp); +typedef int (__WINAPI get_Nrows_func)(lprec *lp); +typedef REAL (__WINAPI get_obj_bound_func)(lprec *lp); +typedef REAL (__WINAPI get_objective_func)(lprec *lp); +typedef int (__WINAPI get_orig_index_func)(lprec *lp, int lp_index); +typedef char * (__WINAPI get_origcol_name_func)(lprec *lp, int column); +typedef char * (__WINAPI get_origrow_name_func)(lprec *lp, int row); +typedef void (__WINAPI get_partialprice_func)(lprec *lp, int *blockcount, int *blockstart, MYBOOL isrow); +typedef int (__WINAPI get_pivoting_func)(lprec *lp); +typedef int (__WINAPI get_presolve_func)(lprec *lp); +typedef MYBOOL (__WINAPI get_primal_solution_func)(lprec *lp, REAL *pv); +typedef int (__WINAPI get_print_sol_func)(lprec *lp); +typedef MYBOOL (__WINAPI get_PseudoCosts_func)(lprec *lp, REAL *clower, REAL *cupper, int *updatelimit); +typedef MYBOOL (__WINAPI get_ptr_constraints_func)(lprec *lp, REAL **constr); +typedef MYBOOL (__WINAPI get_ptr_dual_solution_func)(lprec *lp, REAL **rc); +typedef MYBOOL (__WINAPI get_ptr_lambda_func)(lprec *lp, REAL **lambda); +typedef MYBOOL (__WINAPI get_ptr_primal_solution_func)(lprec *lp, REAL **pv); +typedef MYBOOL (__WINAPI get_ptr_sensitivity_obj_func)(lprec *lp, REAL **objfrom, REAL **objtill); +typedef MYBOOL (__WINAPI get_ptr_sensitivity_rhs_func)(lprec *lp, REAL **duals, REAL **dualsfrom, REAL **dualstill); +typedef MYBOOL (__WINAPI get_ptr_variables_func)(lprec *lp, REAL **var); +typedef REAL (__WINAPI get_rh_func)(lprec *lp, int row); +typedef REAL (__WINAPI get_rh_range_func)(lprec *lp, int row); +typedef MYBOOL (__WINAPI get_row_func)(lprec *lp, int row_nr, REAL *row); +typedef char * (__WINAPI get_row_name_func)(lprec *lp, int row); +typedef REAL (__WINAPI get_scalelimit_func)(lprec *lp); +typedef int (__WINAPI get_scaling_func)(lprec *lp); +typedef MYBOOL (__WINAPI get_sensitivity_obj_func)(lprec *lp, REAL *objfrom, REAL *objtill); +typedef MYBOOL (__WINAPI get_sensitivity_rhs_func)(lprec *lp, REAL *duals, REAL *dualsfrom, REAL *dualstill); +typedef int (__WINAPI get_simplextype_func)(lprec *lp); +typedef int (__WINAPI get_solutioncount_func)(lprec *lp); +typedef int (__WINAPI get_solutionlimit_func)(lprec *lp); +typedef int (__WINAPI get_splitnegvars_func)(lprec *lp); +typedef const char * (__WINAPI get_statustext_func)(lprec *lp, int statuscode); +typedef long (__WINAPI get_timeout_func)(lprec *lp); +typedef int (__WINAPI get_total_iter_func)(lprec *lp); +typedef int (__WINAPI get_total_nodes_func)(lprec *lp); +typedef REAL (__WINAPI get_upbo_func)(lprec *lp, int column); +typedef int (__WINAPI get_var_branch_func)(lprec *lp, int column); +typedef REAL (__WINAPI get_var_dualresult_func)(lprec *lp, int index); +typedef REAL (__WINAPI get_var_primalresult_func)(lprec *lp, int index); +typedef int (__WINAPI get_var_priority_func)(lprec *lp, int column); +typedef MYBOOL (__WINAPI get_variables_func)(lprec *lp, REAL *var); +typedef int (__WINAPI get_verbose_func)(lprec *lp); +typedef REAL (__WINAPI get_working_objective_func)(lprec *lp); +typedef MYBOOL (__WINAPI has_BFP_func)(lprec *lp); +typedef MYBOOL (__WINAPI has_XLI_func)(lprec *lp); +typedef MYBOOL (__WINAPI is_add_rowmode_func)(lprec *lp); +typedef MYBOOL (__WINAPI is_anti_degen_func)(lprec *lp, int testmask); +typedef MYBOOL (__WINAPI is_binary_func)(lprec *lp, int column); +typedef MYBOOL (__WINAPI is_break_at_first_func)(lprec *lp); +typedef MYBOOL (__WINAPI is_constr_type_func)(lprec *lp, int row, int mask); +typedef MYBOOL (__WINAPI is_debug_func)(lprec *lp); +typedef MYBOOL (__WINAPI is_feasible_func)(lprec *lp, REAL *values, REAL threshold); +typedef MYBOOL (__WINAPI is_free_func)(lprec *lp, int column); +typedef MYBOOL (__WINAPI is_int_func)(lprec *lp, int column); +typedef MYBOOL (__WINAPI is_integerscaling_func)(lprec *lp); +typedef MYBOOL (__WINAPI is_lag_trace_func)(lprec *lp); +typedef MYBOOL (__WINAPI is_maxim_func)(lprec *lp); +typedef MYBOOL (__WINAPI is_nativeBFP_func)(lprec *lp); +typedef MYBOOL (__WINAPI is_nativeXLI_func)(lprec *lp); +typedef MYBOOL (__WINAPI is_negative_func)(lprec *lp, int column); +typedef MYBOOL (__WINAPI is_piv_mode_func)(lprec *lp, int testmask); +typedef MYBOOL (__WINAPI is_piv_rule_func)(lprec *lp, int rule); +typedef MYBOOL (__WINAPI is_presolve_func)(lprec *lp, int testmask); +typedef MYBOOL (__WINAPI is_scalemode_func)(lprec *lp, int testmask); +typedef MYBOOL (__WINAPI is_scaletype_func)(lprec *lp, int scaletype); +typedef MYBOOL (__WINAPI is_semicont_func)(lprec *lp, int column); +typedef MYBOOL (__WINAPI is_SOS_var_func)(lprec *lp, int column); +typedef MYBOOL (__WINAPI is_trace_func)(lprec *lp); +typedef void (__WINAPI lp_solve_version_func)(int *majorversion, int *minorversion, int *release, int *build); +typedef lprec * (__WINAPI make_lp_func)(int rows, int columns); +typedef void (__WINAPI print_constraints_func)(lprec *lp, int columns); +typedef MYBOOL (__WINAPI print_debugdump_func)(lprec *lp, char *filename); +typedef void (__WINAPI print_duals_func)(lprec *lp); +typedef void (__WINAPI print_lp_func)(lprec *lp); +typedef void (__WINAPI print_objective_func)(lprec *lp); +typedef void (__WINAPI print_scales_func)(lprec *lp); +typedef void (__WINAPI print_solution_func)(lprec *lp, int columns); +typedef void (__WINAPI print_str_func)(lprec *lp, char *str); +typedef void (__WINAPI print_tableau_func)(lprec *lp); +typedef void (__WINAPI put_abortfunc_func)(lprec *lp, ctrlcfunc newctrlc, void *ctrlchandle); +typedef void (__WINAPI put_logfunc_func)(lprec *lp, logfunc newlog, void *loghandle); +typedef void (__WINAPI put_msgfunc_func)(lprec *lp, msgfunc newmsg, void *msghandle, int mask); +typedef lprec * (__WINAPI read_LP_func)(char *filename, int verbose, char *lp_name); +typedef lprec * (__WINAPI read_lp_func)(FILE *filename, int verbose, char *lp_name); +typedef lprec * (__WINAPI read_LPT_func)(char *filename, int verbose, char *lp_name); +typedef lprec * (__WINAPI read_lpt_func)(FILE *filename, int verbose, char *lp_name); +typedef lprec * (__WINAPI read_MPS_func)(char *filename, int verbose); +typedef lprec * (__WINAPI read_mps_func)(FILE *filename, int verbose); +typedef lprec * (__WINAPI read_freeMPS_func)(char *filename, int verbose); +typedef lprec * (__WINAPI read_freemps_func)(FILE *filename, int verbose); +typedef lprec * (__WINAPI read_XLI_func)(char *xliname, char *modelname, char *dataname, char *options, int verbose); +typedef void (__WINAPI reset_basis_func)(lprec *lp); +typedef MYBOOL (__WINAPI set_add_rowmode_func)(lprec *lp, MYBOOL turnon); +typedef void (__WINAPI set_anti_degen_func)(lprec *lp, int anti_degen); +typedef MYBOOL (__WINAPI set_basis_func)(lprec *lp, int *bascolumn, MYBOOL nonbasic); +typedef void (__WINAPI set_basiscrash_func)(lprec *lp, int mode); +typedef void (__WINAPI set_bb_depthlimit_func)(lprec *lp, int bb_maxlevel); +typedef void (__WINAPI set_bb_floorfirst_func)(lprec *lp, int bb_floorfirst); +typedef void (__WINAPI set_bb_rule_func)(lprec *lp, int bb_rule); +typedef MYBOOL (__WINAPI set_BFP_func)(lprec *lp, char *filename); +typedef MYBOOL (__WINAPI set_binary_func)(lprec *lp, int column, MYBOOL must_be_bin); +typedef MYBOOL (__WINAPI set_bounds_func)(lprec *lp, int column, REAL lower, REAL upper); +typedef void (__WINAPI set_bounds_tighter_func)(lprec *lp, MYBOOL tighten); +typedef void (__WINAPI set_break_at_first_func)(lprec *lp, MYBOOL break_at_first); +typedef void (__WINAPI set_break_at_value_func)(lprec *lp, REAL break_at_value); +typedef MYBOOL (__WINAPI set_col_name_func)(lprec *lp, int column, char *new_name); +typedef MYBOOL (__WINAPI set_constr_type_func)(lprec *lp, int row, int con_type); +typedef void (__WINAPI set_debug_func)(lprec *lp, MYBOOL debug); +typedef void (__WINAPI set_epsb_func)(lprec *lp, REAL epsb); +typedef void (__WINAPI set_epsd_func)(lprec *lp, REAL epsd); +typedef void (__WINAPI set_epsel_func)(lprec *lp, REAL epsel); +typedef void (__WINAPI set_epsilon_func)(lprec *lp, REAL epsilon); +typedef void (__WINAPI set_epsperturb_func)(lprec *lp, REAL epsperturb); +typedef void (__WINAPI set_epspivot_func)(lprec *lp, REAL epspivot); +typedef MYBOOL (__WINAPI set_free_func)(lprec *lp, int column); +typedef void (__WINAPI set_improve_func)(lprec *lp, int improve); +typedef void (__WINAPI set_infinite_func)(lprec *lp, REAL infinite); +typedef MYBOOL (__WINAPI set_int_func)(lprec *lp, int column, MYBOOL must_be_int); +typedef void (__WINAPI set_lag_trace_func)(lprec *lp, MYBOOL lag_trace); +typedef MYBOOL (__WINAPI set_lowbo_func)(lprec *lp, int column, REAL value); +typedef MYBOOL (__WINAPI set_lp_name_func)(lprec *lp, char *lpname); +typedef MYBOOL (__WINAPI set_mat_func)(lprec *lp, int row, int column, REAL value); +typedef void (__WINAPI set_maxim_func)(lprec *lp); +typedef void (__WINAPI set_maxpivot_func)(lprec *lp, int max_num_inv); +typedef void (__WINAPI set_minim_func)(lprec *lp); +typedef void (__WINAPI set_mip_gap_func)(lprec *lp, MYBOOL absolute, REAL mip_gap); +typedef MYBOOL (__WINAPI set_multiprice_func)(lprec *lp, int multiblockdiv); +typedef void (__WINAPI set_negrange_func)(lprec *lp, REAL negrange); +typedef MYBOOL (__WINAPI set_obj_func)(lprec *lp, int Column, REAL Value); +typedef void (__WINAPI set_obj_bound_func)(lprec *lp, REAL obj_bound); +typedef MYBOOL (__WINAPI set_obj_fn_func)(lprec *lp, REAL *row); +typedef MYBOOL (__WINAPI set_obj_fnex_func)(lprec *lp, int count, REAL *row, int *colno); +typedef MYBOOL (__WINAPI set_outputfile_func)(lprec *lp, char *filename); +typedef void (__WINAPI set_outputstream_func)(lprec *lp, FILE *stream); +typedef MYBOOL (__WINAPI set_partialprice_func)(lprec *lp, int blockcount, int *blockstart, MYBOOL isrow); +typedef void (__WINAPI set_pivoting_func)(lprec *lp, int piv_rule); +typedef void (__WINAPI set_preferdual_func)(lprec *lp, MYBOOL dodual); +typedef void (__WINAPI set_presolve_func)(lprec *lp, int do_presolve); +typedef void (__WINAPI set_print_sol_func)(lprec *lp, int print_sol); +typedef MYBOOL (__WINAPI set_PseudoCosts_func)(lprec *lp, REAL *clower, REAL *cupper, int *updatelimit); +typedef MYBOOL (__WINAPI set_rh_func)(lprec *lp, int row, REAL value); +typedef MYBOOL (__WINAPI set_rh_range_func)(lprec *lp, int row, REAL deltavalue); +typedef void (__WINAPI set_rh_vec_func)(lprec *lp, REAL *rh); +typedef MYBOOL (__WINAPI set_row_name_func)(lprec *lp, int row, char *new_name); +typedef void (__WINAPI set_scalelimit_func)(lprec *lp, REAL scalelimit); +typedef void (__WINAPI set_scaling_func)(lprec *lp, int scalemode); +typedef MYBOOL (__WINAPI set_semicont_func)(lprec *lp, int column, MYBOOL must_be_sc); +typedef void (__WINAPI set_sense_func)(lprec *lp, MYBOOL maximize); +typedef void (__WINAPI set_simplextype_func)(lprec *lp, int simplextype); +typedef void (__WINAPI set_solutionlimit_func)(lprec *lp, int limit); +typedef void (__WINAPI set_splitnegvars_func)(lprec *lp, int splitnegvars); +typedef void (__WINAPI set_timeout_func)(lprec *lp, long sectimeout); +typedef void (__WINAPI set_trace_func)(lprec *lp, MYBOOL trace); +typedef MYBOOL (__WINAPI set_upbo_func)(lprec *lp, int column, REAL value); +typedef MYBOOL (__WINAPI set_var_branch_func)(lprec *lp, int column, int branch_mode); +typedef MYBOOL (__WINAPI set_var_weights_func)(lprec *lp, REAL *weights); +typedef void (__WINAPI set_verbose_func)(lprec *lp, int verbose); +typedef MYBOOL (__WINAPI set_XLI_func)(lprec *lp, char *filename); +typedef int (__WINAPI solve_func)(lprec *lp); +typedef MYBOOL (__WINAPI str_add_column_func)(lprec *lp, char *col_string); +typedef MYBOOL (__WINAPI str_add_constraint_func)(lprec *lp, char *row_string ,int constr_type, REAL rh); +typedef MYBOOL (__WINAPI str_add_lag_con_func)(lprec *lp, char *row_string, int con_type, REAL rhs); +typedef MYBOOL (__WINAPI str_set_obj_fn_func)(lprec *lp, char *row_string); +typedef MYBOOL (__WINAPI str_set_rh_vec_func)(lprec *lp, char *rh_string); +typedef REAL (__WINAPI time_elapsed_func)(lprec *lp); +typedef void (__WINAPI unscale_func)(lprec *lp); +typedef MYBOOL (__WINAPI write_lp_func)(lprec *lp, char *filename); +typedef MYBOOL (__WINAPI write_lpt_func)(lprec *lp, char *filename); +typedef MYBOOL (__WINAPI write_mps_func)(lprec *lp, char *filename); +typedef MYBOOL (__WINAPI write_freemps_func)(lprec *lp, char *filename); +typedef MYBOOL (__WINAPI write_XLI_func)(lprec *lp, char *filename, char *options, MYBOOL results); + + +/* Prototypes for callbacks from basis inverse/factorization libraries */ +/* ------------------------------------------------------------------------- */ +typedef void (__WINAPI reportfunc)(lprec *lp, int level, char const *format, ...); +typedef int (__WINAPI getnzfunc)(lprec *lp); +typedef int (__WINAPI setbasicfunc)(lprec *lp, int basisPos, int enteringCol); +typedef int (__WINAPI getvectorfunc)(lprec *lp, int varin, REAL *pcol, int *nzlist, int *maxabs); +typedef int (__WINAPI getpackedfunc)(lprec *lp, int j, int rn[], double bj[]); +typedef MYBOOL (__WINAPI modifyOF1func)(lprec *lp, int col_nr, REAL *ofValue, REAL mult); +typedef MYBOOL (__WINAPI invertfunc)(lprec *lp, MYBOOL shiftbounds); + + +/* Prototypes for basis inverse/factorization libraries */ +/* ------------------------------------------------------------------------- */ +typedef char *(BFP_CALLMODEL BFPchar)(void); +typedef void (BFP_CALLMODEL BFP_lp)(lprec *lp); +typedef void (BFP_CALLMODEL BFP_lpint)(lprec *lp, int newsize); +typedef int (BFP_CALLMODEL BFPint_lp)(lprec *lp); +typedef REAL (BFP_CALLMODEL BFPreal_lp)(lprec *lp); +typedef REAL *(BFP_CALLMODEL BFPrealp_lp)(lprec *lp); +typedef void (BFP_CALLMODEL BFP_lpbool)(lprec *lp, MYBOOL maximum); +typedef int (BFP_CALLMODEL BFPint_lpbool)(lprec *lp, MYBOOL maximum); +typedef int (BFP_CALLMODEL BFPint_lpintintbool)(lprec *lp, int uservars, int Bsize, MYBOOL *usedpos); +typedef void (BFP_CALLMODEL BFP_lprealint)(lprec *lp, REAL *pcol, int *nzidx); +typedef void (BFP_CALLMODEL BFP_lprealintrealint)(lprec *lp, REAL *prow, int *pnzidx, REAL *drow, int *dnzidx); +typedef MYBOOL (BFP_CALLMODEL BFPbool_lp)(lprec *lp); +typedef MYBOOL (BFP_CALLMODEL BFPbool_lpbool)(lprec *lp, MYBOOL changesign); +typedef MYBOOL (BFP_CALLMODEL BFPbool_lpintint)(lprec *lp, int size, int deltasize); +typedef LREAL (BFP_CALLMODEL BFPlreal_lpintintreal)(lprec *lp, int row_nr, int col_nr, REAL *pcol); +typedef REAL (BFP_CALLMODEL BFPreal_lplrealreal)(lprec *lp, LREAL theta, REAL *pcol); + + +/* Prototypes for external language libraries */ +/* ------------------------------------------------------------------------- */ +typedef char *(XLI_CALLMODEL XLIchar)(void); +typedef MYBOOL (XLI_CALLMODEL XLIbool_lpintint)(lprec* lp, int size, int deltasize); +typedef MYBOOL (XLI_CALLMODEL XLIbool_lpcharcharcharint)(lprec *lp, char *modelname, char *dataname, char *options, int verbose); +typedef MYBOOL (XLI_CALLMODEL XLIbool_lpcharcharbool)(lprec *lp, char *filename, char *options, MYBOOL results); + + +/* Main lp_solve prototypes and function definitions */ +/* ------------------------------------------------------------------------- */ +struct _lprec +{ + /* Full list of exported functions made available in a quasi object-oriented fashion */ + add_column_func *add_column; + add_columnex_func *add_columnex; + add_constraint_func *add_constraint; + add_constraintex_func *add_constraintex; + add_lag_con_func *add_lag_con; + add_SOS_func *add_SOS; + column_in_lp_func *column_in_lp; + default_basis_func *default_basis; + del_column_func *del_column; + del_constraint_func *del_constraint; + delete_lp_func *delete_lp; + free_lp_func *free_lp; + get_anti_degen_func *get_anti_degen; + get_basis_func *get_basis; + get_basiscrash_func *get_basiscrash; + get_bb_depthlimit_func *get_bb_depthlimit; + get_bb_floorfirst_func *get_bb_floorfirst; + get_bb_rule_func *get_bb_rule; + get_bounds_tighter_func *get_bounds_tighter; + get_break_at_value_func *get_break_at_value; + get_col_name_func *get_col_name; + get_column_func *get_column; + get_constr_type_func *get_constr_type; + get_constraints_func *get_constraints; + get_dual_solution_func *get_dual_solution; + get_epsb_func *get_epsb; + get_epsd_func *get_epsd; + get_epsel_func *get_epsel; + get_epsilon_func *get_epsilon; + get_epsperturb_func *get_epsperturb; + get_epspivot_func *get_epspivot; + get_improve_func *get_improve; + get_infinite_func *get_infinite; + get_lambda_func *get_lambda; + get_lowbo_func *get_lowbo; + get_lp_index_func *get_lp_index; + get_lp_name_func *get_lp_name; + get_Lrows_func *get_Lrows; + get_mat_func *get_mat; + get_max_level_func *get_max_level; + get_maxpivot_func *get_maxpivot; + get_mip_gap_func *get_mip_gap; + get_multiprice_func *get_multiprice; + get_Ncolumns_func *get_Ncolumns; + get_negrange_func *get_negrange; + get_Norig_columns_func *get_Norig_columns; + get_Norig_rows_func *get_Norig_rows; + get_Nrows_func *get_Nrows; + get_obj_bound_func *get_obj_bound; + get_objective_func *get_objective; + get_orig_index_func *get_orig_index; + get_origcol_name_func *get_origcol_name; + get_origrow_name_func *get_origrow_name; + get_partialprice_func *get_partialprice; + get_pivoting_func *get_pivoting; + get_presolve_func *get_presolve; + get_primal_solution_func *get_primal_solution; + get_print_sol_func *get_print_sol; + get_PseudoCosts_func *get_PseudoCosts; + get_ptr_constraints_func *get_ptr_constraints; + get_ptr_dual_solution_func *get_ptr_dual_solution; + get_ptr_lambda_func *get_ptr_lambda; + get_ptr_primal_solution_func *get_ptr_primal_solution; + get_ptr_sensitivity_obj_func *get_ptr_sensitivity_obj; + get_ptr_sensitivity_rhs_func *get_ptr_sensitivity_rhs; + get_ptr_variables_func *get_ptr_variables; + get_rh_func *get_rh; + get_rh_range_func *get_rh_range; + get_row_func *get_row; + get_row_name_func *get_row_name; + get_scalelimit_func *get_scalelimit; + get_scaling_func *get_scaling; + get_sensitivity_obj_func *get_sensitivity_obj; + get_sensitivity_rhs_func *get_sensitivity_rhs; + get_simplextype_func *get_simplextype; + get_solutioncount_func *get_solutioncount; + get_solutionlimit_func *get_solutionlimit; + get_splitnegvars_func *get_splitnegvars; + get_statustext_func *get_statustext; + get_timeout_func *get_timeout; + get_total_iter_func *get_total_iter; + get_total_nodes_func *get_total_nodes; + get_upbo_func *get_upbo; + get_var_branch_func *get_var_branch; + get_var_dualresult_func *get_var_dualresult; + get_var_primalresult_func *get_var_primalresult; + get_var_priority_func *get_var_priority; + get_variables_func *get_variables; + get_verbose_func *get_verbose; + get_working_objective_func *get_working_objective; + has_BFP_func *has_BFP; + has_XLI_func *has_XLI; + is_add_rowmode_func *is_add_rowmode; + is_anti_degen_func *is_anti_degen; + is_binary_func *is_binary; + is_break_at_first_func *is_break_at_first; + is_constr_type_func *is_constr_type; + is_debug_func *is_debug; + is_feasible_func *is_feasible; + is_free_func *is_free; + is_int_func *is_int; + is_integerscaling_func *is_integerscaling; + is_lag_trace_func *is_lag_trace; + is_maxim_func *is_maxim; + is_nativeBFP_func *is_nativeBFP; + is_nativeXLI_func *is_nativeXLI; + is_negative_func *is_negative; + is_piv_mode_func *is_piv_mode; + is_piv_rule_func *is_piv_rule; + is_presolve_func *is_presolve; + is_scalemode_func *is_scalemode; + is_scaletype_func *is_scaletype; + is_semicont_func *is_semicont; + is_SOS_var_func *is_SOS_var; + is_trace_func *is_trace; + lp_solve_version_func *lp_solve_version; + make_lp_func *make_lp; + print_constraints_func *print_constraints; + print_debugdump_func *print_debugdump; + print_duals_func *print_duals; + print_lp_func *print_lp; + print_objective_func *print_objective; + print_scales_func *print_scales; + print_solution_func *print_solution; + print_str_func *print_str; + print_tableau_func *print_tableau; + put_abortfunc_func *put_abortfunc; + put_logfunc_func *put_logfunc; + put_msgfunc_func *put_msgfunc; + read_lp_func *read_lp; + read_LP_func *read_LP; + read_lpt_func *read_lpt; + read_LPT_func *read_LPT; + read_mps_func *read_mps; + read_MPS_func *read_MPS; + read_freemps_func *read_freemps; + read_freeMPS_func *read_freeMPS; + read_XLI_func *read_XLI; + reset_basis_func *reset_basis; + set_add_rowmode_func *set_add_rowmode; + set_anti_degen_func *set_anti_degen; + set_basis_func *set_basis; + set_basiscrash_func *set_basiscrash; + set_bb_depthlimit_func *set_bb_depthlimit; + set_bb_floorfirst_func *set_bb_floorfirst; + set_bb_rule_func *set_bb_rule; + set_BFP_func *set_BFP; + set_binary_func *set_binary; + set_bounds_func *set_bounds; + set_bounds_tighter_func *set_bounds_tighter; + set_break_at_first_func *set_break_at_first; + set_break_at_value_func *set_break_at_value; + set_col_name_func *set_col_name; + set_constr_type_func *set_constr_type; + set_debug_func *set_debug; + set_epsb_func *set_epsb; + set_epsd_func *set_epsd; + set_epsel_func *set_epsel; + set_epsilon_func *set_epsilon; + set_epsperturb_func *set_epsperturb; + set_epspivot_func *set_epspivot; + set_free_func *set_free; + set_improve_func *set_improve; + set_infinite_func *set_infinite; + set_int_func *set_int; + set_lag_trace_func *set_lag_trace; + set_lowbo_func *set_lowbo; + set_lp_name_func *set_lp_name; + set_mat_func *set_mat; + set_maxim_func *set_maxim; + set_maxpivot_func *set_maxpivot; + set_minim_func *set_minim; + set_mip_gap_func *set_mip_gap; + set_multiprice_func *set_multiprice; + set_negrange_func *set_negrange; + set_obj_bound_func *set_obj_bound; + set_obj_fn_func *set_obj_fn; + set_obj_fnex_func *set_obj_fnex; + set_obj_func *set_obj; + set_outputfile_func *set_outputfile; + set_outputstream_func *set_outputstream; + set_partialprice_func *set_partialprice; + set_pivoting_func *set_pivoting; + set_preferdual_func *set_preferdual; + set_presolve_func *set_presolve; + set_print_sol_func *set_print_sol; + set_PseudoCosts_func *set_PseudoCosts; + set_rh_func *set_rh; + set_rh_range_func *set_rh_range; + set_rh_vec_func *set_rh_vec; + set_row_name_func *set_row_name; + set_scalelimit_func *set_scalelimit; + set_scaling_func *set_scaling; + set_semicont_func *set_semicont; + set_sense_func *set_sense; + set_simplextype_func *set_simplextype; + set_solutionlimit_func *set_solutionlimit; + set_splitnegvars_func *set_splitnegvars; + set_timeout_func *set_timeout; + set_trace_func *set_trace; + set_upbo_func *set_upbo; + set_var_branch_func *set_var_branch; + set_var_weights_func *set_var_weights; + set_verbose_func *set_verbose; + set_XLI_func *set_XLI; + solve_func *solve; + str_add_column_func *str_add_column; + str_add_constraint_func *str_add_constraint; + str_add_lag_con_func *str_add_lag_con; + str_set_obj_fn_func *str_set_obj_fn; + str_set_rh_vec_func *str_set_rh_vec; + time_elapsed_func *time_elapsed; + unscale_func *unscale; + write_lp_func *write_lp; + write_lpt_func *write_lpt; + write_mps_func *write_mps; + write_freemps_func *write_freemps; + write_XLI_func *write_XLI; + + /* Problem description */ + char *lp_name; /* The name of the lp */ + + /* Problem sizes */ + int rows; + int columns; + int equalities; /* No of non-Lagrangean equality constraints in the problem */ + int orig_rows; /* Number of actual problem rows before deletions */ + int orig_columns; /* Number of actual problem columns before working columns */ + int sum; /* The size of the variables + the slacks */ + + /* Solver result variables */ + MYBOOL source_is_file; /* The base model was read from a file */ + MYBOOL model_is_pure; /* The model has been built entirely from row and column additions */ + MYBOOL model_is_valid; /* Has this lp pased the 'test' */ + int solvecount; /* The number of lp_solve_solve() performed in this model */ + int spx_status; /* Simplex solver feasibility/mode code */ + int lag_status; /* Extra status variable for lag_solve */ + int solutioncount; /* number of equal-valued solutions found (up to solutionlimit) */ + int solutionlimit; /* upper number of equal-valued solutions kept track of */ + REAL *solution; /* sum_alloc+1 : Solution array of the next to optimal LP, + Index 0 : Objective function value, + Indeces 1..rows : Slack variable values, + Indeced rows+1..sum : Variable values */ + REAL *best_solution; /* sum_alloc+1 : Solution array of optimal 'Integer' LP, + structured as the solution array above */ + REAL *full_solution; /* sum_alloc+1 : Final solution array expanded for deleted variables */ + REAL real_solution; /* Optimal non-MIP solution base */ + REAL *edgeVector; /* Array of reduced cost scaling norms (DEVEX and Steepest Edge) */ + REAL *duals; /* rows_alloc+1 : The dual variables of the last LP */ + REAL *dualsfrom; /* sum_alloc+1 :The sensitivity on dual variables/reduced costs + of the last LP */ + REAL *dualstill; /* sum_alloc+1 :The sensitivity on dual variables/reduced costs + of the last LP */ + REAL *objfrom; /* columns_alloc+1 :The sensitivity on objective function + of the last LP */ + REAL *objtill; /* columns_alloc+1 :The sensitivity on objective function + of the last LP */ + REAL *OF_override; /* Special vector used to temporarily change the OF vector */ + + int *var_to_orig; /* sum_alloc+1 : Mapping of variables from solution to + best_solution to account for removed variables and + rows during presolve; a non-positive value indicates + that the constraint or variable was removed */ + int *orig_to_var; + MYBOOL varmap_locked; /* Determines whether the var_to_orig and orig_to_var are fixed */ + + int current_iter; /* Number of iterations in the current/last simplex */ + int total_iter; /* Number of iterations over all B&B steps */ + int current_bswap; /* Number of bound swaps in the current/last simplex */ + int total_bswap; /* Number of bount swaps over all B&B steps */ + + /* Various execution parameters */ + int simplex_strategy; /* Set desired combination of primal and dual simplex algorithms */ + int simplex_mode; /* Specifies the current simplex mode during solve; see simplex_strategy */ + int max_pivots; /* Number of pivots between reinversions of the basis */ + int verbose; /* Set amount of run-time messages and results */ + FILE *outstream; /* Output stream, initialized to STDOUT */ + int print_sol; /* TRUE to print optimal solution; AUTOMATIC skips zeros */ + + int scalemode; /* */ + REAL scalelimit; /* Relative convergence criterion for iterated scaling */ + int improve; /* Set to non-zero for iterative improvement */ + MYBOOL spx_trace; /* Print information on simplex progression */ + MYBOOL lag_trace; /* Print information on Lagrange progression */ + MYBOOL bb_trace; /* TRUE to print extra debug information */ + MYBOOL break_at_first; /* TRUE to stop at first feasible solution */ + REAL break_at_value; /* Value for the objective function deemed + to be sufficiently good in an integer problem */ + int anti_degen; /* Anti-degen strategy (or none) TRUE to avoid cycling */ + int do_presolve; /* PRESOLVE_ parameters for LP presolving */ + + int piv_strategy; /* Strategy for selecting row and column entering/leaving */ + int _piv_rule_; /* Internal working rule-part of piv_strategy above */ + MYBOOL _piv_left_; /* Internal variable indicating active pricing loop order */ + + int bb_rule; /* ## Set rule for selecting B&B variables */ + MYBOOL bb_floorfirst; /* ## Set BRANCH_FLOOR for B&B to set variables to floor bound first; + conversely with BRANCH_CEILING, the ceiling value is set first */ + MYBOOL *bb_varbranch; /* Determines branching strategy at the individual variable level; + the setting here overrides the bb_floorfirst setting */ + int bb_compressions; /* The count of memory-saving compressions during B&B*/ + + REAL obj_mincost; /* The smallest OF value used for dual phase 1 purposes */ + + /* Row and column names storage variables */ + MYBOOL names_used; /* Flag to indicate if names for rows and columns are used */ + hashelem **row_name; /* rows_alloc+1 */ + hashelem **col_name; /* columns_alloc+1 */ + hashtable *rowname_hashtab; /* hash table to store row names */ + hashtable *colname_hashtab; /* hash table to store column names */ + + /* Optionally specify continuous rows/column blocks for partial pricing */ + partialrec *rowblocks; + partialrec *colblocks; + + /* Optionally specify list of active rows/columns used in multiple pricing */ + int multiblockdiv; /* ## The divisor used to set or augment pricing block */ + int multisize; /* The maximum number of multiply priced rows/columns */ + int multiused; /* The current / active number of multiply priced rows/columns */ + int multinow; /* Index of currently active multiply priced row/column */ + pricerec *multivar; /* Array of best multiply priced rows/columns */ + + /* Memory allocation sizes */ + int rows_alloc; /* The allocated memory for row-sized data */ + int columns_alloc; /* The allocated memory for column-sized data */ + int sum_alloc; /* The allocated memory for row+column-sized data */ + + /* RHS storage */ + REAL *orig_rh; /* rows_alloc+1 : The RHS after scaling and sign + changing, but before 'Bound transformation' */ + LREAL *rhs; /* rows_alloc+1 : The RHS of the current simplex tableau */ + + /* Row (constraint) parameters */ + int *row_type; /* rows_alloc+1 : Row/constraint type coding */ + + /* Variable (column) parameters */ + int int_count; /* Number of variables required to be integer */ + MYBOOL *must_be_int; /* sum_alloc+1 : TRUE if variable must be integer */ + int *var_priority; /* columns: Priority-mapping of variables */ + int sc_count; /* Number of semi-continuous variables */ + REAL *var_is_sc; /* sum_columns+1 : TRUE if variable is semi-continuous; + value replaced by conventional lower bound during solve */ + int *var_is_free; /* columns+1: Index of twin variable if variable is free */ + + SOSgroup *SOS; /* Pointer to record containing all SOS'es */ + int sos_ints; /* Number of integers in SOS'es above */ + int sos_vars; /* Number of variables in the sos_priority list */ + int *sos_priority; /* Priority-sorted list of variables (no duplicates) */ + + SOSgroup *GUB; /* Pointer to record containing GUBs */ + + MYBOOL tighten_on_set; /* Specify if bounds will be tightened or overriden at bound setting */ + REAL *orig_upbo; /* sum_alloc+1 : Bound before transformations */ + REAL *orig_lowbo; /* " " */ + REAL *upbo; /* " " : Upper bound after transformation and B&B work */ + REAL *lowbo; /* " " : Lower bound after transformation and B&B work */ + + /* Scaling parameters */ + MYBOOL scaling_used; /* TRUE if scaling is used */ + MYBOOL columns_scaled; /* TRUE if the columns are scaled too */ + REAL *scalars; /* sum_alloc+1:0..Rows the scaling of the Rows, + Rows+1..Sum the scaling of the columns */ + + /* Simplex basis indicators */ + MYBOOL basis_valid; /* TRUE is the basis is still valid */ + int *var_basic; /* rows_alloc+1: The list of columns in the basis */ + MYBOOL *is_basic; /* sum_alloc+1: TRUE if the column is in the basis */ + MYBOOL *is_lower; /* " " : TRUE if the variable is at its + lower bound (or in the basis), FALSE otherwise */ + int crashmode; /* basis crashing mode */ + BBrec *rootbounds; /* The bounds at the lowest level */ + BBrec *bb_bounds; /* The linked list of B&B bounds */ + basisrec *bb_basis; /* The linked list of B&B bases */ + + int *rejectpivot; /* List of unacceptable pivot choices due to division-by-zero */ + + /* User data and basis inverse-matrices (ETA or LU, product form) */ + MATrec *matA; + INVrec *invB; + + /* Solver working variables */ + int Extrap; /* Primal mode: Set when solving the auxiliary problem for model feasiblility */ + REAL Extrad; /* Dual mode: Temporary objective function offset to make model feasible */ + REAL bigM; /* Original objective weighting in primal phase 1 */ + MYBOOL doIterate; /* Perform iteration at next opportunity */ + MYBOOL doInvert; /* Force basis reinversion immediateluy */ + MYBOOL doRebase; /* Do rebasing of the solution base and recompute */ + MYBOOL doRecompute; /* Recompute the solution vector */ + MYBOOL justInverted; /* The basis was recently reinverted */ + MYBOOL wasPreprocessed; /* The solve preprocessing was performed */ + MYBOOL wasPresolved; /* The solve presolver was invoked */ + int fixedvars; /* The number of basic fixed variables in the model */ + + /* Lagragian solver storage and parameters */ + MATrec *matL; + REAL *lag_rhs; /* Array of Lagrangean rhs vector */ + int *lag_con_type; /* Array of GT, LT or EQ */ + REAL *lambda; /* Lambda values (Lagrangean multipliers) */ + REAL lag_bound; /* The Lagrangian lower OF bound */ + REAL lag_accept; /* The Lagrangian convergence criterion */ + + /* Solver thresholds */ + REAL infinite; /* ## limit for dynamic range */ + REAL negrange; /* ## limit for negative variable range */ + int splitnegvars; /* ## split negative variables in a positive and negative component + Can be TRUE or AUTOMATIC + TRUE => always split negative variables + AUTOMATIC => only split when negative LB and positive UB */ + REAL epsmachine; /* ## default machine accuracy */ + REAL epsvalue; /* ## input data precision / rounding of data values to 0 */ + REAL epsprimal; /* ## for rounding RHS values to 0/infeasibility */ + REAL epsdual; /* ## for rounding reduced costs to zero */ + REAL epsperturb; /* ## perturbation scalar */ + REAL epspivot; /* ## Pivot reject tolerance (try others first) */ + + /* Branch & Bound working parameters */ + MYBOOL bb_break; /* Solver working variable; signals break of the B&B */ + int bb_level; /* Solver B&B working variable (recursion depth) */ + int bb_maxlevel; /* The deepest B&B level of the last solution */ + int bb_limitlevel; /* The maximum B&B level allowed */ + int bb_solutionlevel; /* The B&B level of the last / best solution */ + int bb_totalnodes; /* Total number of nodes processed in B&B */ + int bb_action; /* In-process B&B instructions for next level of solving */ + int bb_status; /* Indicator that the last solvelp() gave an improved B&B solution */ + REAL bb_limitOF; /* Limit-optimal MIP solution base */ + REAL bb_deltaOF; /* Minimum OF step value; computed at beginning of lp_solve_solve() */ + REAL bb_heuristicOF; /* ## Set initial "at least better than" guess for objective function + (can in particular speed up B&B iterations) */ + REAL bb_parentOF; /* The OF value of the previous BB simplex */ + BBPSrec *bb_PseudoCost; /* Data structure for costing of nodes */ + int *bb_varactive; /* The B&B state of the variable; 0 means inactive */ + + /* MIP parameters */ + REAL epsint; /* ## to determine if a float value is integer */ + REAL mip_absgap; /* ## Absolute MIP gap */ + REAL mip_relgap; /* ## Relative MIP gap */ + + /* Internal work arrays allocated as required */ + int *workrowsINT; /* rows+1: Available only during the solution process */ + + /* Time/timer variables */ + long sectimeout; + double timestart; + double timeend; + + /* Refactorization engine interface routines (for dynamic DLL/SO BFPs) */ +#if LoadInverseLib == TRUE + #ifdef WIN32 + HINSTANCE hBFP; + #else + void *hBFP; + #endif +#endif + BFPchar *bfp_name; + BFPbool_lpintint *bfp_compatible; + BFPbool_lpintint *bfp_init; + BFP_lp *bfp_free; + BFP_lpint *bfp_resize; + BFPint_lp *bfp_memallocated; + BFPbool_lp *bfp_restart; + BFPbool_lp *bfp_mustrefactorize; + BFPint_lp *bfp_preparefactorization; + BFPint_lpintintbool *bfp_factorize; + BFP_lp *bfp_finishfactorization; + BFPlreal_lpintintreal *bfp_prepareupdate; + BFPreal_lplrealreal *bfp_pivotRHS; + BFPbool_lpbool *bfp_finishupdate; + BFP_lprealint *bfp_ftran_prepare; + BFP_lprealint *bfp_ftran_normal; + BFP_lprealint *bfp_btran_normal; + BFP_lprealintrealint *bfp_btran_double; + BFPint_lp *bfp_status; + BFPint_lpbool *bfp_nonzeros; + BFPint_lp *bfp_indexbase; + BFPint_lp *bfp_rowoffset; + BFPint_lp *bfp_pivotmax; + BFP_lpint *bfp_pivotalloc; + BFPint_lp *bfp_colcount; + BFPbool_lp *bfp_canresetbasis; + BFPreal_lp *bfp_efficiency; + BFPrealp_lp *bfp_pivotvector; + BFPint_lp *bfp_pivotcount; + BFPint_lp *bfp_refactcount; + BFPbool_lp *bfp_isSetI; + + /* External language interface routines (for dynamic DLL/SO XLIs) */ +#if LoadLanguageLib == TRUE + #ifdef WIN32 + HINSTANCE hXLI; + #else + void *hXLI; + #endif +#endif + XLIchar *xli_name; + XLIbool_lpintint *xli_compatible; + XLIbool_lpcharcharcharint *xli_readmodel; + XLIbool_lpcharcharbool *xli_writemodel; + + /* Miscellaneous internal functions made available externally */ + abortfunc *userabort; + reportfunc *report; + getnzfunc *get_nonzeros; + setbasicfunc *set_basisvar; + getvectorfunc *get_lpcolumn; + getpackedfunc *get_basiscolumn; + modifyOF1func *modifyOF1; + invertfunc *invert; + + /* User program interface callbacks */ + ctrlcfunc *ctrlc; + void *ctrlchandle; + logfunc *writelog; + void *loghandle; + debugfunc *debuginfo; + msgfunc *usermessage; + int msgmask; + void *msghandle; + +}; + + +#ifdef __cplusplus +__EXTERN_C { +#endif + + +/* User and system function interfaces */ +/* ------------------------------------------------------------------------- */ + +void __EXPORT_TYPE __WINAPI lp_solve_version(int *majorversion, int *minorversion, int *release, int *build); + +#ifdef __BORLANDC__ +lprec* __EXPORT_TYPE __WINAPI lp_solve_make_lp(int rows, int columns); +#else +lprec __EXPORT_TYPE * __WINAPI lp_solve_make_lp(int rows, int columns); +#endif +const char __EXPORT_TYPE * __WINAPI get_statustext(lprec *lp, int statuscode); +/* Create and initialise a lprec structure defaults */ + +void __EXPORT_TYPE __WINAPI lp_solve_delete_lp(lprec *lp); +void __EXPORT_TYPE __WINAPI free_lp(lprec **plp); +/* Remove problem from memory */ + +MYBOOL __EXPORT_TYPE __WINAPI set_lp_name(lprec *lp, char *lpname); +char __EXPORT_TYPE * __WINAPI get_lp_name(lprec *lp); +/* Set and get the problem name */ + +MYBOOL __EXPORT_TYPE __WINAPI has_BFP(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI is_nativeBFP(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI set_BFP(lprec *lp, char *filename); +/* Set basis factorization engine */ + + +#ifdef __BORLANDC__ +lprec* __EXPORT_TYPE __WINAPI read_XLI(char *xliname, char *modelname, char *dataname, char *options, int verbose); +#else +lprec __EXPORT_TYPE * __WINAPI read_XLI(char *xliname, char *modelname, char *dataname, char *options, int verbose); +#endif +MYBOOL __EXPORT_TYPE __WINAPI write_XLI(lprec *lp, char *filename, char *options, MYBOOL results); +MYBOOL __EXPORT_TYPE __WINAPI has_XLI(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI is_nativeXLI(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI set_XLI(lprec *lp, char *filename); +/* Set external language interface */ + +MYBOOL __EXPORT_TYPE __WINAPI set_obj(lprec *lp, int Column, REAL Value); +MYBOOL __EXPORT_TYPE __WINAPI set_obj_fn(lprec *lp, REAL *row); +MYBOOL __EXPORT_TYPE __WINAPI set_obj_fnex(lprec *lp, int count, REAL *row, int *colno); +/* set the objective function (Row 0) of the matrix */ +MYBOOL __EXPORT_TYPE __WINAPI str_set_obj_fn(lprec *lp, char *row_string); +/* The same, but with string input */ +void __EXPORT_TYPE __WINAPI set_sense(lprec *lp, MYBOOL maximize); +void __EXPORT_TYPE __WINAPI lp_solve_set_maxim(lprec *lp); +void __EXPORT_TYPE __WINAPI lp_solve_set_minim(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI is_maxim(lprec *lp); +/* Set optimization direction for the objective function */ + +MYBOOL __EXPORT_TYPE __WINAPI add_constraint(lprec *lp, REAL *row, int constr_type, REAL rh); +MYBOOL __EXPORT_TYPE __WINAPI add_constraintex(lprec *lp, int count, REAL *row, int *colno, int constr_type, REAL rh); +MYBOOL __EXPORT_TYPE __WINAPI set_add_rowmode(lprec *lp, MYBOOL turnon); +MYBOOL __EXPORT_TYPE __WINAPI is_add_rowmode(lprec *lp); +/* Add a constraint to the problem, row is the constraint row, rh is the right hand side, + constr_type is the type of constraint (LE (<=), GE(>=), EQ(=)) */ +MYBOOL __EXPORT_TYPE __WINAPI str_add_constraint(lprec *lp, char *row_string ,int constr_type, REAL rh); +/* The same, but with string input */ +MYBOOL __EXPORT_TYPE __WINAPI get_row(lprec *lp, int row_nr, REAL *row); +/* Fill row with the row row_nr from the problem */ +MYBOOL __EXPORT_TYPE __WINAPI del_constraint(lprec *lp,int del_row); +/* Remove constrain nr del_row from the problem */ + +MYBOOL __EXPORT_TYPE __WINAPI add_lag_con(lprec *lp, REAL *row, int con_type, REAL rhs); +/* add a Lagrangian constraint of form Row' x contype Rhs */ +MYBOOL __EXPORT_TYPE __WINAPI str_add_lag_con(lprec *lp, char *row_string, int con_type, REAL rhs); +/* The same, but with string input */ +void __EXPORT_TYPE __WINAPI set_lag_trace(lprec *lp, MYBOOL lag_trace); +MYBOOL __EXPORT_TYPE __WINAPI is_lag_trace(lprec *lp); +/* Set debugging/tracing mode of the Lagrangean solver */ + +MYBOOL __EXPORT_TYPE __WINAPI lp_solve_set_constr_type(lprec *lp, int row, int con_type); +int __EXPORT_TYPE __WINAPI get_constr_type(lprec *lp, int row); +MYBOOL __EXPORT_TYPE __WINAPI is_constr_type(lprec *lp, int row, int mask); +/* Set the type of constraint in row Row (LE, GE, EQ) */ + +MYBOOL __EXPORT_TYPE __WINAPI lp_solve_set_rh(lprec *lp, int row, REAL value); +REAL __EXPORT_TYPE __WINAPI get_rh(lprec *lp, int row); +/* Set and get the right hand side of a constraint row */ +MYBOOL __EXPORT_TYPE __WINAPI set_rh_range(lprec *lp, int row, REAL deltavalue); +REAL __EXPORT_TYPE __WINAPI get_rh_range(lprec *lp, int row); +/* Set the RHS range; i.e. the lower and upper bounds of a constraint row */ +void __EXPORT_TYPE __WINAPI set_rh_vec(lprec *lp, REAL *rh); +/* Set the right hand side vector */ +MYBOOL __EXPORT_TYPE __WINAPI str_set_rh_vec(lprec *lp, char *rh_string); +/* The same, but with string input */ + +MYBOOL __EXPORT_TYPE __WINAPI add_column(lprec *lp, REAL *column); +MYBOOL __EXPORT_TYPE __WINAPI add_columnex(lprec *lp, int count, REAL *column, int *rowno); +/* Add a column to the problem */ +MYBOOL __EXPORT_TYPE __WINAPI str_add_column(lprec *lp, char *col_string); +/* The same, but with string input */ +int __EXPORT_TYPE __WINAPI column_in_lp(lprec *lp, REAL *column); +/* Returns the column index if column is already present in lp, otherwise 0. + (Does not look at bounds and types, only looks at matrix values */ +MYBOOL __EXPORT_TYPE __WINAPI get_column(lprec *lp, int col_nr, REAL *column); +/* Fill column with the column col_nr from the problem */ +MYBOOL __EXPORT_TYPE __WINAPI del_column(lprec *lp, int column); +/* Delete a column */ + +MYBOOL __EXPORT_TYPE __WINAPI lp_solve_set_mat(lprec *lp, int row, int column, REAL value); +/* Fill in element (Row,Column) of the matrix + Row in [0..Rows] and Column in [1..Columns] */ +REAL __EXPORT_TYPE __WINAPI get_mat(lprec *lp, int row, int column); +/* get a single element from the matrix */ /* Name changed from "mat_elm" by KE */ + +void __EXPORT_TYPE __WINAPI set_bounds_tighter(lprec *lp, MYBOOL tighten); +MYBOOL get_bounds(lprec *lp, int column, REAL *lower, REAL *upper); +MYBOOL __EXPORT_TYPE __WINAPI get_bounds_tighter(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI lp_solve_set_upbo(lprec *lp, int column, REAL value); +REAL __EXPORT_TYPE __WINAPI get_upbo(lprec *lp, int column); +MYBOOL __EXPORT_TYPE __WINAPI lp_solve_set_lowbo(lprec *lp, int column, REAL value); +REAL __EXPORT_TYPE __WINAPI get_lowbo(lprec *lp, int column); +MYBOOL __EXPORT_TYPE __WINAPI set_bounds(lprec *lp, int column, REAL lower, REAL upper); +MYBOOL __EXPORT_TYPE __WINAPI set_free(lprec *lp, int column); +MYBOOL __EXPORT_TYPE __WINAPI is_free(lprec *lp, int column); +/* Set the upper and lower bounds of a variable */ + +MYBOOL __EXPORT_TYPE __WINAPI lp_solve_set_int(lprec *lp, int column, MYBOOL must_be_int); +MYBOOL __EXPORT_TYPE __WINAPI is_int(lprec *lp, int column); +MYBOOL __EXPORT_TYPE __WINAPI set_binary(lprec *lp, int column, MYBOOL must_be_bin); +MYBOOL __EXPORT_TYPE __WINAPI is_binary(lprec *lp, int column); +MYBOOL __EXPORT_TYPE __WINAPI set_semicont(lprec *lp, int column, MYBOOL must_be_sc); +MYBOOL __EXPORT_TYPE __WINAPI is_semicont(lprec *lp, int column); +MYBOOL __EXPORT_TYPE __WINAPI is_negative(lprec *lp, int column); +MYBOOL __EXPORT_TYPE __WINAPI set_var_weights(lprec *lp, REAL *weights); +int __EXPORT_TYPE __WINAPI get_var_priority(lprec *lp, int column); +/* Set the type of variable, if must_be_int = TRUE then the variable must be integer */ + +MYBOOL __EXPORT_TYPE __WINAPI set_PseudoCosts(lprec *lp, REAL *clower, REAL *cupper, int *updatelimit); +MYBOOL __EXPORT_TYPE __WINAPI get_PseudoCosts(lprec *lp, REAL *clower, REAL *cupper, int *updatelimit); +/* Set initial values for, or get computed pseudocost vectors; + note that setting of pseudocosts can only happen in response to a + call-back function optionally requesting this */ + +int __EXPORT_TYPE __WINAPI add_SOS(lprec *lp, char *name, int sostype, int priority, int count, int *sosvars, REAL *weights); +MYBOOL __EXPORT_TYPE __WINAPI is_SOS_var(lprec *lp, int column); +/* Add SOS constraints */ + +MYBOOL __EXPORT_TYPE __WINAPI set_row_name(lprec *lp, int row, char *new_name); +char __EXPORT_TYPE * __WINAPI get_row_name(lprec *lp, int row); +char __EXPORT_TYPE * __WINAPI get_origrow_name(lprec *lp, int row); +/* Set/Get the name of a constraint row */ /* Get added by KE */ + +MYBOOL __EXPORT_TYPE __WINAPI set_col_name(lprec *lp, int column, char *new_name); +char __EXPORT_TYPE * __WINAPI get_col_name(lprec *lp, int column); +char __EXPORT_TYPE * __WINAPI get_origcol_name(lprec *lp, int column); +/* Set/Get the name of a variable column */ /* Get added by KE */ + +void __EXPORT_TYPE __WINAPI unscale(lprec *lp); +/* Undo previous scaling of the problem */ + +void __EXPORT_TYPE __WINAPI set_splitnegvars(lprec *lp, int splitnegvars); +int __EXPORT_TYPE __WINAPI get_splitnegvars(lprec *lp); +/* Set splitting mode for negative-bounded variables */ + +void __EXPORT_TYPE __WINAPI set_preferdual(lprec *lp, MYBOOL dodual); +void __EXPORT_TYPE __WINAPI set_simplextype(lprec *lp, int simplextype); +int __EXPORT_TYPE __WINAPI get_simplextype(lprec *lp); +/* Set/Get if lp_solve should prefer the dual simplex over the primal -- added by KE */ + +void __EXPORT_TYPE __WINAPI default_basis(lprec *lp); +void __EXPORT_TYPE __WINAPI set_basiscrash(lprec *lp, int mode); +int __EXPORT_TYPE __WINAPI get_basiscrash(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI set_basis(lprec *lp, int *bascolumn, MYBOOL nonbasic); +void __EXPORT_TYPE __WINAPI get_basis(lprec *lp, int *bascolumn, MYBOOL nonbasic); +/* Set/Get basis for a re-solved system */ /* Added by KE */ + +MYBOOL __EXPORT_TYPE __WINAPI is_feasible(lprec *lp, REAL *values, REAL threshold); +/* returns TRUE if the vector in values is a feasible solution to the lp */ + +int __EXPORT_TYPE __WINAPI lp_solve_solve(lprec *lp); +/* Solve the problem */ + +REAL __EXPORT_TYPE __WINAPI time_elapsed(lprec *lp); +/* Return the number of seconds since start of solution process */ + +void __EXPORT_TYPE __WINAPI put_abortfunc(lprec *lp, ctrlcfunc newctrlc, void *ctrlchandle); +/* Allow the user to define an interruption callback function */ + +void __EXPORT_TYPE __WINAPI put_logfunc(lprec *lp, logfunc newlog, void *loghandle); +/* Allow the user to define a logging function */ + +void __EXPORT_TYPE __WINAPI put_msgfunc(lprec *lp, msgfunc newmsg, void *msghandle, int mask); +/* Allow the user to define an event-driven message/reporting */ + +MYBOOL __EXPORT_TYPE __WINAPI get_primal_solution(lprec *lp, REAL *pv); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_primal_solution(lprec *lp, REAL **pv); +MYBOOL __EXPORT_TYPE __WINAPI get_dual_solution(lprec *lp, REAL *rc); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_dual_solution(lprec *lp, REAL **rc); +MYBOOL __EXPORT_TYPE __WINAPI get_lambda(lprec *lp, REAL *lambda); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_lambda(lprec *lp, REAL **lambda); +/* Get the primal, dual/reduced costs and Lambda vectors */ + +void __EXPORT_TYPE __WINAPI reset_basis(lprec *lp); +/* Reset the basis of a problem, can be useful in case of degeneracy - JD */ + +/* Read an MPS file */ +#ifdef __BORLANDC__ +lprec* __EXPORT_TYPE __WINAPI read_MPS(char *filename, int verbose); +lprec* __EXPORT_TYPE __WINAPI read_mps(FILE *filename, int verbose); +lprec* __EXPORT_TYPE __WINAPI read_freeMPS(char *filename, int verbose); +lprec* __EXPORT_TYPE __WINAPI read_freemps(FILE *filename, int verbose); +#else +lprec __EXPORT_TYPE * __WINAPI read_MPS(char *filename, int verbose); +lprec __EXPORT_TYPE * __WINAPI read_mps(FILE *filename, int verbose); +lprec __EXPORT_TYPE * __WINAPI read_freeMPS(char *filename, int verbose); +lprec __EXPORT_TYPE * __WINAPI read_freemps(FILE *filename, int verbose); +#endif + +/* Write a MPS file to output */ +MYBOOL __EXPORT_TYPE __WINAPI write_mps(lprec *lp, char *filename); +MYBOOL __EXPORT_TYPE __WINAPI write_freemps(lprec *lp, char *filename); + +MYBOOL __EXPORT_TYPE __WINAPI write_lp(lprec *lp, char *filename); +/* Write a LP file to output */ + +lprec __EXPORT_TYPE * __WINAPI read_lp(FILE *filename, int verbose, char *lp_name); +lprec __EXPORT_TYPE * __WINAPI read_LP(char *filename, int verbose, char *lp_name); +/* Old-style lp format file parser */ + +lprec __EXPORT_TYPE * __WINAPI read_LPT(char *filename, int verbose, char *lp_name); +lprec __EXPORT_TYPE * __WINAPI read_lpt(FILE *filename, int verbose, char *lp_name); +MYBOOL __EXPORT_TYPE __WINAPI write_lpt(lprec *lp, char *filename); +/* CPLEX-style lp format file parser and writer */ + +void __EXPORT_TYPE __WINAPI lp_solve_print_lp(lprec *lp); +void __EXPORT_TYPE __WINAPI print_tableau(lprec *lp); +/* Print the current problem, only useful in very small (test) problems */ + +void __EXPORT_TYPE __WINAPI print_objective(lprec *lp); +void __EXPORT_TYPE __WINAPI print_solution(lprec *lp, int columns); +void __EXPORT_TYPE __WINAPI print_constraints(lprec *lp, int columns); +/* Print the solution to stdout */ + +void __EXPORT_TYPE __WINAPI print_duals(lprec *lp); +/* Print the dual variables of the solution */ + +void __EXPORT_TYPE __WINAPI print_scales(lprec *lp); +/* If scaling is used, print the scaling factors */ + +void __EXPORT_TYPE __WINAPI print_str(lprec *lp, char *str); + +void __EXPORT_TYPE __WINAPI set_outputstream(lprec *lp, FILE *stream); +MYBOOL __EXPORT_TYPE __WINAPI set_outputfile(lprec *lp, char *filename); + +void __EXPORT_TYPE __WINAPI set_verbose(lprec *lp, int verbose); +int __EXPORT_TYPE __WINAPI get_verbose(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_timeout(lprec *lp, long sectimeout); +long __EXPORT_TYPE __WINAPI get_timeout(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_print_sol(lprec *lp, int print_sol); +int __EXPORT_TYPE __WINAPI get_print_sol(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_debug(lprec *lp, MYBOOL debug); +MYBOOL __EXPORT_TYPE __WINAPI is_debug(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_trace(lprec *lp, MYBOOL trace); +MYBOOL __EXPORT_TYPE __WINAPI is_trace(lprec *lp); + +MYBOOL __EXPORT_TYPE __WINAPI print_debugdump(lprec *lp, char *filename); + +void __EXPORT_TYPE __WINAPI set_anti_degen(lprec *lp, int anti_degen); +int __EXPORT_TYPE __WINAPI get_anti_degen(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI is_anti_degen(lprec *lp, int testmask); + +void __EXPORT_TYPE __WINAPI set_presolve(lprec *lp, int do_presolve); +int __EXPORT_TYPE __WINAPI get_presolve(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI is_presolve(lprec *lp, int testmask); + +int __EXPORT_TYPE __WINAPI get_orig_index(lprec *lp, int lp_index); +int __EXPORT_TYPE __WINAPI get_lp_index(lprec *lp, int orig_index); + +void __EXPORT_TYPE __WINAPI set_maxpivot(lprec *lp, int max_num_inv); +int __EXPORT_TYPE __WINAPI get_maxpivot(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_obj_bound(lprec *lp, REAL obj_bound); +REAL __EXPORT_TYPE __WINAPI get_obj_bound(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_mip_gap(lprec *lp, MYBOOL absolute, REAL mip_gap); +REAL __EXPORT_TYPE __WINAPI get_mip_gap(lprec *lp, MYBOOL absolute); + +void __EXPORT_TYPE __WINAPI set_bb_rule(lprec *lp, int bb_rule); +int __EXPORT_TYPE __WINAPI get_bb_rule(lprec *lp); + +MYBOOL __EXPORT_TYPE __WINAPI set_var_branch(lprec *lp, int column, int branch_mode); +int __EXPORT_TYPE __WINAPI get_var_branch(lprec *lp, int column); + +void __EXPORT_TYPE __WINAPI set_infinite(lprec *lp, REAL infinite); +REAL __EXPORT_TYPE __WINAPI get_infinite(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsilon(lprec *lp, REAL epsilon); +REAL __EXPORT_TYPE __WINAPI get_epsilon(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsb(lprec *lp, REAL epsb); +REAL __EXPORT_TYPE __WINAPI get_epsb(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsd(lprec *lp, REAL epsd); +REAL __EXPORT_TYPE __WINAPI get_epsd(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsel(lprec *lp, REAL epsel); +REAL __EXPORT_TYPE __WINAPI get_epsel(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_scaling(lprec *lp, int scalemode); +int __EXPORT_TYPE __WINAPI get_scaling(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI is_scalemode(lprec *lp, int testmask); +MYBOOL __EXPORT_TYPE __WINAPI is_scaletype(lprec *lp, int scaletype); +MYBOOL __EXPORT_TYPE __WINAPI is_integerscaling(lprec *lp); +void __EXPORT_TYPE __WINAPI set_scalelimit(lprec *lp, REAL scalelimit); +REAL __EXPORT_TYPE __WINAPI get_scalelimit(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_improve(lprec *lp, int improve); +int __EXPORT_TYPE __WINAPI get_improve(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_pivoting(lprec *lp, int piv_rule); +int __EXPORT_TYPE __WINAPI get_pivoting(lprec *lp); +MYBOOL __EXPORT_TYPE __WINAPI set_partialprice(lprec *lp, int blockcount, int *blockstart, MYBOOL isrow); +void __EXPORT_TYPE __WINAPI get_partialprice(lprec *lp, int *blockcount, int *blockstart, MYBOOL isrow); + +MYBOOL __EXPORT_TYPE __WINAPI set_multiprice(lprec *lp, int multiblockdiv); +int __EXPORT_TYPE __WINAPI get_multiprice(lprec *lp, MYBOOL getabssize); + +MYBOOL __EXPORT_TYPE __WINAPI is_piv_mode(lprec *lp, int testmask); +MYBOOL __EXPORT_TYPE __WINAPI is_piv_rule(lprec *lp, int rule); + +void __EXPORT_TYPE __WINAPI set_break_at_first(lprec *lp, MYBOOL break_at_first); +MYBOOL __EXPORT_TYPE __WINAPI is_break_at_first(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_bb_floorfirst(lprec *lp, int bb_floorfirst); +int __EXPORT_TYPE __WINAPI get_bb_floorfirst(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_bb_depthlimit(lprec *lp, int bb_maxlevel); +int __EXPORT_TYPE __WINAPI get_bb_depthlimit(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_break_at_value(lprec *lp, REAL break_at_value); +REAL __EXPORT_TYPE __WINAPI get_break_at_value(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_negrange(lprec *lp, REAL negrange); +REAL __EXPORT_TYPE __WINAPI get_negrange(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epsperturb(lprec *lp, REAL epsperturb); +REAL __EXPORT_TYPE __WINAPI get_epsperturb(lprec *lp); + +void __EXPORT_TYPE __WINAPI set_epspivot(lprec *lp, REAL epspivot); +REAL __EXPORT_TYPE __WINAPI get_epspivot(lprec *lp); + +int __EXPORT_TYPE __WINAPI get_max_level(lprec *lp); +int __EXPORT_TYPE __WINAPI get_total_nodes(lprec *lp); +int __EXPORT_TYPE __WINAPI get_total_iter(lprec *lp); + +REAL __EXPORT_TYPE __WINAPI get_objective(lprec *lp); +REAL __EXPORT_TYPE __WINAPI get_working_objective(lprec *lp); + +REAL __EXPORT_TYPE __WINAPI get_var_primalresult(lprec *lp, int index); +REAL __EXPORT_TYPE __WINAPI get_var_dualresult(lprec *lp, int index); + +MYBOOL __EXPORT_TYPE __WINAPI get_variables(lprec *lp, REAL *var); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_variables(lprec *lp, REAL **var); + +MYBOOL __EXPORT_TYPE __WINAPI get_constraints(lprec *lp, REAL *constr); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_constraints(lprec *lp, REAL **constr); + +MYBOOL __EXPORT_TYPE __WINAPI get_sensitivity_rhs(lprec *lp, REAL *duals, REAL *dualsfrom, REAL *dualstill); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_sensitivity_rhs(lprec *lp, REAL **duals, REAL **dualsfrom, REAL **dualstill); + +MYBOOL __EXPORT_TYPE __WINAPI get_sensitivity_obj(lprec *lp, REAL *objfrom, REAL *objtill); +MYBOOL __EXPORT_TYPE __WINAPI get_ptr_sensitivity_obj(lprec *lp, REAL **objfrom, REAL **objtill); + +void __EXPORT_TYPE __WINAPI set_solutionlimit(lprec *lp, int limit); +int __EXPORT_TYPE __WINAPI get_solutionlimit(lprec *lp); +int __EXPORT_TYPE __WINAPI get_solutioncount(lprec *lp); + +int __EXPORT_TYPE __WINAPI get_Norig_rows(lprec *lp); +int __EXPORT_TYPE __WINAPI get_Nrows(lprec *lp); +int __EXPORT_TYPE __WINAPI get_Lrows(lprec *lp); + +int __EXPORT_TYPE __WINAPI get_Norig_columns(lprec *lp); +int __EXPORT_TYPE __WINAPI get_Ncolumns(lprec *lp); + + +#ifdef __cplusplus +} +#endif + + +/* Forward definitions of functions used internaly by the lp toolkit */ +STATIC int yieldformessages(lprec *lp); +MYBOOL __WINAPI userabort(lprec *lp, int message); +void __WINAPI report(lprec *lp, int level, const char *format, ...); +MYBOOL set_callbacks(lprec *lp); + +/* Memory management routines */ +STATIC MYBOOL append_rows(lprec *lp, int deltarows); +STATIC MYBOOL append_columns(lprec *lp, int deltacolumns); +STATIC void inc_rows(lprec *lp, int delta); +STATIC void inc_columns(lprec *lp, int delta); +STATIC MYBOOL init_rowcol_names(lprec *lp); +STATIC MYBOOL inc_row_space(lprec *lp, int deltarows); +STATIC MYBOOL inc_col_space(lprec *lp, int deltacols); +STATIC MYBOOL shift_rowcoldata(lprec *lp, int base, int delta, MYBOOL isrow); +STATIC MYBOOL shift_basis(lprec *lp, int base, int delta, MYBOOL isrow); +STATIC MYBOOL shift_rowdata(lprec *lp, int base, int delta); +STATIC MYBOOL shift_coldata(lprec *lp, int base, int delta); + +STATIC MYBOOL is_chsign(lprec *lp, int rownr); + +STATIC MYBOOL inc_lag_space(lprec *lp, int deltarows, MYBOOL ignoreMAT); +lprec *make_lag(lprec *server); + +STATIC int __WINAPI get_nonzeros(lprec *lp); + +STATIC REAL get_OF_raw(lprec *lp, int colnr); +STATIC REAL get_OF_contribution(lprec *lp, int colnr, MYBOOL basic); + +REAL get_rh_upper(lprec *lp, int row); +REAL get_rh_lower(lprec *lp, int row); +MYBOOL set_rh_upper(lprec *lp, int row, REAL value); +MYBOOL set_rh_lower(lprec *lp, int row, REAL value); +STATIC int MIP_count(lprec *lp); +STATIC int SOS_count(lprec *lp); +STATIC int GUB_count(lprec *lp); +STATIC int identify_GUB(lprec *lp, MYBOOL mark); +STATIC int prepare_GUB(lprec *lp); + +STATIC MYBOOL check_if_less(lprec *lp, REAL x, REAL y, int variable); +STATIC MYBOOL feasiblePhase1(lprec *lp, REAL epsvalue); +STATIC void initialize_solution(lprec *lp, MYBOOL shiftbounds); +STATIC void recompute_solution(lprec *lp, MYBOOL shiftbounds); +STATIC int verify_solution(lprec *lp, MYBOOL reinvert, char *info); +STATIC int check_solution(lprec *lp, int lastcolumn, REAL *solution, + REAL *upbo, REAL *lowbo, REAL tolerance); +STATIC MYBOOL is_fixedvar(lprec *lp, int variable); +STATIC MYBOOL is_splitvar(lprec *lp, int column); +STATIC MYBOOL is_bb_action(lprec *lp, int testmask); +STATIC MYBOOL is_bb_rule(lprec *lp, int bb_rule); +STATIC MYBOOL is_bb_mode(lprec *lp, int bb_mask); +STATIC int get_piv_rule(lprec *lp); +MYBOOL set_varpriority(lprec *lp); +STATIC int find_sc_bbvar(lprec *lp, int *count); +STATIC int find_sos_bbvar(lprec *lp, int *count, MYBOOL intsos); +STATIC int find_int_bbvar(lprec *lp, int *count, REAL *lowbo, REAL*upbo, int *firstint, int *lastint); + +/* Reduced cost and other solution-related functions */ +STATIC void update_reducedcosts(lprec *lp, MYBOOL isdual, int leave_nr, int enter_nr, REAL *prow, REAL *drow); +STATIC void compute_reducedcosts(lprec *lp, MYBOOL isdual, int row_nr, REAL *prow, int *nzprow, + REAL *drow, int *nzdrow); +STATIC MYBOOL solution_is_int(lprec *lp, int i, MYBOOL checkfixed); +STATIC void construct_solution(lprec *lp, REAL *target); +STATIC void transfer_solution_var(lprec *lp, int uservar); +STATIC MYBOOL calculate_duals(lprec *lp); +STATIC MYBOOL calculate_sensitivity_duals(lprec *lp); +STATIC MYBOOL calculate_sensitivity_obj(lprec *lp); + +STATIC int add_GUB(lprec *lp, char *name, int priority, int count, int *sosvars); +STATIC basisrec *push_basis(lprec *lp, int *basisvar, MYBOOL *isbasic, MYBOOL *islower); +STATIC MYBOOL compare_basis(lprec *lp); +STATIC MYBOOL restore_basis(lprec *lp); +STATIC MYBOOL pop_basis(lprec *lp, MYBOOL restore); +STATIC MYBOOL is_BasisReady(lprec *lp); +STATIC MYBOOL verifyBasis(lprec *lp); +STATIC int __WINAPI setBasisVar(lprec *lp, int basisPos, int enteringCol); +STATIC int unload_basis(lprec *lp, MYBOOL restorelast); + +STATIC int perturb_bounds(lprec *lp, BBrec *perturbed, MYBOOL includeNONBASIC, MYBOOL includeFIXED); +STATIC MYBOOL restore_bounds(lprec *lp, MYBOOL doupbo, MYBOOL dolowbo); +STATIC MYBOOL validate_bounds(lprec *lp, REAL *upbo, REAL *lowbo); +STATIC MYBOOL impose_bounds(lprec *lp, REAL * upbo, REAL *lowbo); +STATIC int unload_BB(lprec *lp); + +STATIC MYBOOL add_artificial(lprec *lp, int forrownr); +STATIC int get_artificialRow(lprec *lp, int colnr); +STATIC int find_artificialReplacement(lprec *lp, int rownr, REAL *prow); +STATIC int findAnti_artificial(lprec *lp, int colnr); +STATIC void eliminate_artificials(lprec *lp, REAL *prow); +STATIC void clear_artificials(lprec *lp); +STATIC REAL feasibilityOffset(lprec *lp, MYBOOL isdual); +STATIC MYBOOL isPhase1(lprec *lp); +STATIC REAL get_refactfrequency(lprec *lp, MYBOOL final); +MYBOOL __WINAPI invert(lprec *lp, MYBOOL shiftbounds); +STATIC MYBOOL isBasisVarFeasible(lprec *lp, REAL tol, int basis_row); +STATIC MYBOOL isPrimalFeasible(lprec *lp, REAL tol); +STATIC MYBOOL isDualFeasible(lprec *lp, REAL tol); + +/* Main simplex driver routines */ +STATIC int preprocess(lprec *lp); +STATIC void postprocess(lprec *lp); +STATIC MYBOOL performiteration(lprec *lp, int row_nr, int varin, LREAL theta, MYBOOL primal, REAL *prow, int *nzprow); +STATIC void transfer_solution_var(lprec *lp, int uservar); +STATIC void transfer_solution(lprec *lp, MYBOOL dofinal); + +/* Scaling utilities */ +STATIC REAL scaled_floor(lprec *lp, int column, REAL value, REAL epsscale); +STATIC REAL scaled_ceil(lprec *lp, int column, REAL value, REAL epsscale); +STATIC REAL scaled_mat(lprec *lp, REAL value, int row, int col); +STATIC REAL unscaled_mat(lprec *lp, REAL value, int row, int col); +STATIC REAL scaled_value(lprec *lp, REAL value, int index); +STATIC REAL unscaled_value(lprec *lp, REAL value, int index); + +/* Variable mapping utility routines */ +STATIC void varmap_lock(lprec *lp); +STATIC void varmap_clear(lprec *lp); +STATIC void varmap_addconstraint(lprec *lp); +STATIC void varmap_deleteconstraint(lprec *lp, int del_row); +STATIC void varmap_addcolumn(lprec *lp); +STATIC void varmap_deletecolumn(lprec *lp, int del_col); + +/* Pseudo-cost routines (internal) */ +STATIC BBPSrec *init_PseudoCost(lprec *lp); +STATIC void free_PseudoCost(lprec *lp); +STATIC REAL get_PseudoRange(lprec *lp, int mipvar, int varcode); +STATIC void update_PseudoCost(lprec *lp, int mipvar, int varcode, MYBOOL capupper, REAL varsol); +STATIC REAL get_PseudoCost(lprec *lp, int mipvar, int vartype, REAL varsol); + +/* Matrix access and equation solving routines */ +REAL get_mat_byindex(lprec *lp, int matindex, MYBOOL isrow); +MATitem *get_mat_item(lprec *lp, int matindex, MYBOOL isrow); +STATIC void set_OF_override(lprec *lp, REAL *ofVector); +STATIC void fill_OF_override(lprec *lp); +MYBOOL __WINAPI modifyOF1(lprec *lp, int col_nr, REAL *ofValue, REAL mult); +int __WINAPI getBasisColumn(lprec *lp, int j, int rn[], double bj[]); +int __WINAPI obtain_column(lprec *lp, int varin, REAL *pcol, int *nzlist, int *maxabs); +STATIC int compute_theta(lprec *lp, int rownr, LREAL *theta, REAL HarrisScalar, MYBOOL primal); + +/* Pivot utility routines */ +STATIC int findBasisPos(lprec *lp, int notint, int *var_basic); +STATIC MYBOOL check_degeneracy(lprec *lp, REAL *pcol, int *degencount); + +#endif /* HEADER_lp_lib */ Index: src/tools/solver/lp_solve/lp_matrix.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_matrix.c diff -N src/tools/solver/lp_solve/lp_matrix.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_matrix.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1924 @@ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_price.h" +#include "lp_pricePSE.h" +#include "lp_matrix.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* ------------------------------------------------------------------------- + Basic matrix routines in lp_solve v5.0+ + ------------------------------------------------------------------------- + Author: Michel Berkelaar (to lp_solve v3.2), + Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h, lp_pricerPSE.h, lp_matrix.h + + Release notes: + v5.0.0 1 January 2004 First integrated and repackaged version. + v5.0.1 7 May 2004 Added matrix transpose function. + + ------------------------------------------------------------------------- */ + +STATIC MATrec *mat_create(lprec *lp, int rows, int columns, REAL epsvalue) +{ + MATrec *newmat; + + newmat = (MATrec *) calloc(1, sizeof(*newmat)); + newmat->lp = lp; + + newmat->rows_alloc = 0; + newmat->columns_alloc = 0; + newmat->mat_alloc = 0; + + inc_matrow_space(newmat, rows); + newmat->rows = rows; + inc_matcol_space(newmat, columns); + newmat->columns = columns; + inc_mat_space(newmat, 0); + + newmat->epsvalue = epsvalue; + + return( newmat ); +} + +STATIC void mat_free(MATrec **matrix) +{ + FREE((*matrix)->col_mat); + FREE((*matrix)->col_end); + FREE((*matrix)->row_mat); +#if MatrixRowAccess==RAM_IndexAndCol + FREE((*matrix)->row_col); +#endif + FREE((*matrix)->row_end); + FREE(*matrix); +} + +STATIC MYBOOL inc_mat_space(MATrec *mat, int mindelta) +{ + int spaceneeded; + + if(mindelta <= 0) + mindelta = MAX(mat->rows, mat->columns) + 1; + if(mat->mat_alloc == 0) + spaceneeded = mindelta; + else + spaceneeded = mat_nonzeros(mat) + mindelta; + + if(spaceneeded >= mat->mat_alloc) { + /* Let's allocate at least MAT_START_SIZE entries */ + if(mat->mat_alloc < MAT_START_SIZE) + mat->mat_alloc = MAT_START_SIZE; + + /* Increase the size by RESIZEFACTOR each time it becomes too small */ + while(spaceneeded >= mat->mat_alloc) + mat->mat_alloc += mat->mat_alloc / RESIZEFACTOR; + + mat->col_mat = (MATitem *) realloc(mat->col_mat, (mat->mat_alloc) * sizeof(*(mat->col_mat))); +#if MatrixRowAccess==RAM_Index + allocINT(mat->lp, &(mat->row_mat), mat->mat_alloc, AUTOMATIC); +#elif MatrixRowAccess==RAM_IndexAndCol + allocINT(mat->lp, &(mat->row_mat), mat->mat_alloc, AUTOMATIC); + allocINT(mat->lp, &(mat->row_col), mat->mat_alloc, AUTOMATIC); +#elif MatrixRowAccess==RAM_Pointer + mat->row_mat = (MATitem **) realloc(mat->row_mat, (mat->mat_alloc) * sizeof(*(mat->row_mat))); +#elif MatrixRowAccess==RAM_FullCopy + mat->row_mat = (MATitem *) realloc(mat->row_mat, (mat->mat_alloc) * sizeof(*(mat->row_mat))); +#else + assert(NULL); +#endif + } + return(TRUE); +} + +STATIC MYBOOL inc_matrow_space(MATrec *mat, int deltarows) +{ + int rowsum, oldrowsalloc, rowdelta = DELTAROWALLOC; + MYBOOL status = TRUE; + + /* Adjust lp row structures */ + if(mat->rows+deltarows > mat->rows_alloc) { + deltarows = MAX(deltarows, rowdelta); + oldrowsalloc = mat->rows_alloc; + mat->rows_alloc += deltarows; + rowsum = mat->rows_alloc + 1; + + status = allocINT(mat->lp, &mat->row_end, rowsum, AUTOMATIC); + mat->row_end_valid = FALSE; + } + return( status ); +} + +STATIC MYBOOL inc_matcol_space(MATrec *mat, int deltacols) +{ + int i,colsum, oldcolsalloc, newcolcount; + MYBOOL status = TRUE; + + newcolcount = mat->columns+deltacols; + + if(newcolcount >= mat->columns_alloc) { + + /* Update memory allocation and sizes */ + oldcolsalloc = mat->columns_alloc; + deltacols = MAX(DELTACOLALLOC, newcolcount); + mat->columns_alloc += deltacols; + colsum = mat->columns_alloc + 1; + status = allocINT(mat->lp, &mat->col_end, colsum, AUTOMATIC); + + /* Update column pointers */ + if(oldcolsalloc == 0) + mat->col_end[0] = 0; + for(i = MIN(oldcolsalloc, mat->columns) + 1; i < colsum; i++) + mat->col_end[i] = mat->col_end[i-1]; + mat->row_end_valid = FALSE; + } + return( status ); +} + +STATIC int mat_collength(MATrec *mat, int colnr) +{ + return( mat->col_end[colnr] - mat->col_end[colnr-1] ); +} + +STATIC int mat_rowlength(MATrec *mat, int rownr) +{ + if(mat_validate(mat)) { + if(rownr <= 0) + return( mat->row_end[0] ); + else + return( mat->row_end[rownr] - mat->row_end[rownr-1] ); + } + else + return( 0 ); +} + +STATIC int mat_nonzeros(MATrec *mat) +{ + return( mat->col_end[mat->columns] ); +} + +STATIC int mat_shiftrows(MATrec *mat, int *bbase, int delta) +{ + MATitem *matelm1, *matelm2; + int j, k, i, ii, thisrow, *colend, base; + MYBOOL preparecompact = FALSE; + + if(delta == 0) + return( 0 ); + base = abs(*bbase); + + if(delta > 0) { + + /* Insert row by simply incrementing existing row indeces */ + if(base <= mat->rows) { + k = mat_nonzeros(mat); + for(ii = 0, matelm1 = mat->col_mat; ii < k; ii++, matelm1++) { + if((*matelm1).row_nr >= base) + (*matelm1).row_nr += delta; + } + } + + /* Set defaults (actual basis set in separate procedure) */ + for(i = 0; i < delta; i++) { + ii = base + i; + mat->row_end[ii] = 0; + } + } + else if(base <= mat->rows) { + + /* Check if we should prepare for compacting later + (this is in order to speed up multiple row deletions) */ + preparecompact = (MYBOOL) (*bbase < 0); + if(preparecompact) + *bbase = my_flipsign((*bbase)); + + /* First make sure we don't cross the row count border */ + if(base-delta-1 > mat->rows) + delta = base - mat->rows - 1; + + /* Then scan over all entries shifting and updating rows indeces */ + if(preparecompact) { + k = 0; + for(j = 1, colend = mat->col_end + 1; + j <= mat->columns; j++, colend++) { + i = k; + k = *colend; + matelm2 = mat->col_mat + i; + for(; i < k; i++, matelm2++) { + thisrow = (*matelm2).row_nr; + if(thisrow < base) + continue; + else if(thisrow >= base-delta) + (*matelm2).row_nr += delta; + else + (*matelm2).row_nr = -1; + } + } + } + else { + k = 0; + ii = 0; + matelm1 = mat->col_mat; + matelm2 = matelm1; + for(j = 1, colend = mat->col_end + 1; + j <= mat->columns; j++, colend++) { + i = k; + k = *colend; + matelm2 = mat->col_mat + i; + for(; i < k; i++, matelm2++) { + thisrow = (*matelm2).row_nr; + if(thisrow >= base) { + if(thisrow >= base-delta) + (*matelm2).row_nr += delta; + else + continue; + } + if(matelm1 != matelm2) + *matelm1 = *matelm2; + matelm1++; + ii++; + } + *colend = ii; + } + } + } + return( 0 ); +} + +STATIC int mat_rowcompact(MATrec *mat) +{ + MATitem *matelm1, *matelm2; + int i, ii, j, k, nn, *colend; + + nn = 0; + k = 0; + ii = 0; + matelm1 = mat->col_mat; + matelm2 = matelm1; + for(j = 1, colend = mat->col_end + 1; + j <= mat->columns; j++, colend++) { + i = k; + k = *colend; + matelm2 = mat->col_mat + i; + for(; i < k; i++, matelm2++) { + if((*matelm2).row_nr < 0) { + nn++; + continue; + } + if(matelm1 != matelm2) + *matelm1 = *matelm2; + matelm1++; + ii++; + } + *colend = ii; + } + return(nn); +} + +STATIC int mat_shiftcols(MATrec *mat, int base, int delta) +{ + int i, ii, k; + MATitem *matelm1, *matelm2; + + k = 0; + if(delta == 0) + return( k ); + + if(delta > 0) { + /* Shift pointers right */ + for(ii = mat->columns; ii > base; ii--) { + i = ii + delta; + mat->col_end[i] = mat->col_end[ii]; + } + /* Set defaults */ + for(i = 0; i < delta; i++) { + ii = base + i; + mat->col_end[ii] = mat->col_end[ii-1]; + } + } + else { + + /* First make sure we don't cross the column count border */ + if(base-delta-1 > mat->columns) + delta = base - mat->columns - 1; + + /* Delete sparse matrix data, if required */ + if(base <= mat->columns) { + + i = mat->col_end[base-1]; /* Beginning of data to be deleted */ + matelm1 = mat->col_mat+i; + ii = mat->col_end[base-delta-1]; /* Beginning of data to be shifted left */ + matelm2 = mat->col_mat+ii; + k = ii-i; /* Number of entries to be deleted */ + if(k > 0) { + i = mat_nonzeros(mat); + for(; ii < i; ii++, matelm1++, matelm2++) + *matelm1 = *matelm2; + } + } + + /* Update indexes */ + for(i = base; i <= mat->columns + delta; i++) { + ii = i - delta; + mat->col_end[i] = mat->col_end[ii] - k; + } + } + return( k ); +} + +#if 0 +STATIC int mat_appendrow(MATrec *mat, int count, REAL *row, int *colno, REAL mult) +{ + int i, j, jj, stcol, elmnr, orignr, newnr, firstcol; + MYBOOL *addto = NULL, isA, isNZ; + REAL value; + + /* Check if we are in row order mode and should add as column instead; + the matrix will be transposed at a later stage */ + isA = (MYBOOL) (mat == mat->lp->matA); + isNZ = (MYBOOL) (colno != NULL); + if(mat->is_roworder) { + if(isNZ) + sortREALByINT(row, colno, count, 0, TRUE); + else + row[0] = 0; + return( mat_appendcol(mat, count, row, colno, mult) ); + } + + /* Optionally tally and map the new non-zero values */ + firstcol = 0; + if(isNZ) + newnr = count; + else { + newnr = 0; + if(!allocMYBOOL(mat->lp, &addto, mat->columns + 1, TRUE)) { + mat->lp->spx_status = NOMEMORY; + return( newnr ); + } + for(i = 1; i <= mat->columns; i++) { + if(fabs(row[i]) > mat->epsvalue) { + addto[i] = TRUE; + if(firstcol == 0) + firstcol = i; + newnr++; + } + } + if(newnr == 0) + firstcol = mat->columns+1; + } + + /* Make sure we have sufficient space */ + if(!inc_mat_space(mat, newnr)) { + FREE(addto); + return( 0 ); + } + + /* Insert the non-zero constraint values */ + orignr = mat_nonzeros(mat) - 1; + elmnr = orignr + newnr; + if(isNZ) + jj = newnr-1; + else + jj = mat->columns; + for(; jj >= firstcol; jj--) { + if(isNZ) + j = colno[jj]; + else + j = jj; + stcol = mat->col_end[j] - 1; + mat->col_end[j] = elmnr + 1; + + /* Add a new non-zero entry */ + if(isNZ || addto[j]) { +#ifdef DoMatrixRounding + value = roundToPrecision(row[j], mat->epsvalue); +#else + value = row[j]; +#endif + value *= mult; + if(isA) + value = scaled_mat(mat->lp, value, mat->rows, j); + mat->col_mat[elmnr].value = value; + mat->col_mat[elmnr].row_nr = mat->rows; + newnr--; + elmnr--; + } + + /* Shift previous column entries down */ +#if 1 + i = stcol - mat->col_end[j-1] + 1; + if(i > 0) { + orignr -= i; + elmnr -= i; + MEMMOVE(mat->col_mat+elmnr+1, mat->col_mat+orignr+1, i); + } +#else + for(i = stcol; i >= mat->col_end[j-1]; i--) { + mat->col_mat[elmnr] = mat->col_mat[orignr]; + orignr--; + elmnr--; + } +#endif + } + + FREE(addto); + + return( newnr ); + +} +#else +STATIC int mat_appendrow(MATrec *mat, int count, REAL *row, int *colno, REAL mult) +{ + int i, j, jj = 0, stcol, elmnr, orignr, newnr, firstcol; + MYBOOL *addto = NULL, isA, isNZ; + REAL value; + + /* Check if we are in row order mode and should add as column instead; + the matrix will be transposed at a later stage */ + isA = (MYBOOL) (mat == mat->lp->matA); + isNZ = (MYBOOL) (colno != NULL); + if(mat->is_roworder) { + if(isNZ) + sortREALByINT(row, colno, count, 0, TRUE); + else + row[0] = 0; + return( mat_appendcol(mat, count, row, colno, mult) ); + } + + /* Optionally tally and map the new non-zero values */ + firstcol = mat->columns + 1; + if(isNZ) { + newnr = count; + if(newnr) { + firstcol = colno[0]; + jj = colno[newnr - 1]; + } + } + else { + newnr = 0; + if(!allocMYBOOL(mat->lp, &addto, mat->columns + 1, TRUE)) { + mat->lp->spx_status = NOMEMORY; + return( newnr ); + } + for(i = mat->columns; i >= 1; i--) { + if(fabs(row[i]) > mat->epsvalue) { + addto[i] = TRUE; + firstcol = i; + newnr++; + } + } + } + + /* Make sure we have sufficient space */ + if(!inc_mat_space(mat, newnr)) { + FREE(addto); + return( 0 ); + } + + /* Insert the non-zero constraint values */ + orignr = mat_nonzeros(mat) - 1; + elmnr = orignr + newnr; + + for(j = mat->columns; j >= firstcol; j--) { + stcol = mat->col_end[j] - 1; + mat->col_end[j] = elmnr + 1; + + /* Add a new non-zero entry */ + if(((isNZ) && (j == jj)) || ((addto != NULL) && (addto[j]))) { + newnr--; + if(isNZ) { + value = row[newnr]; + if(newnr) + jj = colno[newnr - 1]; + else + jj = 0; + } + else + value = row[j]; +#ifdef DoMatrixRounding + value = roundToPrecision(value, mat->epsvalue); +#endif + value *= mult; + if(isA) + value = scaled_mat(mat->lp, value, mat->rows, j); + mat->col_mat[elmnr].value = value; + mat->col_mat[elmnr].row_nr = mat->rows; + elmnr--; + } + + /* Shift previous column entries down */ +#if 1 + i = stcol - mat->col_end[j-1] + 1; + if(i > 0) { + orignr -= i; + elmnr -= i; + MEMMOVE(mat->col_mat+elmnr+1, mat->col_mat+orignr+1, i); + } +#else + for(i = stcol; i >= mat->col_end[j-1]; i--) { + mat->col_mat[elmnr] = mat->col_mat[orignr]; + orignr--; + elmnr--; + } +#endif + } + + FREE(addto); + + return( newnr ); + +} +#endif + +STATIC int mat_appendcol(MATrec *mat, int count, REAL *column, int *rowno, REAL mult) +{ + int i, row, elmnr, lastnr; + MATitem *elm; + REAL value; + MYBOOL isA; + + /* Make sure we have enough space */ + if(!inc_mat_space(mat, mat->rows+1)) + return( 0 ); + + if(rowno != NULL) + count--; + isA = (MYBOOL) (mat == mat->lp->matA); + + /* Append sparse regular constraint values */ + elmnr = mat->col_end[mat->columns - 1]; + + if(column != NULL) { + row = -1; + elm = mat->col_mat + elmnr; + for(i = 0 ; i <= count ; i++) { + value = column[i]; + if(fabs(value) > mat->epsvalue) { + if(rowno == NULL) + row = i; + else { + lastnr = row; + row = rowno[i]; + /* Check if we have come to the Lagrangean constraints */ + if(row > mat->rows) break; + if(row <= lastnr) + return( -1 ); + } +#ifdef DoMatrixRounding + value = roundToPrecision(value, mat->epsvalue); +#endif + if(mat->is_roworder) + value *= mult; + else if(isA) { + value = my_chsign(is_chsign(mat->lp, row), value); + value = scaled_mat(mat->lp, value, row, mat->columns); + } + + /* Store the item and update counters */ + (*elm).row_nr = row; + (*elm).value = value; + elm++; + elmnr++; + } + } + + /* Fill dense Lagrangean constraints */ + if(get_Lrows(mat->lp) > 0) + mat_appendcol(mat->lp->matL, get_Lrows(mat->lp), column+mat->rows, NULL, mult); + + } + + /* Set end of data */ + mat->col_end[mat->columns] = elmnr; + + return( mat->col_end[mat->columns] - mat->col_end[mat->columns-1] ); +} + +STATIC int mat_checkcounts(MATrec *mat, int *rownum, int *colnum, MYBOOL freeonexit) +{ + int i, j, n; + + if(rownum == NULL) + allocINT(mat->lp, &rownum, mat->rows + 1, TRUE); + if(colnum == NULL) + allocINT(mat->lp, &colnum, mat->columns + 1, TRUE); + + for(i = 1 ; i <= mat->columns; i++) { + n = mat->col_end[i]; + for(j = mat->col_end[i - 1]; j < n; j++) { + colnum[i]++; + rownum[mat->col_mat[j].row_nr]++; + } + } + + n = 0; + if((mat->lp->do_presolve != PRESOLVE_NONE) && + (mat->lp->spx_trace || (mat->lp->verbose > NORMAL))) { + for(j = 1; j <= mat->columns; j++) + if(colnum[j] == 0) { + n++; + report(mat->lp, FULL, "mat_checkcounts: Variable %s is not used in any constraints\n", + get_col_name(mat->lp, j)); + } + for(i = 0; i <= mat->rows; i++) + if(rownum[i] == 0) { + n++; + report(mat->lp, FULL, "mat_checkcounts: Constraint %s empty\n", + get_row_name(mat->lp, i)); + } + } + + if(freeonexit) { + FREE(rownum); + FREE(colnum); + } + + return( n ); + +} + +STATIC MYBOOL mat_validate(MATrec *mat) +/* Routine to make sure that row mapping arrays are valid */ +{ + int i, j, je, row_nr, *rownum; + MATitem *matelem; + + if(!mat->row_end_valid) { + + MEMCLEAR(mat->row_end, mat->rows + 1); + allocINT(mat->lp, &rownum, mat->rows + 1, TRUE); + + /* First tally row counts and then cumulate them */ + matelem = mat->col_mat; + j = mat_nonzeros(mat); + for(i = 0; i < j; i++, matelem++) + mat->row_end[(*matelem).row_nr]++; + for(i = 1; i <= mat->rows; i++) + mat->row_end[i] += mat->row_end[i - 1]; + + /* Calculate the column index for every non-zero */ + for(i = 1; i <= mat->columns; i++) { + j = mat->col_end[i - 1]; + je = mat->col_end[i]; + for(matelem = mat->col_mat + j; j < je; j++, matelem++) { + row_nr = (*matelem).row_nr; + (*matelem).col_nr = i; +#if MatrixRowAccess==RAM_IndexAndCol + ROW_MAT_COL(j) = i; +#endif + if(row_nr == 0) + mat->row_mat[rownum[row_nr]] = ROW_MAT_ITEM(i, j, matelem); + else + mat->row_mat[mat->row_end[row_nr - 1] + rownum[row_nr]] = ROW_MAT_ITEM(i, j, matelem); + rownum[row_nr]++; + } + } + + FREE(rownum); + mat->row_end_valid = TRUE; + } + + if(mat == mat->lp->matA) + mat->lp->model_is_valid = TRUE; + return( TRUE ); +} + +/* Implement combined binary/linear sub-search for matrix look-up */ +int mat_findelm(MATrec *mat, int row, int column) +{ + int low, high, mid, item; + +#if 0 + if(mat->row_end_valid && (row > 0) && + (ROW_MAT_COL(mat->row_mat[(low = mat->row_end[row-1])]) == column)) + return(low); +#endif + + if((column < 1) || (column > mat->columns)) { + report(mat->lp, IMPORTANT, "mat_findelm: Column %d out of range\n", column); + return( -1 ); + } + if((row < 0) || (row > mat->rows)) { + report(mat->lp, IMPORTANT, "mat_findelm: Row %d out of range\n", row); + return( -1 ); + } + + low = mat->col_end[column - 1]; + high = mat->col_end[column] - 1; + if(low > high) + return( -2 ); + + /* Do binary search logic */ + mid = (low+high) / 2; + item = mat->col_mat[mid].row_nr; + while(high - low > LINEARSEARCH) { + if(item < row) { + low = mid + 1; + mid = (low+high) / 2; + item = mat->col_mat[mid].row_nr; + } + else if(item > row) { + high = mid - 1; + mid = (low+high) / 2; + item = mat->col_mat[mid].row_nr; + } + else { + low = mid; + high = mid; + } + } + + /* Do linear scan search logic */ + if((high > low) && (high - low <= LINEARSEARCH)) { + item = mat->col_mat[low].row_nr; + while((low < high) && (item < row)) { + low++; + item = mat->col_mat[low].row_nr; + } + if(item == row) + high = low; + } + + if((low == high) && (row == item)) + return( low ); + else + return( -2 ); +} + +int mat_findins(MATrec *mat, int row, int column, int *insertpos, MYBOOL validate) +{ + int low, high, mid, item, exitvalue, insvalue; + +#if 0 + if(mat->row_end_valid && (row > 0) && + (ROW_MAT_COL(mat->row_mat[(low = mat->row_end[row-1])]) == column)) { + insvalue = low; + exitvalue = low; + goto Done; + } +#endif + + insvalue = -1; + + if((column < 1) || (column > mat->columns)) { + if((column > 0) && !validate) { + insvalue = mat->col_end[mat->columns]; + exitvalue = -2; + goto Done; + } + report(mat->lp, IMPORTANT, "mat_findins: Column %d out of range\n", column); + exitvalue = -1; + goto Done; + } + if((row < 0) || (row > mat->rows)) { + if((row >= 0) && !validate) { + insvalue = mat->col_end[column]; + exitvalue = -2; + goto Done; + } + report(mat->lp, IMPORTANT, "mat_findins: Row %d out of range\n", row); + exitvalue = -1; + goto Done; + } + + low = mat->col_end[column - 1]; + insvalue = low; + high = mat->col_end[column] - 1; + if(low > high) { + exitvalue = -2; + goto Done; + } + + /* Do binary search logic */ + mid = (low+high) / 2; + item = mat->col_mat[mid].row_nr; + while(high - low > LINEARSEARCH) { + if(item < row) { + low = mid + 1; + mid = (low+high) / 2; + item = mat->col_mat[mid].row_nr; + } + else if(item > row) { + high = mid - 1; + mid = (low+high) / 2; + item = mat->col_mat[mid].row_nr; + } + else { + low = mid; + high = mid; + } + } + + /* Do linear scan search logic */ + if((high > low) && (high - low <= LINEARSEARCH)) { + item = mat->col_mat[low].row_nr; + while((low < high) && (item < row)) { + low++; + item = mat->col_mat[low].row_nr; + } + if(item == row) + high = low; + } + + insvalue = low; + if((low == high) && (row == item)) + exitvalue = low; + else { + if((low < mat->col_end[column]) && (mat->col_mat[low].row_nr < row)) + insvalue++; + exitvalue = -2; + } + +Done: + if(insertpos != NULL) + (*insertpos) = insvalue; + return( exitvalue ); +} + +STATIC REAL mat_getitem(MATrec *mat, int row, int column) +{ + int elmnr; + +#ifdef DirectOverrideOF + if((row == 0) && (mat == mat->lp->matA) && (mat->lp->OF_override != NULL)) + return( mat->lp->OF_override[column] ); + else +#endif + { + elmnr = mat_findelm(mat, row, column); + if(elmnr >= 0) + return(mat->col_mat[elmnr].value); + else + return(0); + } +} + +STATIC void mat_multrow(MATrec *mat, int row_nr, REAL mult) +{ + int i, k1, k2; + + if(row_nr == 0) { + k2 = mat->col_end[0]; + for(i = 1; i <= mat->columns; i++) { + k1 = k2; + k2 = mat->col_end[i]; + if((k1 < k2) && (mat->col_mat[k1].row_nr == row_nr)) + mat->col_mat[k1].value *= mult; + } + } + else if(mat_validate(mat)) { + if(row_nr == 0) + k1 = 0; + else + k1 = mat->row_end[row_nr-1]; + k2 = mat->row_end[row_nr]; + for(i = k1; i < k2; i++) + ROW_MAT_VALUE(mat->row_mat[i]) *= mult; + } +} + +STATIC void mat_multcol(MATrec *mat, int col_nr, REAL mult) +{ + int i, ie; + MYBOOL isA; + +#ifdef Paranoia + if(col_nr < 1 || col_nr > mat->columns) { + report(mat->lp, IMPORTANT, "mult_column: Column %d out of range\n", col_nr); + return; + } +#endif + + isA = (MYBOOL) (mat == mat->lp->matA); + + ie = mat->col_end[col_nr]; + for(i = mat->col_end[col_nr - 1]; i < ie; i++) + mat->col_mat[i].value *= mult; + if(isA && (get_Lrows(mat->lp) > 0)) + mat_multcol(mat->lp->matL, col_nr, mult); +} + +STATIC MYBOOL mat_setvalue(MATrec *mat, int Row, int Column, REAL Value, MYBOOL doscale) +{ + int elmnr, lastelm, i; + MYBOOL isA; + + /* This function is inefficient if used to add new matrix entries in + other places than at the end of the matrix. OK for replacing existing + a non-zero value with another non-zero value */ + isA = (MYBOOL) (mat == mat->lp->matA); + if(isA && mat->is_roworder) + mat_transpose(mat, TRUE); + + /* Set small numbers to zero */ + if(fabs(Value) < mat->epsvalue) + Value = 0; +#ifdef DoMatrixRounding + else + Value = roundToPrecision(Value, mat->epsvalue); +#endif + + /* Check if we need to update column space */ + if(Column > mat->columns) { + if(isA) + inc_col_space(mat->lp, Column - mat->columns); + else + inc_matcol_space(mat, Column - mat->columns); + } + + /* Find out if we already have such an entry, or return insertion point */ + i = mat_findins(mat, Row, Column, &elmnr, FALSE); + if(i == -1) + return(FALSE); + + if(isA) { + if((Row > 0) && mat->lp->is_basic[mat->rows+Column]) + mat->lp->basis_valid = FALSE; + mat->lp->doInvert = TRUE; + } + + if(i >= 0) { + /* there is an existing entry */ + if(fabs(Value) > mat->epsvalue) { /* we replace it by something non-zero */ + if(isA) { + Value = my_chsign(is_chsign(mat->lp, Row), Value); + if(doscale && mat->lp->scaling_used) + Value = scaled_mat(mat->lp, Value, Row,Column); + } + mat->col_mat[elmnr].value = Value; + } + else { /* setting existing non-zero entry to zero. Remove the entry */ + /* This might remove an entire column, or leave just a bound. No + nice solution for that yet */ + + /* Shift up tail end of the matrix */ + lastelm = mat_nonzeros(mat); + for(i = elmnr; i < lastelm ; i++) + mat->col_mat[i] = mat->col_mat[i + 1]; + for(i = Column; i <= mat->columns; i++) + mat->col_end[i]--; + + mat->row_end_valid = FALSE; + } + } + else if(fabs(Value) > mat->epsvalue) { + /* no existing entry. make new one only if not nearly zero */ + /* check if more space is needed for matrix */ + if (!inc_mat_space(mat, 1)) + return(FALSE); + + if(Column > mat->columns) { + if(isA) + shift_coldata(mat->lp, mat->columns+1, Column - mat->columns); + else + mat_shiftcols(mat, mat->columns+1, Column - mat->columns); + } + + /* Shift down tail end of the matrix by one */ + lastelm = mat_nonzeros(mat); + for(i = lastelm; i > elmnr ; i--) + mat->col_mat[i] = mat->col_mat[i - 1]; + + /* Set new element */ + mat->col_mat[elmnr].row_nr = Row; + if(isA) { + Value = my_chsign(is_chsign(mat->lp, Row), Value); + if(doscale) + Value = scaled_mat(mat->lp, Value, Row, Column); + } + mat->col_mat[elmnr].value = Value; + + /* Update column indexes */ + for(i = Column; i <= mat->columns; i++) + mat->col_end[i]++; + + mat->row_end_valid = FALSE; + } + + if(isA && (mat->lp->var_is_free != NULL) && (mat->lp->var_is_free[Column] > 0)) + return( mat_setvalue(mat, Row, mat->lp->var_is_free[Column], -Value, doscale) ); + return(TRUE); +} + +STATIC int mat_findcolumn(MATrec *mat, int matindex) +{ + int j; + + for(j = 1; j <= mat->columns; j++) { + if(matindex < mat->col_end[j]) + break; + } + return(j); +} + +STATIC MYBOOL mat_transpose(MATrec *mat, MYBOOL col1_row0) +{ + int i, j, nz, k; + MATitem *newmat; + MYBOOL status; + + if(col1_row0 && !mat->is_roworder) + return( FALSE ); + + status = mat_validate(mat); + if(status) { + + /* Create a column-ordered sparse element list; "column" index must be shifted */ + nz = mat_nonzeros(mat); + if(nz > 0) { + newmat = (MATitem *) malloc((mat->mat_alloc) * sizeof(*(mat->col_mat))); + if(col1_row0) { /* Transposition of fast constraint add mode */ + for(i = nz-1; i >= 0 ; i--) { + newmat[i] = mat->col_mat[mat->row_mat[i]]; + newmat[i].row_nr = newmat[i].col_nr-1; + } + } + else { /* Transposition where row index 0 becomes columns+1 */ + j = mat->row_end[0]; + for(i = nz-1; i >= j ; i--) { + k = i-j; + newmat[k] = mat->col_mat[mat->row_mat[i]]; + newmat[k].row_nr = newmat[k].col_nr; + } + for(i = j-1; i >= 0 ; i--) { + k = nz-j+i; + newmat[k] = mat->col_mat[mat->row_mat[i]]; + newmat[k].row_nr = newmat[k].col_nr; + } + } + swapPTR((void **) &mat->col_mat, (void **) &newmat); + FREE(newmat); + } + + /* Transfer row start to column start position; must adjust for different offsets */ + if(mat->rows == mat->rows_alloc) + inc_matcol_space(mat, 1); + if(col1_row0) + mat->columns--; + else { + j = mat->row_end[0]; + for(i = mat->rows; i >= 1; i--) + mat->row_end[i] -= j; + mat->rows++; + mat->row_end[mat->rows] = nz; + } + swapPTR((void **) &mat->row_end, (void **) &mat->col_end); + + /* Swap array sizes */ + swapINT(&mat->rows, &mat->columns); + swapINT(&mat->rows_alloc, &mat->columns_alloc); + + /* Finally set current storage mode */ + mat->is_roworder = (MYBOOL) !mat->is_roworder; + mat->row_end_valid = FALSE; + } + return(status); +} + +/* ---------------------------------------------------------------------------------- */ +/* High level matrix inverse and product routines in lp_solve */ +/* ---------------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------------------- */ +/* Inverse handling */ +/* ---------------------------------------------------------------------------------- */ +/* + A brief description of the inversion logic in lp_solve + ----------------------------------------------------------------- + + In order to better understand the code in lp_solve in relation to + standard matrix-based textbook descriptions, I (KE) will briefly + explain the conventions and associated matrix algebra. The matrix + description of a linear program (as represented by lp_solve) goes + like this: + + maximize c'x + subject to r <= Ax <= b + where l <= x <= u + + The matrix A is partitioned into two column sets [B|N], where B is + a square matrix of "basis" variables containing non-fixed + variables of the linear program at any given stage and N is the + submatrix of corresponding non-basic, fixed variables. The + variables (columns) in N may be fixed at their lower or upper levels. + + Similarly, the c vector is partitioned into the basic and non-basic + parts [z|n]. + + lp_solve stores the objective vector c and the data matrix A in a + common sparse format where c is put as the 0-th row of A. This may + be called the "A~" form: + + A~ = [ c ] + [ A ] + + Linear programming involves solving linear equations based on B, + various updates and bookkeeping operations. The relationship + between the common storage of c and A (e.g. A~) vs. the inverse of + B therefore needs to be understood. In lp_solve, B is stored in + an expanded, bordered format using the following (non-singular) + representation: + + B~ = [ 1 z ] + [ 0 B ] + + Any inversion engine used by lp_solve must therefore explicitly + represent and handle the implications of this structure for + associated matrix multiplications. + + The standard matrix formula for computing the inverse of a bordered + matrix shows what the inversion of B~ actually produces: + + Inv(B~) = [ 1 -z*Inv(B) ] + [ 0 Inv(B) ] + + The A~ and B~ representations mean that it becomes necessary to be + aware of the side effects of the presence of the top row when doing + product operations such as b'N, btran and ftran. A very nice + thing about the matrix representation in lp_solve is that a very + common update in the simplex algorithm (reduced costs) is obtained + simply by setting 1 at the top of the vector being pre-multiplied + with Inv(B~). + + However, if the objective vector (c) is changed, the representation + requires B / B~ to be reinverted. Also, when doing ftran, btran + and x'A-type operations, you will patently get the incorrect result + if you simply copy the operations described in textbooks. First I'll + show the results of an ftran operation: + + Bx = a ==> x = ftran(a) + + In lp_solve, this operation solves: + + [ 1 z ] [y] = [d] + [ 0 B ] [x] [a] + + Using the Inv(B~) expression earlier, the ftran result is: + + [y] = [ 1 -z*Inv(B) ] [d] = [ d - z*Inv(B)*a ] + [x] [ 0 Inv(B) ] [a] [ Inv(B)*a ] + + Similarily, doing the left solve - performing the btran calculation: + + [x y] [ 1 z ] = [d a'] + [ 0 B ] + + ... will produce the following result in lp_solve: + + [x y] = [d a'] [ 1 -z*Inv(B) ] = [ d | -d*z*Inv(B) + a'*Inv(B) ] + [ 0 Inv(B) ] + + So, if you thought you were computing "a'*Inv(B)", look again. + In order to produce the desired result, you have to set d to 0 + before the btran operation. + + Equipped with this understanding, I hope that you see that + the approach in lp_solve is actually pretty convenient. It + also makes it far easier to extend functionality by drawing on + formulas and expressions from LP literature that assume the + conventional syntax and representation. + + Kjell Eikland -- November 2003. + +*/ + +STATIC MYBOOL __WINAPI invert(lprec *lp, MYBOOL shiftbounds) +{ + MYBOOL *usedpos, resetbasis; + REAL test; + int k, i, j; + int singularities, usercolB; + + /* Make sure the tags are correct */ + if(!mat_validate(lp->matA)) { + lp->spx_status = INFEASIBLE; + return(FALSE); + } + + /* Create the inverse management object at the first call to invert() */ + if(lp->invB == NULL) + lp->bfp_init(lp, lp->rows, 0); + else + lp->bfp_preparefactorization(lp); + singularities = 0; + + /* Must save spx_status since it is used to carry information about + the presence and handling of singular columns in the matrix */ + if(userabort(lp, MSG_INVERT)) + return(FALSE); + +#ifdef Paranoia + if(lp->spx_trace) + report(lp, DETAILED, "invert: iteration %6d, inv-length %4d, OF " MPSVALUEMASK " \n", + get_total_iter(lp), lp->bfp_colcount(lp), (double) -lp->rhs[0]); +#endif + + /* Store state of pre-existing basis, and at the same time check if + the basis is I; in this case take the easy way out */ + if(!allocMYBOOL(lp, &usedpos, lp->sum + 1, TRUE)) { + lp->spx_status = NOMEMORY; + lp->bb_break = TRUE; + return(FALSE); + } + usedpos[0] = TRUE; + usercolB = 0; + for(i = 1; i <= lp->rows; i++) { + k = lp->var_basic[i]; + if(k > lp->rows) + usercolB++; + usedpos[k] = TRUE; + } +#ifdef Paranoia + if(!verifyBasis(lp)) + report(lp, SEVERE, "invert: Invalid basis detected (iteration %d).\n", + get_total_iter(lp)); +#endif + + /* Tally matrix nz-counts and check if we should reset basis + indicators to all slacks */ + resetbasis = (MYBOOL) ((usercolB > 0) && lp->bfp_canresetbasis(lp)); + k = 0; + for(i = 1; i <= lp->rows; i++) { + if(lp->var_basic[i] > lp->rows) + k += mat_collength(lp->matA, lp->var_basic[i] - lp->rows); + if(resetbasis) { + j = lp->var_basic[i]; + if(j > lp->rows) + lp->is_basic[j] = FALSE; + lp->var_basic[i] = i; + lp->is_basic[i] = TRUE; + } + } + /* If we don't reset the basis, optionally sort it by index. + (experimental code at one time used for debugging) */ +#if 0 + if(!resetbasis) { + j = lp->rows; + for(i = 1; i <= lp->rows; i++) { + if(lp->is_basic[i]) + lp->var_basic[i] = i; + else { + j++; + while((j <= lp->sum) && !lp->is_basic[j]) + j++; + lp->var_basic[i] = j; + } + } + } +#endif + + /* Now do the refactorization */ + singularities = lp->bfp_factorize(lp, usercolB, k, usedpos); + + /* Do user reporting */ + if(userabort(lp, MSG_INVERT)) + goto Cleanup; + +#ifdef Paranoia + if(lp->spx_trace) { + k = lp->bfp_nonzeros(lp, FALSE); + report(lp, DETAILED, "invert: inv-length %4d, inv-NZ %4d\n", + lp->bfp_colcount(lp), k); + } +#endif + + /* Finalize factorization/inversion */ + lp->bfp_finishfactorization(lp); + + /* Recompute the RHS ( Ref. lp_solve inverse logic and Chvatal p. 121 ) */ +#ifdef DebugInv + blockWriteLREAL(stdout, "RHS-values pre invert", lp->rhs, 0, lp->rows); +#endif + recompute_solution(lp, shiftbounds); + restartPricer(lp, AUTOMATIC); +#ifdef DebugInv + blockWriteLREAL(stdout, "RHS-values post invert", lp->rhs, 0, lp->rows); +#endif + +Cleanup: + /* Check for numerical instability indicated by frequent refactorizations */ + test = get_refactfrequency(lp, FALSE); + if(test < MIN_REFACTFREQUENCY) { + test = get_refactfrequency(lp, TRUE); + report(lp, NORMAL, "invert: Refactorization frequency %.1g indicates numeric instability\n", + test); + lp->spx_status = NUMFAILURE; + } + + FREE(usedpos); + return((MYBOOL) (singularities <= 0)); +} /* invert */ + + +STATIC MYBOOL fimprove(lprec *lp, REAL *pcol, int *nzidx, REAL roundzero) +{ + REAL *errors, sdp; + int j; + MYBOOL Ok = TRUE; + + allocREAL(lp, &errors, lp->rows + 1, FALSE); + if(errors == NULL) { + lp->spx_status = NOMEMORY; + Ok = FALSE; + return(Ok); + } + MEMCOPY(errors, pcol, lp->rows + 1); + lp->bfp_ftran_normal(lp, pcol, nzidx); + prod_Ax(lp, SCAN_ALLVARS, pcol, NULL, 0, 0.0, -1, errors, NULL); + lp->bfp_ftran_normal(lp, errors, NULL); + + sdp = 0; + for(j = 1; j <= lp->rows; j++) + if(fabs(errors[j])>sdp) + sdp = fabs(errors[j]); + if(sdp > lp->epsmachine) { + report(lp, DETAILED, "Iterative FTRAN correction metric %g", sdp); + for(j = 1; j <= lp->rows; j++) { + pcol[j] += errors[j]; + my_roundzero(pcol[j], roundzero); + } + } + FREE(errors); + return(Ok); +} + +STATIC MYBOOL bimprove(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero) +{ + int j; + REAL *errors, err, maxerr; + MYBOOL Ok = TRUE; + + allocREAL(lp, &errors, lp->sum + 1, FALSE); + if(errors == NULL) { + lp->spx_status = NOMEMORY; + Ok = FALSE; + return(Ok); + } + MEMCOPY(errors, rhsvector, lp->sum + 1); + + /* Solve Ax=b for x, compute b back */ + lp->bfp_btran_normal(lp, errors, nzidx); + prod_xA(lp, SCAN_ALLVARS+USE_BASICVARS, errors, NULL, XRESULT_FREE, 0.0, 1.0, + errors, NULL); + + /* Take difference with ingoing values, while shifting the column values + to the rows section and zeroing the columns again */ + for(j = 1; j <= lp->rows; j++) + errors[j] = errors[lp->rows+lp->var_basic[j]] - rhsvector[j]; + for(j = lp->rows; j <= lp->sum; j++) + errors[j] = 0; + + /* Solve the b errors for the iterative x adjustment */ + lp->bfp_btran_normal(lp, errors, NULL); + + /* Generate the adjustments and compute statistic */ + maxerr = 0; + for(j = 1; j <= lp->rows; j++) { + if(lp->var_basic[j]<=lp->rows) continue; + err = errors[lp->rows+lp->var_basic[j]]; + if(fabs(err)>maxerr) + maxerr = fabs(err); + } + if(maxerr > lp->epsmachine) { + report(lp, DETAILED, "Iterative BTRAN correction metric %g", maxerr); + for(j = 1; j <= lp->rows; j++) { + if(lp->var_basic[j]<=lp->rows) continue; + rhsvector[j] += errors[lp->rows+lp->var_basic[j]]; + my_roundzero(rhsvector[j], roundzero); + } + } + FREE(errors); + return(Ok); +} + +STATIC void ftran(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero) +{ + if((lp->improve & IMPROVE_FTRAN) && lp->bfp_pivotcount(lp)) + fimprove(lp, rhsvector, nzidx, roundzero); + else { + lp->bfp_ftran_normal(lp, rhsvector, nzidx); + } +} + +STATIC void btran(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero) +{ + if((lp->improve & IMPROVE_BTRAN) && lp->bfp_pivotcount(lp)) + bimprove(lp, rhsvector, nzidx, roundzero); + else { + lp->bfp_btran_normal(lp, rhsvector, nzidx); + } +} + +STATIC MYBOOL fsolve(lprec *lp, int varin, REAL *pcol, int *nzidx, REAL roundzero, REAL ofscalar, MYBOOL prepareupdate) +/* Was setpivcol in versions earlier than 4.0.1.8 - KE */ +{ + MYBOOL ok = TRUE; + + if(varin > 0) + obtain_column(lp, varin, pcol, nzidx, NULL); + + /* Solve, adjusted for objective function scalar */ + pcol[0] *= ofscalar; + if(prepareupdate) + lp->bfp_ftran_prepare(lp, pcol, nzidx); + else + ftran(lp, pcol, nzidx, roundzero); + + return(ok); + +} /* fsolve */ + + +STATIC MYBOOL bsolve(lprec *lp, int row_nr, REAL *rhsvector, int *nzidx, REAL roundzero, REAL ofscalar) +{ + MYBOOL ok = TRUE; + + if(row_nr >= 0) + row_nr = obtain_column(lp, row_nr, rhsvector, nzidx, NULL); + + /* Solve, adjusted for objective function scalar */ + rhsvector[0] *= ofscalar; + btran(lp, rhsvector, nzidx, roundzero); + + return(ok); + +} /* bsolve */ + + +STATIC int prod_Ax(lprec *lp, int varset, REAL *input, int *nzinput, + int range, REAL roundzero, REAL multfactor, + REAL *output, int *nzoutput) +/* prod_Ax is only used in fimprove; note that it is NOT VALIDATED/verified as of 20030801 - KE */ +{ + int j, k, colnr, Extrap, + ib, ie, vb, ve; + MYBOOL omitfixed, omitnonfixed; + REAL sdp; + MATitem *matentry; + + /* Find what variable range to scan (default is all) */ + /* First determine the starting position; add from the top, going down */ + Extrap = abs(lp->Extrap); + vb = 1; + if(varset & SCAN_ARTIFICIALVARS) + vb = lp->sum - Extrap + 1; + if(varset & SCAN_USERVARS) + vb = lp->rows + 1; + if(varset & SCAN_SLACKVARS) + vb = 1; + + /* Then determine the ending position, add from the bottom, going up */ + ve = lp->sum; + if(varset & SCAN_SLACKVARS) + ve = lp->rows; + if(varset & SCAN_USERVARS) + ve = lp->sum - Extrap; + if(varset & SCAN_ARTIFICIALVARS) + ve = lp->sum; + + /* Determine exclusion columns */ + omitfixed = (MYBOOL) ((varset & OMIT_FIXED) != 0); + omitnonfixed = (MYBOOL) ((varset & OMIT_NONFIXED) != 0); + if(omitfixed && omitnonfixed) + return(FALSE); + + /* Scan the basic columns */ + for(j = 1; j <= lp->rows; j++) { + colnr = lp->var_basic[j]; + + /* Do exclusions */ + if(colnr < vb || colnr > ve) + continue; + + sdp = lp->upbo[colnr]; + if((omitfixed && (sdp == 0)) || + (omitnonfixed && (sdp != 0))) + continue; + + /* Exclusions done, perform the multiplication */ + sdp = multfactor*input[j]; + if(colnr <= lp->rows) /* A slack variable is in the basis */ + output[colnr] += sdp; + else { /* A normal variable is in the basis */ + colnr -= lp->rows; + ie = lp->matA->col_end[colnr]; + ib = lp->matA->col_end[colnr - 1]; + for(matentry = lp->matA->col_mat + ib; ib < ie; ib++, matentry++) { + k = (*matentry).row_nr; + output[k] += (*matentry).value*sdp; + } + } + } + roundVector(output+1, lp->rows-1, roundzero); + + return(TRUE); +} + +STATIC int prod_xA(lprec *lp, int varset, REAL *input, int *nzinput, + int range, REAL roundzero, REAL ofscalar, + REAL *output, int *nzoutput) +/* Note that the dot product xa is stored at the active column index of A, i.e. of a. + This means that if the basis only contains non-slack variables, output may point to + the same vector as input, without overwriting the [0..rows] elements. */ +{ + int i, rownr, varnr, Extrap, vb, ve, ib, ie; + MYBOOL omitfixed, omitnonfixed; + RREAL vmax; + REGISTER RREAL v; + REAL matValue, *rhsvector; + int inz, *rowin, countNZ = 0; + MATrec *mat = lp->matA; + MATitem *matItem; + + /* Clean output area (only necessary if we are returning the full vector) */ + if(nzoutput == NULL) { + if(input == output) + MEMCLEAR(output+lp->rows+1, lp->columns); + else + MEMCLEAR(output, lp->sum+1); + } + + /* Find what variable range to scan - default is {SCAN_USERVARS} */ + /* First determine the starting position; add from the top, going down */ + Extrap = abs(lp->Extrap); + vb = lp->rows + 1; + if(varset & SCAN_ARTIFICIALVARS) + vb = lp->sum - Extrap + 1; + if(varset & SCAN_USERVARS) + vb = lp->rows + 1; + if(varset & SCAN_SLACKVARS) + vb = 1; + + /* Then determine the ending position, add from the bottom, going up */ + ve = lp->sum; + if(varset & SCAN_SLACKVARS) + ve = lp->rows; + if(varset & SCAN_USERVARS) + ve = lp->sum - Extrap; + if(varset & SCAN_ARTIFICIALVARS) + ve = lp->sum; + + /* Adjust for partial pricing */ + if(varset & SCAN_PARTIALBLOCK) { + if(vb > lp->rows) + vb = MAX(vb, partial_blockStart(lp, FALSE)); + ve = MIN(ve, partial_blockEnd(lp, FALSE)); + } + + /* Determine exclusion columns */ + omitfixed = (MYBOOL) ((varset & OMIT_FIXED) != 0); + omitnonfixed = (MYBOOL) ((varset & OMIT_NONFIXED) != 0); + if(omitfixed && omitnonfixed) + return(FALSE); + + /* Scan the target colums */ + vmax = 0; + for(varnr = vb, rhsvector = output + vb; + varnr <= ve; varnr++, rhsvector++) { + + /* Skip gap in the specified colum scan range (possibly user variables) */ + if((varnr > lp->rows) && (varnr <= lp->sum-Extrap) && !(varset & SCAN_USERVARS)) + continue; + + /* Find if the variable is in the scope - default is {Ø} */ + i = lp->is_basic[varnr]; + if((varset & USE_BASICVARS) > 0 && (i)) + ; + else if((varset & USE_NONBASICVARS) > 0 && (!i)) + ; + else + continue; + + v = lp->upbo[varnr]; + if((omitfixed && (v == 0)) || + (omitnonfixed && (v != 0))) + continue; + + if(varnr <= lp->rows) { + v = input[varnr]; + } + else { + i = varnr - lp->rows; + v = 0; + ie = mat->col_end[i]; + ib = mat->col_end[i - 1]; + if(ib < ie) { + + /* Do dense input vector version */ + if(nzinput == NULL) { + + /* Handle the objective row specially */ + matItem = mat->col_mat + ib; + rownr = (*matItem).row_nr; + if(rownr == 0) { + matValue = (*matItem).value; + ib++; + matItem++; + } + else { + rownr = 0; + matValue = 0; + } + if(modifyOF1(lp, varnr, &matValue, ofscalar)) + v += input[rownr] * matValue; + + /* Then loop over all regular rows */ + if(ib < ie) + for(; ib < ie; ib++, matItem++) { + v += input[(*matItem).row_nr] * (*matItem).value; + } + } + + /* Do sparse input vector version */ + else { + + /* Initialize pointers */ + inz = 1; + rowin = nzinput+inz; + matItem = mat->col_mat + ib; + rownr = matItem->row_nr; + ie--; + + /* Handle OF row separately (since it can be overridden) */ + if(*rowin == 0) { + if(rownr == 0) { + matValue = matItem->value; + ib++; + /* Step forward at right */ + if(ib <= ie) { + matItem++; + rownr = matItem->row_nr; + } + } + else + matValue = 0; + if(modifyOF1(lp, varnr, &matValue, ofscalar)) + v += input[0]*matValue; + /* Step forward at left */ + inz++; + rowin++; + } + + /* Then loop over all non-OF rows */ + while((inz <= *nzinput) && (ib <= ie)) { + + /* Try to synchronize at right */ + while((*rowin > rownr) && (ib < ie)) { + ib++; + matItem++; + rownr = matItem->row_nr; + } + /* Try to synchronize at left */ + while((*rowin < rownr) && (inz < *nzinput)) { + inz++; + rowin++; + } + /* Perform dot product operation if there was a match */ + if(*rowin == rownr) { + v += input[*rowin] * matItem->value; + /* Step forward at left */ + inz++; + rowin++; + } + } + } + } +#ifndef Prod_xA_RoundRelative + my_roundzero(v, roundzero); +#endif + } + vmax = MAX(vmax, fabs((REAL) v)); + if((v != 0) && ((range == XRESULT_FREE) || (v*range > 0))) { + countNZ++; + if(nzoutput != NULL) + nzoutput[countNZ] = varnr; + } + *rhsvector = (REAL) v; + } + + /* Check if we should do relative rounding */ +#ifdef Prod_xA_RoundRelative + if((roundzero > 0) && (nzoutput != NULL)) { + ie = 0; + for(i = 1; i <= countNZ; i++) { + rownr = nzoutput[i]; + if(fabs(output[rownr])/vmax < roundzero) + output[rownr] = 0; + else if((range == XRESULT_FREE) || (output[rownr]*range > 0)) { + ie++; + nzoutput[ie] = rownr; + } + } + countNZ = ie; + } +#endif + + if(nzoutput != NULL) + *nzoutput = countNZ; + return(countNZ); +} + +STATIC void prod_xA2(lprec *lp, REAL *prow, int prange, REAL proundzero, int *nzprow, + REAL *drow, int drange, REAL droundzero, int *nzdrow, REAL ofscalar) +{ +#ifdef UseDouble_prod_xA + prod_xA(lp, SCAN_ALLVARS+ + SCAN_PARTIALBLOCK+ + USE_NONBASICVARS+OMIT_FIXED, + prow, NULL, XRESULT_RC, proundzero, ofscalar, + prow, nzprow); + prod_xA(lp, SCAN_ALLVARS+ + SCAN_PARTIALBLOCK+ + USE_NONBASICVARS+OMIT_FIXED, + drow, NULL, XRESULT_FREE, droundzero, ofscalar, + drow, nzdrow); +#else + int i, ii, varnr, rownr, ib, ie; + RREAL dmax, pmax; + REGISTER RREAL d, p; + MATrec *mat = lp->matA; + REAL matValue; + MATitem *matItem; + + /* Loop over slack variables only if we do sparse mapping; since slack variable + coefficients are all 1, values in the incoming vectors are preserved */ + pmax = 0; + dmax = 0; + i = partial_blockStart(lp, FALSE); + ii = partial_blockEnd(lp, FALSE); + if(ii == lp->sum) + ii -= abs(lp->Extrap); + if((nzprow != NULL) || (nzdrow != NULL)) { + if(nzprow != NULL) + *nzprow = 0; + if(nzdrow != NULL) + *nzdrow = 0; + for(; i <= lp->rows; i++) + if(!lp->is_basic[i] && (lp->upbo[i] > 0)) { + pmax = MAX(pmax, fabs(prow[i])); + dmax = MAX(dmax, fabs(drow[i])); + if((nzprow != NULL) && (prow[i] != 0) && + ((prange == XRESULT_FREE) || (prow[i]*my_chsign(lp->is_lower[i], -1) > 0))) { + (*nzprow)++; + nzprow[*nzprow] = i; + } + if((nzdrow != NULL) && (drow[i] != 0) && + ((drange == XRESULT_FREE) || (drow[i]*my_chsign(lp->is_lower[i], -1) > 0))) { + (*nzdrow)++; + nzdrow[*nzdrow] = i; + } + } + } + + /* Loop over user variables or any active subset */ + i = MAX(i, lp->rows + 1); + for(; i <= ii; i++) { + + varnr = i - lp->rows; + + /* Only non-basic user variables */ + if(lp->is_basic[i]) + continue; + + /* Use only non-fixed variables */ + if(lp->upbo[i] == 0) + continue; + + p = 0; + d = 0; + ie = mat->col_end[varnr]; + ib = mat->col_end[varnr - 1]; + + if(ib < ie) { + + /* Handle the objective row specially */ + matItem = mat->col_mat + ib; + rownr = (*matItem).row_nr; + if(rownr == 0) { + matValue = (*matItem).value; + ib++; + matItem++; + } + else { + rownr = 0; + matValue = 0; + } + if(modifyOF1(lp, i, &matValue, ofscalar)) { + p += prow[rownr] * matValue; + d += drow[rownr] * matValue; + } + + /* Then loop over all regular rows */ + if(ib < ie) + for( ; ib < ie; ib++, matItem++) { + rownr = (*matItem).row_nr; + matValue = (*matItem).value; + p += prow[rownr] * matValue; + d += drow[rownr] * matValue; + } + + } + + my_roundzero(p, proundzero); + pmax = MAX(pmax, fabs((REAL) p)); + prow[i] = (REAL) p; + if((nzprow != NULL) && (p != 0) && + ((prange == XRESULT_FREE) || (p*my_chsign(lp->is_lower[i], -1) > 0))) { + (*nzprow)++; + nzprow[*nzprow] = i; + } + my_roundzero(d, droundzero); + dmax = MAX(dmax, fabs((REAL) d)); + drow[i] = (REAL) d; + if((nzdrow != NULL) && (d != 0) && + ((drange == XRESULT_FREE) || (d*my_chsign(lp->is_lower[i], -1) > 0))) { + (*nzdrow)++; + nzdrow[*nzdrow] = i; + } + } + + /* Check if we should do relative rounding */ +#ifdef Prod_xA_RoundRelative + if((proundzero > 0) && (nzprow != NULL)) { + ie = 0; + for(i = 1; i <= *nzprow; i++) { + rownr = nzprow[i]; + if(fabs(prow[rownr])/pmax < proundzero) + prow[rownr] = 0; + else if((prange == XRESULT_FREE) || (prow[rownr]*my_chsign(lp->is_lower[rownr], -1) > 0)) { + ie++; + nzprow[ie] = rownr; + } + } + *nzprow = ie; + } + if((droundzero > 0) && (nzdrow != NULL)) { + ie = 0; + for(i = 1; i <= *nzdrow; i++) { + rownr = nzdrow[i]; + if(fabs(drow[rownr])/dmax < droundzero) + drow[rownr] = 0; + else if((drange == XRESULT_FREE) || (drow[rownr]*my_chsign(lp->is_lower[rownr], -1) > 0)) { + ie++; + nzdrow[ie] = rownr; + } + } + *nzdrow = ie; + } +#endif + +#endif + +} + +STATIC void bsolve_xA2(lprec *lp, int row_nr1, REAL *vector1, REAL roundzero1, int *nzvector1, + int row_nr2, REAL *vector2, REAL roundzero2, int *nzvector2) +{ + REAL ofscalar = 1.0; + int rc_range = XRESULT_RC; + + /* Clear and initialize first vector */ + if(nzvector1 == NULL) + MEMCLEAR(vector1, lp->sum + 1); + else + MEMCLEAR(vector1, lp->rows + 1); + vector1[row_nr1] = 1; +/* lp->workrowsINT[0] = 1; + lp->workrowsINT[1] = row_nr1; */ + + if(vector2 == NULL) { + lp->bfp_btran_normal(lp, vector1, NULL); + prod_xA(lp, SCAN_USERVARS+ + SCAN_PARTIALBLOCK+ + USE_NONBASICVARS+OMIT_FIXED, + vector1, NULL, rc_range, roundzero1, ofscalar*0, + vector1, nzvector1); + } + else { + + /* Clear and initialize second vector */ + if(nzvector2 == NULL) + MEMCLEAR(vector2, lp->sum + 1); + else + MEMCLEAR(vector2, lp->rows + 1); + vector2[row_nr2] = 1; +/* lp->workrowsINT[2] = 1; + lp->workrowsINT[3] = row_nr2; */ + + /* A double BTRAN equation solver process is implemented "in-line" below in + order to save time and to implement different rounding for the two */ + lp->bfp_btran_double(lp, vector1, NULL, vector2, NULL); + + /* Multiply solution vectors with matrix values */ + prod_xA2(lp, vector1, rc_range, roundzero1, nzvector1, + vector2, XRESULT_FREE, roundzero2, nzvector2, ofscalar); + } +} + Index: src/tools/solver/lp_solve/lp_matrix.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_matrix.h diff -N src/tools/solver/lp_solve/lp_matrix.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_matrix.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,154 @@ +#ifndef HEADER_lp_matrix +#define HEADER_lp_matrix + +#include "lp_types.h" + + +/* Compiler option edevelopment features */ +/*#define DebugInv*/ /* Report array values at inversion */ + +/* Matrix column access macros to be able to easily change storage model */ +#define CAM_Record 0 +#define CAM_Vector 1 +#define MatrixColAccess CAM_Record + +#define COL_MAT_PTR(item) &(item) +#define COL_MAT_COL(item) (item).col_nr +#define COL_MAT_ROW(item) (item).row_nr +#define COL_MAT_VALUE(item) (item).value + + +/* Matrix row access macros to be able to easily change storage model */ +#define RAM_Index 0 +#define RAM_IndexAndCol 1 +#define RAM_Pointer 2 +#define RAM_FullCopy 3 +#define MatrixRowAccess RAM_Index + +#if MatrixRowAccess==RAM_Index +#define ROW_MAT_PTR(item) &(mat->col_mat[item]) +#define ROW_MAT_ITEM(col,idx,ptr) (idx) +#define ROW_MAT_COL(item) mat->col_mat[item].col_nr +#define ROW_MAT_ROW(item) mat->col_mat[item].row_nr +#define ROW_MAT_VALUE(item) mat->col_mat[item].value + +#elif MatrixRowAccess==RAM_IndexAndCol +#define ROW_MAT_PTR(item) &(mat->col_mat[item]) +#define ROW_MAT_ITEM(col,idx,ptr) (idx) +#define ROW_MAT_COL(item) mat->row_col[item] +#define ROW_MAT_ROW(item) mat->col_mat[item].row_nr +#define ROW_MAT_VALUE(item) mat->col_mat[item].value + +#elif MatrixRowAccess==RAM_Pointer +#define ROW_MAT_PTR(item) (item) +#define ROW_MAT_ITEM(col,idx,ptr) (ptr) +#define ROW_MAT_COL(item) (item)->col_nr +#define ROW_MAT_ROW(item) (item)->row_nr +#define ROW_MAT_VALUE(item ) (item)->value + +#else /* if MatrixRowAccess==RAM_FullCopy */ +#define ROW_MAT_PTR(item) &(item) +#define ROW_MAT_ITEM(col,idx,ptr) (*ptr) +#define ROW_MAT_COL(item) (item).col_nr +#define ROW_MAT_ROW(item) (item).row_nr +#define ROW_MAT_VALUE(item) (item).value + +#endif + + +/* Sparse matrix element (ordered columnwise) */ +typedef struct _MATitem +{ + int row_nr; + int col_nr; + REAL value; +} MATitem; + +typedef struct _MATrec +{ + /* Owner reference */ + lprec *lp; + + /* Active dimensions */ + int rows; + int columns; + REAL epsvalue; /* Zero element rejection threshold */ + + /* Allocated memory */ + int rows_alloc; + int columns_alloc; + int mat_alloc; /* The allocated size for matrix sized structures */ + + /* Sparse problem matrix storage */ + MATitem *col_mat; /* mat_alloc : The sparse data storage */ + int *col_end; /* columns_alloc+1 : col_end[i] is the index of the + first element after column i; column[i] is stored + in elements col_end[i-1] to col_end[i]-1 */ +#if MatrixRowAccess==RAM_Index + int *row_mat; /* mat_alloc : From index 0, row_mat contains the + row-ordered index of the elements of col_mat */ +#elif MatrixRowAccess==RAM_IndexAndCol + int *row_mat; /* mat_alloc : From index 0, row_mat contains the + row-ordered index of the elements of col_mat */ + int *row_col; /* mat_alloc : Column index */ +#elif MatrixRowAccess==RAM_Pointer + MATitem **row_mat; /* mat_alloc : From index 0, row_mat contains the + row-ordered pointers to the elements of col_mat */ +#else /* if MatrixRowAccess==RAM_FullCopy */ + MATitem *row_mat; /* mat_alloc : From index 0, row_mat contains the + row-ordered copy of the elements in col_mat */ +#endif + int *row_end; /* rows_alloc+1 : row_end[i] is the index of the + first element in row_mat after row i */ + MYBOOL row_end_valid; /* TRUE if row_end & row_mat are valid */ + MYBOOL is_roworder; /* FUTURE */ + +} MATrec; + + +#ifdef __cplusplus +__EXTERN_C { +#endif + +/* Sparse matrix routines */ +STATIC MATrec *mat_create(lprec *lp, int rows, int columns, REAL epsvalue); +STATIC void mat_free(MATrec **matrix); +STATIC MYBOOL inc_matrow_space(MATrec *mat, int deltarows); +STATIC int mat_rowcompact(MATrec *mat); +STATIC MYBOOL inc_matcol_space(MATrec *mat, int deltacols); +STATIC MYBOOL inc_mat_space(MATrec *mat, int mindelta); +STATIC int mat_shiftrows(MATrec *mat, int *bbase, int delta); +STATIC int mat_shiftcols(MATrec *mat, int base, int delta); +STATIC int mat_appendrow(MATrec *mat, int count, REAL *row, int *colno, REAL mult); +STATIC int mat_appendcol(MATrec *mat, int count, REAL *column, int *rowno, REAL mult); +STATIC MYBOOL mat_validate(MATrec *mat); +STATIC int mat_findelm(MATrec *mat, int row, int column); +STATIC int mat_findins(MATrec *mat, int row, int column, int *insertpos, MYBOOL validate); +STATIC void mat_multcol(MATrec *mat, int col_nr, REAL mult); +STATIC REAL mat_getitem(MATrec *mat, int row, int column); +STATIC int mat_nonzeros(MATrec *mat); +STATIC int mat_collength(MATrec *mat, int colnr); +STATIC int mat_rowlength(MATrec *mat, int rownr); +STATIC void mat_multrow(MATrec *mat, int row_nr, REAL mult); +STATIC MYBOOL mat_setvalue(MATrec *mat, int Row, int Column, REAL Value, MYBOOL doscale); +STATIC int mat_checkcounts(MATrec *mat, int *rownum, int *colnum, MYBOOL freeonexit); +STATIC MYBOOL mat_transpose(MATrec *mat, MYBOOL col1_row0); + +STATIC MYBOOL fimprove(lprec *lp, REAL *pcol, int *nzidx, REAL roundzero); +STATIC void ftran(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero); +STATIC MYBOOL bimprove(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero); +STATIC void btran(lprec *lp, REAL *rhsvector, int *nzidx, REAL roundzero); +STATIC int prod_Ax(lprec *lp, int varset, REAL *input, int *nzinput, int range, REAL roundzero, REAL multfactor, REAL *output, int *nzoutput); +STATIC int prod_xA(lprec *lp, int varset, REAL *input, int *nzinput, int range, REAL roundzero, REAL ofscalar, REAL *output, int *nzoutput); +STATIC void prod_xA2(lprec *lp, REAL *prow, int prange, REAL proundzero, int *pnzprow, + REAL *drow, int drange, REAL droundzero, int *dnzdrow, REAL ofscalar); +STATIC MYBOOL fsolve(lprec *lp, int varin, REAL *pcol, int *nzidx, REAL roundzero, REAL ofscalar, MYBOOL prepareupdate); +STATIC MYBOOL bsolve(lprec *lp, int row_nr, REAL *rhsvector, int *nzidx, REAL roundzero, REAL ofscalar); +STATIC void bsolve_xA2(lprec *lp, int row_nr1, REAL *vector1, REAL roundzero1, int *nzvector1, + int row_nr2, REAL *vector2, REAL roundzero2, int *nzvector2); + +#ifdef __cplusplus +} +#endif + +#endif /* HEADER_lp_matrix */ Index: src/tools/solver/lp_solve/lp_mipbb.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_mipbb.c diff -N src/tools/solver/lp_solve/lp_mipbb.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_mipbb.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1108 @@ + +/* + Mixed integer programming optimization drivers for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Michel Berkelaar (to lp_solve v3.2), + Kjell Eikland + Contact: + License terms: LGPL. + + Requires: string.h, float.h, lp_lib.h, lp_report.h, lp_simplex.h + + Release notes: + v5.0.0 31 January 2004 New unit isolating B&B routines. + v5.0.1 01 February 2004 Complete rewrite into non-recursive version. + v5.0.2 05 April 2004 Expanded pseudocosting with options for MIP fraction + counts and "cost/benefit" ratio (KE special!). + Added GUB functionality based on SOS structures. + v5.0.3 1 May 2004 Changed routine names to be more intuitive. + v5.0.4 15 May 2004 Added functinality to pack bounds in order to + conserve memory in B&B-processing large MIP models. + + ---------------------------------------------------------------------------------- +*/ + +#include +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_report.h" +#include "lp_simplex.h" +#include "lp_mipbb.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* Allocation routine for the BB record structure */ +STATIC BBrec *create_BB(lprec *lp, BBrec *parentBB, REAL *UB_active, REAL *LB_active) +{ + BBrec *newBB; + + newBB = (BBrec *) calloc(1, sizeof(*newBB)); + if((newBB != NULL) && + allocREAL(lp, &newBB->upbo, lp->sum + 1, FALSE) && + allocREAL(lp, &newBB->lowbo, lp->sum + 1, FALSE)) { + + if(parentBB == NULL) { + newBB->UB_base = lp->orig_upbo; + newBB->LB_base = lp->orig_lowbo; + } + else { + newBB->UB_base = UB_active; + newBB->LB_base = LB_active; + } + MEMCOPY(newBB->upbo, newBB->UB_base, lp->sum + 1); + MEMCOPY(newBB->lowbo, newBB->LB_base, lp->sum + 1); + + newBB->lp = lp; + + /* Set parent by default, but not child and then compress backwards (if possible) */ + newBB->parent = parentBB; + if(compress_BB(newBB)) { + if(parentBB->saved_upbo != NULL) + lp->bb_compressions++; + if(parentBB->saved_lowbo != NULL) + lp->bb_compressions++; + } + + } + return( newBB ); +} + + +/* Memory conservation by compressing the parent's bounds */ +STATIC MYBOOL compress_BB(BBrec *BB) +{ + BBrec *parentBB; + + if(BB == NULL) + return( FALSE ); + parentBB = BB->parent; + if((parentBB == NULL) || (parentBB == parentBB->lp->rootbounds) || + (MAX_BB_FULLBOUNDSAVE <= 0) || (MIP_count(parentBB->lp) < MAX_BB_FULLBOUNDSAVE)) + return( FALSE ); + + if(BB->LB_base != parentBB->lowbo) { + parentBB->saved_lowbo = createPackedVector(parentBB->lp->sum, parentBB->lowbo, NULL); + if(parentBB->saved_lowbo != NULL) + allocFREE(parentBB->lp, (void **) &(parentBB->lowbo)); + } + + if(BB->UB_base != parentBB->upbo) { + parentBB->saved_upbo = createPackedVector(parentBB->lp->sum, parentBB->upbo, NULL); + if(parentBB->saved_upbo != NULL) + allocFREE(parentBB->lp, (void **) &(parentBB->upbo)); + } + + return( TRUE ); +} + +STATIC MYBOOL uncompress_BB(BBrec *BB) +{ + if(BB == NULL) + return( FALSE ); + + if(BB->saved_lowbo != NULL) { + allocREAL(BB->lp, &BB->lowbo, BB->lp->sum + 1, FALSE); + unpackPackedVector(BB->saved_lowbo, &(BB->lowbo)); + freePackedVector(&BB->saved_lowbo); + } + if(BB->saved_upbo != NULL) { + allocREAL(BB->lp, &BB->upbo, BB->lp->sum + 1, FALSE); + unpackPackedVector(BB->saved_upbo, &(BB->upbo)); + freePackedVector(&BB->saved_upbo); + } + return( TRUE ); +} + + +/* Pushing and popping routines for the B&B structure */ + +STATIC BBrec *push_BB(lprec *lp, BBrec *parentBB, int varno, int vartype, + REAL *UB_active, REAL *LB_active, int varcus) +/* Push ingoing bounds and B&B data onto the stack */ +{ + BBrec *newBB; + + /* Do initialization and updates */ + if(parentBB == NULL) + parentBB = lp->bb_bounds; + newBB = create_BB(lp, parentBB, UB_active, LB_active); + if(newBB != NULL) { + + newBB->varno = varno; + newBB->vartype = vartype; + newBB->lastvarcus = varcus; + + /* Handle case where we are pushing at the end */ + if(parentBB == lp->bb_bounds) + lp->bb_bounds = newBB; + /* Handle case where we are pushing in the middle */ + else + newBB->child = parentBB->child; + if(parentBB != NULL) + parentBB->child = newBB; + + lp->bb_level++; + if(lp->bb_level > lp->bb_maxlevel) + lp->bb_maxlevel = lp->bb_level; + + if(!initbranches_BB(newBB)) + newBB = pop_BB(newBB); + else if(MIP_count(lp) > 0) { + if((lp->bb_level <= 1) && (lp->bb_varactive == NULL) && + !allocINT(lp, &lp->bb_varactive, lp->columns+1, TRUE)) + newBB = pop_BB(newBB); + if(varno > 0) { + lp->bb_varactive[varno-lp->rows]++; +/* mergeshadow_BB(newBB); */ /* Preparation for dynamic B&B */ + } + } + } + return( newBB ); +} + +STATIC void free_BB(BBrec **BB) +{ + if((BB == NULL) || (*BB == NULL)) + return; + + FREE((*BB)->upbo); + FREE((*BB)->lowbo); + + uncompress_BB((*BB)->parent); + + FREE(*BB); +} + +STATIC BBrec *pop_BB(BBrec *BB) +/* Pop / free the previously "pushed" / saved bounds */ +{ + int k; + BBrec *parentBB; + lprec *lp = BB->lp; + + if(BB == NULL) + return( BB ); + + /* Handle case where we are popping the end of the chain */ + parentBB = BB->parent; + if(BB == lp->bb_bounds) { + lp->bb_bounds = parentBB; + if(parentBB != NULL) + parentBB->child = NULL; + } + /* Handle case where we are popping inside or at the beginning of the chain */ + else { + if(parentBB != NULL) + parentBB->child = BB->child; + if(BB->child != NULL) + BB->child->parent = parentBB; + } + + /* Unwind other variables */ + lp->bb_level--; + k = BB->varno - lp->rows; + if(lp->bb_level == 0) { + if(lp->bb_varactive != NULL) { + FREE(lp->bb_varactive); + } + if(lp->int_count+lp->sc_count > 0) + free_PseudoCost(lp); + pop_basis(lp, FALSE); + lp->rootbounds = NULL; + } + else + lp->bb_varactive[k]--; + + /* Undo SOS/GUB markers */ + if(BB->isSOS && (BB->vartype != BB_INT)) + SOS_unmark(lp->SOS, 0, k); + else if(BB->isGUB) + SOS_unmark(lp->GUB, 0, k); + + /* Undo the SC marker */ + if(BB->sc_canset) + lp->var_is_sc[k] *= -1; + + /* Pop the associated basis */ +#if 1 + /* Original version that does not restore previous basis */ + pop_basis(lp, FALSE); +#else + /* Experimental version that restores previous basis */ + pop_basis(lp, BB->isSOS); +#endif + + /* Finally free the B&B object */ + free_BB(&BB); + + /* Return the parent BB */ + return( parentBB ); +} + +STATIC MYBOOL initbranches_BB(BBrec *BB) +{ + REAL new_bound, temp; + int k; + lprec *lp = BB->lp; + + /* Create and initialize local bounds and basis */ + BB->nodestatus = NOTRUN; + BB->noderesult = lp->infinite; + push_basis(lp, NULL, NULL, NULL); + + /* Set default number of branches at the current B&B branch */ + if(BB->vartype == BB_REAL) + BB->nodesleft = 1; + + else { + /* The default is a binary up-low branching */ + BB->nodesleft = 2; + + /* Initialize the MIP status code pair and set reference values */ + k = BB->varno - lp->rows; + BB->lastsolution = lp->solution[BB->varno]; + + /* Determine if we must process in the B&B SOS mode */ + BB->isSOS = (MYBOOL) ((BB->vartype == BB_SOS) || SOS_is_member(lp->SOS, 0, k)); +#ifdef Paranoia + if((BB->vartype == BB_SOS) && !SOS_is_member(lp->SOS, 0, k)) + report(lp, SEVERE, "initbranches_BB: Inconsistent identification of SOS variable %s (%d)\n", + get_col_name(lp, k), k); +#endif + + /* Check if we have a GUB-member variable that needs a triple-branch */ + BB->isGUB = (MYBOOL) ((BB->vartype == BB_INT) && SOS_can_activate(lp->GUB, 0, k)); + if(BB->isGUB) + BB->nodesleft++; + + + /* Set local pruning info, automatic, or user-defined strategy */ + if(BB->vartype == BB_SOS) { + if(!SOS_can_activate(lp->SOS, 0, k)) { + BB->nodesleft--; + BB->isfloor = TRUE; + } + else + BB->isfloor = (MYBOOL) (BB->lastsolution == 0); + } + else if(get_var_branch(lp, k) == BRANCH_AUTOMATIC) { + new_bound = modf(BB->lastsolution/get_PseudoRange(lp, k, BB->vartype), &temp); + if(_isnan(new_bound)) + new_bound = 0; + BB->isfloor = (MYBOOL) (new_bound <= 0.5); + if(is_bb_mode(lp, NODE_GREEDYMODE)) { + /* Set direction by OF value; note that a zero-value in + the OF gives priority to floor_first = TRUE */ + if(is_bb_mode(lp, NODE_PSEUDOCOSTMODE)) + BB->sc_bound = get_PseudoCost(lp, k, BB->vartype, BB->lastsolution); + else + BB->sc_bound = mat_getitem(lp->matA, 0, k); + new_bound -= 0.5; + BB->sc_bound *= new_bound; + BB->isfloor = (MYBOOL) (BB->sc_bound > 0); + } + /* Check for reversal */ + if(is_bb_mode(lp, NODE_BRANCHREVERSEMODE)) + BB->isfloor = !BB->isfloor; + } + else + BB->isfloor = (MYBOOL) (get_var_branch(lp, k) == BRANCH_FLOOR); + + /* SC logic: If the current SC variable value is in the [0..NZLOBOUND> range, then + + UP: Set lower bound to NZLOBOUND, upper bound is the original + LO: Fix the variable by setting upper/lower bound to zero + + ... indicate that the variable is B&B-active by reversing sign of var_is_sc[]. */ + new_bound = fabs(lp->var_is_sc[k]); + BB->sc_bound = new_bound; + BB->sc_canset = (MYBOOL) (new_bound != 0); + + /* Must make sure that we handle fractional lower bounds properly; + also to ensure that we do a full binary tree search */ + new_bound = unscaled_value(lp, new_bound, BB->varno); + if(is_int(lp, k) && ((new_bound > 0) && + (BB->lastsolution > floor(new_bound)))) { + if(BB->lastsolution < ceil(new_bound)) + BB->lastsolution += 1; + BB->lowbo[BB->varno] = scaled_floor(lp, BB->varno, BB->lastsolution, 1); + } + } + + /* Now initialize the brances and set to first */ + return( fillbranches_BB(BB) ); + +} + +STATIC MYBOOL fillbranches_BB(BBrec *BB) +{ + int K, k; + REAL ult_upbo, ult_lowbo; + REAL new_bound, SC_bound, intmargin = BB->lp->epsprimal; + lprec *lp = BB->lp; + MYBOOL OKstatus = FALSE; + + if(lp->bb_break || userabort(lp, MSG_MILPSTRATEGY)) + return( OKstatus ); + + K = BB->varno; + if(K > 0) { + + /* Shortcut variables */ + k = BB->varno - lp->rows; + ult_upbo = lp->orig_upbo[K]; + ult_lowbo = lp->orig_lowbo[K]; + SC_bound = unscaled_value(lp, BB->sc_bound, K); + + /* First, establish the upper bound to be applied (when isfloor == TRUE) + --------------------------------------------------------------------- */ +/*SetUB:*/ + BB->UPbound = lp->infinite; + + /* Handle SC-variables for the [0-LoBound> range */ + if((SC_bound > 0) && (BB->lastsolution < SC_bound)) { + new_bound = 0; + } + /* Handle pure integers (non-SOS, non-SC) */ + else if(BB->vartype == BB_INT) { + if((floor(BB->lastsolution) < /* Skip cases where the lower bound becomes violated */ + unscaled_value(lp, MAX(ult_lowbo, fabs(lp->var_is_sc[k])), K)-intmargin)) { + BB->nodesleft--; + goto SetLB; + } + new_bound = scaled_floor(lp, K, BB->lastsolution, 1); + } + else if(BB->isSOS) { /* Handle all SOS variants */ + new_bound = ult_lowbo; + if(is_int(lp, k)) + new_bound = scaled_ceil(lp, K, unscaled_value(lp, new_bound, K), -1); + } + else /* Handle all other variable incarnations */ + new_bound = BB->sc_bound; + + /* Check if the new bound might conflict and possibly make adjustments */ + if(new_bound < BB->LB_base[K]) { +#ifdef Paranoia + debug_print(lp, + "New upper bound value %g conflicts with old lower bound %g\n", + new_bound, BB->LB_base[K]); +#endif + BB->nodesleft--; + goto SetLB; + } +#ifdef Paranoia + /* Do additional consistency checking */ + else if(!check_if_less(lp, new_bound, BB->UB_base[K], K)) { + BB->nodesleft--; + goto SetLB; + } +#endif + /* Bound (at least near) feasible */ + else { + /* Makes a difference with models like QUEEN + (note consistent use of epsint for scaled integer variables) */ + if(fabs(new_bound - BB->LB_base[K]) < intmargin*SCALEDINTFIXRANGE) + new_bound = BB->LB_base[K]; + } + + BB->UPbound = new_bound; + + + /* Next, establish the lower bound to be applied (when isfloor == FALSE) + --------------------------------------------------------------------- */ +SetLB: + BB->LObound = -lp->infinite; + + /* Handle SC-variables for the [0-LoBound> range */ + if((SC_bound > 0) && (BB->lastsolution < SC_bound)) { + if(is_int(lp, k)) + new_bound = scaled_ceil(lp, K, SC_bound, 1); + else + new_bound = BB->sc_bound; + } + /* Handle pure integers (non-SOS, non-SC, but Ok for GUB!) */ + else if((BB->vartype == BB_INT)) { + if(((ceil(BB->lastsolution) == BB->lastsolution)) || /* Skip branch 0 if the current solution is integer */ + (ceil(BB->lastsolution) > /* Skip cases where the upper bound becomes violated */ + unscaled_value(lp, ult_upbo, K)+intmargin) || + (BB->isSOS && (BB->lastsolution == 0))) { /* Don't branch 0 since this is handled in SOS logic */ + BB->nodesleft--; + goto Finish; + } + new_bound = scaled_ceil(lp, K, BB->lastsolution, 1); + } + else if(BB->isSOS) { /* Handle all SOS variants */ + if(SOS_is_member_of_type(lp->SOS, k, SOS3)) + new_bound = scaled_floor(lp, K, 1, 1); + else { + new_bound = ult_lowbo; + if(is_int(lp, k)) + new_bound = scaled_floor(lp, K, unscaled_value(lp, new_bound, K), 1); + } + } + else /* Handle all other variable incarnations */ + new_bound = BB->sc_bound; + + /* Check if the new bound might conflict and possibly make adjustments */ + if(new_bound > BB->UB_base[K]) { +#ifdef Paranoia + debug_print(lp, + "New lower bound value %g conflicts with old upper bound %g\n", + new_bound, BB->UB_base[K]); +#endif + BB->nodesleft--; + goto Finish; + } +#ifdef Paranoia + /* Do additional consistency checking */ + else if(!check_if_less(lp, BB->LB_base[K], new_bound, K)) { + BB->nodesleft--; + goto Finish; + } +#endif + /* Bound (at least near-)feasible */ + else { + /* Makes a difference with models like QUEEN + (note consistent use of lp->epsprimal for scaled integer variables) */ + if(fabs(BB->UB_base[K]-new_bound) < intmargin*SCALEDINTFIXRANGE) + new_bound = BB->UB_base[K]; + } + + BB->LObound = new_bound; + + /* Prepare for the first branch by making sure we are pointing correctly */ +Finish: + if(BB->nodesleft > 0) { + /* Do adjustments */ + if((BB->vartype != BB_SOS) && (fabs(BB->LObound-BB->UPbound) < intmargin)) { + BB->nodesleft--; + if(fabs(BB->LB_base[K]-BB->LObound) < intmargin) + BB->isfloor = FALSE; + else if(fabs(BB->UB_base[K]-BB->UPbound) < intmargin) + BB->isfloor = TRUE; + else + report(BB->lp, IMPORTANT, "fillbranches_BB: Inconsistent equal-valued bounds for %s\n", + get_col_name(BB->lp, k)); + } + if((BB->nodesleft == 1) && + ((BB->isfloor && (BB->UPbound >= lp->infinite)) || + (!BB->isfloor && (BB->LObound <= -lp->infinite)))) + BB->isfloor = !BB->isfloor; + /* Header initialization */ + BB->isfloor = !BB->isfloor; + while(!OKstatus && (BB->nodesleft > 0)) + OKstatus = nextbranch_BB( BB ); + } + + /* Set an SC variable active, if necessary */ + if(BB->sc_canset) + lp->var_is_sc[k] *= -1; + } + else { + BB->nodesleft--; + OKstatus = TRUE; + } + + return( OKstatus ); +} + +STATIC MYBOOL nextbranch_BB(BBrec *BB) +{ + int k; + lprec *lp = BB->lp; + MYBOOL OKstatus = FALSE; + + if(lp->bb_break || userabort(lp, MSG_MILPSTRATEGY)) { + /* Handle the special case of B&B restart; + (typically used with the restart after pseudocost initialization) */ + if((lp->bb_level == 1) && (lp->bb_break == AUTOMATIC)) { + lp->bb_break = FALSE; + OKstatus = TRUE; + } + return( OKstatus ); + } + + if(BB->nodesleft > 0) { + + /* Step and update remaining branch count */ + k = BB->varno - lp->rows; + BB->isfloor = !BB->isfloor; + BB->nodesleft--; + + /* Special SOS handling: + 1) Undo and set new marker for k, + 2) In case that previous branch was ceiling restore upper bounds of the + non-k variables outside of the SOS window set to 0 */ + if(BB->isSOS && (BB->vartype != BB_INT)) { + + /* First undo previous marker */ + if((BB->nodessolved > 0) || ((BB->nodessolved == 0) && (BB->nodesleft == 0))) { + if(BB->isfloor) { + if((BB->nodesleft == 0) && (lp->orig_lowbo[BB->varno] != 0)) + return( OKstatus ); + restore_bounds(lp, TRUE, FALSE); + } + SOS_unmark(lp->SOS, 0, k); + } + + /* Set new SOS marker */ + if(BB->isfloor) + SOS_set_marked(lp->SOS, 0, k, (MYBOOL) (BB->UPbound != 0)); + else { + SOS_set_marked(lp->SOS, 0, k, TRUE); + if(SOS_fix_unmarked(lp->SOS, k, 0, BB->upbo, 0, TRUE, NULL) < 0) + return( OKstatus ); + } + } + + /* Special GUB handling (three branches): + 1) Undo and set new marker for k, + 2) Restore upper bounds of the left/right/all non-k variables + set to 0 in the previous branch + 3) Set new upper bounds for the non-k variables (k is set later) */ + else if(BB->isGUB) { + + /* First restore entering upper bounds and undo previous marker */ + if(BB->nodessolved > 0) { + restore_bounds(lp, TRUE, BB->isfloor); + SOS_unmark(lp->GUB, 0, k); + } + + /* Make sure we take floor bound twice */ + if((BB->nodesleft == 0) && !BB->isfloor) + BB->isfloor = !BB->isfloor; + + /* Handle two floor instances; + (selected variable and left/right halves of non-selected variables at 0) */ + SOS_set_marked(lp->GUB, 0, k, (MYBOOL) !BB->isfloor); + if(BB->isfloor) { + if(SOS_fix_GUB(lp->GUB, k, 0, BB->upbo, (MYBOOL) (BB->nodesleft > 0)) < 0) + return( OKstatus ); + } + /* Handle one ceil instance; + (selected variable at 1, all other at 0) */ + else { + if(SOS_fix_unmarked(lp->GUB, k, 0, BB->upbo, 0, TRUE, NULL) < 0) + return( OKstatus ); + } + } + + OKstatus = TRUE; + + } + /* Initialize simplex status variables */ + if(OKstatus) { + lp->bb_totalnodes++; + BB->nodestatus = NOTRUN; + BB->noderesult = lp->infinite; + } + return( OKstatus ); +} + +STATIC int solve_LP(lprec *lp, BBrec *BB, REAL *upbo, REAL *lowbo) +{ + int tilted, restored, status; + REAL testOF; + BBrec *perturbed = NULL; + + if(lp->bb_break) + return(PROCBREAK); + +#ifdef Paranoia + debug_print(lp, "solve_LP: Starting solve for iteration %d, B&B node level %d.\n", + lp->total_iter, lp->bb_totalnodes); + if(lp->bb_trace && + !validate_bounds(lp, upbo, lowbo)) + report(lp, SEVERE, "solve_LP: Inconsistent bounds at iteration %d, B&B node level %d.\n", + lp->total_iter, lp->bb_totalnodes); +#endif + + /* Copy user-specified entering bounds into lp_solve working bounds; + note that lp->bb_action must have been set before entering this routine */ + impose_bounds(lp, upbo, lowbo); + + /* Restore previously pushed / saved basis for this level if we are in + the B&B mode and it is not the first call of the binary tree */ + if((BB->nodessolved > 1) && is_bb_action(lp, ACTION_REINVERT)) + restore_basis(lp); + + /* Solve and possibly handle degeneracy cases via bound relaxations */ + status = RUNNING; + tilted = 0; + restored = 0; + + while(status == RUNNING) { + + /* Copy user-specified entering bounds into lp_solve working bounds and run */ + status = spx_run(lp); + lp->bb_status = status; + + if(tilted < 0) + break; + + else if((status == OPTIMAL) && (tilted > 0)) { + /* Restore original pre-perturbed problem bounds, and solve again using the basis + found for the perturbed problem; also make sure we rebase and reinvert. */ + free_BB(&perturbed); + if(lp->spx_trace) + report(lp, DETAILED, "solve_LP: Restoring relaxed bounds.\n"); + impose_bounds(lp, upbo, lowbo); + lp->bb_action = ACTION_ACTIVE | ACTION_REINVERT | ACTION_REBASE | ACTION_RECOMPUTE; + BB->UBzerobased = FALSE; + if(lp->bb_totalnodes == 0) + lp->real_solution = lp->infinite; + status = RUNNING; + tilted = 0; + restored++; + } + +#ifdef EnableAntiDegen + else if(((lp->bb_level <= 1) || is_anti_degen(lp, ANTIDEGEN_DURINGBB)) && + (((status == LOSTFEAS) && is_anti_degen(lp, ANTIDEGEN_LOSTFEAS)) || + ((status == INFEASIBLE) && is_anti_degen(lp, ANTIDEGEN_INFEASIBLE)) || + ((status == NUMFAILURE) && is_anti_degen(lp, ANTIDEGEN_NUMFAILURE)) || + ((status == DEGENERATE) && is_anti_degen(lp, ANTIDEGEN_STALLING)))) { + /* Allow up to .. consecutive relaxations for non-B&B phases */ + if((tilted <= DEF_MAXRELAX) && (restored <= DEF_MAXRELAX)) { + + /* Create working copy of ingoing bounds if this is the first perturbation */ + if(tilted == 0) + perturbed = create_BB(lp, BB, upbo, lowbo); + + /* Perturb/shift variable bounds; also make sure we rebase and reinvert */ + perturb_bounds(lp, perturbed, TRUE, (MYBOOL) (tilted > 0)); + impose_bounds(lp, perturbed->upbo, perturbed->lowbo); + lp->bb_action = ACTION_ACTIVE | ACTION_REINVERT | ACTION_REBASE | ACTION_RECOMPUTE; + BB->UBzerobased = FALSE; + status = RUNNING; + tilted++; + if(lp->spx_trace) + report(lp, DETAILED, "solve_LP: Starting bound relaxation number %d (reason=%d)\n", + tilted, status); + } + else if(lp->spx_trace) { + report(lp, DETAILED, "solve_LP: Relaxation limit exceeded in resolving infeasibility\n"); + free_BB(&perturbed); + } + } +#endif + +#ifndef FinalOptimalErrorLimitation + else + /* Reduce likelihood of numeric errors in slack variables identified by check_solution; + assume acceptable error accumulation within 1/4th of pivot limit per refactorization */ + if((lp->bb_totalnodes == 0) && (status == OPTIMAL) && + (lp->bfp_pivotcount(lp) > MIN(10, lp->bfp_pivotmax(lp)/4))) + invert(lp, INITSOL_USEZERO); +#endif + + } + + /* Handle the different simplex outcomes */ + if(status != OPTIMAL) { + lp->bb_parentOF = lp->infinite; + if((status == USERABORT) || (status == TIMEOUT)) { + /* Construct the last feasible solution, if available */ + if((lp->solutioncount == 0) && + ((lp->simplex_mode & (SIMPLEX_Phase2_PRIMAL | SIMPLEX_Phase2_DUAL)) > 0)) { + lp->solutioncount++; + construct_solution(lp, NULL); + transfer_solution(lp, TRUE); + } + /* Return messages */ + report(lp, NORMAL, "\nlp_solve optimization was stopped %s.\n", + ((status == USERABORT) ? "by the user" : "due to time-out")); + } + else if(BB->varno == 0) + report(lp, NORMAL, "The model %s\n", + (status == UNBOUNDED) ? "is UNBOUNDED" : + ((status == INFEASIBLE) ? "is INFEASIBLE" : "FAILED")); + } + + else { /* ... there is a good solution */ + construct_solution(lp, NULL); +#ifdef Paranoia + debug_print(lp, "solve_LP: A valid solution was found\n"); + debug_print_solution(lp); +#endif + if((lp->bb_level <= 1) && (restored > 0)) + report(lp, NORMAL, "%s numerics encountered; validate accuracy\n", + (restored == 1) ? "Difficult" : "Severe"); + /* Hande case where a user bound on the OF was found to + have been set tool aggressively, giving an infeasible model */ + if(lp->spx_status != OPTIMAL) + status = lp->spx_status; + + else if((lp->bb_totalnodes == 0) && (MIP_count(lp) > 0)) { + if(lp->lag_status != RUNNING) { + report(lp, NORMAL, "\nRelaxed solution " MPSVALUEMASK " found as B&B basis.\n", + lp->solution[0]); + report(lp, NORMAL, " \n"); + } + if((lp->usermessage != NULL) && (lp->msgmask & MSG_LPOPTIMAL)) + lp->usermessage(lp, lp->msghandle, MSG_LPOPTIMAL); + set_varpriority(lp); + } + + /* Check if we have a numeric problem (an earlier version of this code used the + absolute difference, but it is not robust for large valued OFs) */ + testOF = my_chsign(is_maxim(lp), my_reldiff(lp->solution[0], lp->real_solution)); + if(testOF < -lp->epsprimal) { + report(lp, DETAILED, "solve_LP: A MIP subproblem returned a value better than the base\n"); + status = INFEASIBLE; + lp->spx_status = status; + lp->doInvert = TRUE; + lp->doRebase = TRUE; + lp->doRecompute = TRUE; + } + else if(testOF < 0) /* Avoid problems later (could undo integer roundings, but usually Ok) */ + lp->solution[0] = lp->real_solution; + + } + + /* status can have the following values: + OPTIMAL, SUBOPTIMAL, TIMEOUT, USERABORT, PROCFAIL, UNBOUNDED and INFEASIBLE. */ + + return( status ); +} /* solve_LP */ + +STATIC BBrec *findself_BB(BBrec *BB) +{ + int varno = BB->varno, vartype = BB->vartype; + + BB = BB->parent; + while((BB != NULL) && (BB->vartype != vartype) && (BB->varno != varno)) + BB = BB->parent; + return( BB ); +} + +STATIC MYBOOL mergeshadow_BB(BBrec *BB) +{ + BBrec *childBB, *predBB = findself_BB(BB); + + if(predBB == NULL) + return( FALSE ); + + else { + /* First copy dominant bound from earlier self BB instance */ + if(predBB->UPbound < BB->UPbound) + BB->UPbound = predBB->UPbound; + if(predBB->LObound > BB->LObound) + BB->LObound = predBB->LObound; + /* Link relevant child bounds to parent bounds */ + if(predBB->parent != NULL) { + childBB = predBB->child; + while(childBB != BB) { + if(childBB->UB_base == BB->upbo) + childBB->UB_base = predBB->parent->UB_base; + if(childBB->LB_base == BB->lowbo) + childBB->LB_base = predBB->parent->LB_base; + childBB = childBB->child; + } + } + /* Then simply remove the earlier self instance */ + pop_BB(predBB); + + return( TRUE ); + } +} + +STATIC MYBOOL findnode_BB(BBrec *BB, int *varno, int *vartype, int *varcus) +{ + int firstnint, lastnint, countnint; + REAL tmpreal, varsol; + MYBOOL is_better = FALSE, is_equal = FALSE; + lprec *lp = BB->lp; + + /* Initialize result and return variables */ + *varno = 0; + *vartype = BB_REAL; + *varcus = 0; + countnint = 0; + BB->nodestatus = lp->spx_status; + BB->noderesult = lp->solution[0]; + + /* If this solution is worse than the best so far, this branch dies. + If we can only have integer OF values, and we only need the first solution + then the OF must be at least (unscaled) 1 better than the best so far */ + if((lp->bb_limitlevel != 0) && (MIP_count(lp) > 0)) { + + /* Check that we don't have a limit on the recursion level; two versions supported: + 1) Absolute B&B level (bb_limitlevel > 0), and + 2) B&B level relative to the "B&B order" (bb_limitlevel < 0). */ + lastnint = lp->sos_vars + lp->sc_count; + if((lp->bb_limitlevel > 0) && (lp->bb_level > lp->bb_limitlevel+lastnint)) + return( FALSE ); + else if((lp->bb_limitlevel < 0) && + (lp->bb_level > 2*(lp->int_count+lastnint)*abs(lp->bb_limitlevel))) { + if(lp->bb_limitlevel == DEF_BB_LIMITLEVEL) + report(lp, IMPORTANT, "findnode_BB: Default B&B limit reached at %d; optionally change strategy or limit.\n\n", + lp->bb_level); + return( FALSE ); + } + + /* First initialize or update pseudo-costs from previous optimal solution */ + if(BB->varno == 0) { + varsol = lp->infinite; + if((lp->int_count+lp->sc_count > 0) && (lp->bb_PseudoCost == NULL)) + lp->bb_PseudoCost = init_PseudoCost(lp); + } + else { + varsol = lp->solution[BB->varno]; + if( ((lp->int_count > 0) && (BB->vartype == BB_INT)) || + ((lp->sc_count > 0) && (BB->vartype == BB_SC) && !is_int(lp, BB->varno-lp->rows)) ) + update_PseudoCost(lp, BB->varno-lp->rows, BB->vartype, BB->isfloor, varsol); + } + + /* Make sure we don't have numeric problems (typically due to integer scaling) */ + tmpreal = lp->mip_absgap; + if((lp->bb_totalnodes == 0) && + (my_chsign(is_maxim(lp), lp->solution[0]-lp->real_solution) < -tmpreal)) { + if(lp->bb_trace) + report(lp, IMPORTANT, "findnode_BB: Simplex failure due to loss of numeric accuracy\n"); + lp->spx_status = NUMFAILURE; + return( FALSE ); + } + + /* Abandon this branch if the solution is worse than the previous best MIP solution */ + tmpreal = MAX(tmpreal, lp->bb_deltaOF - lp->epsprimal); + if((BB->lp->solutioncount > 0) && + ((my_chsign(is_maxim(lp), my_reldiff(lp->solution[0],lp->best_solution[0])) > -lp->mip_relgap) || + (my_chsign(is_maxim(lp), lp->solution[0]-lp->best_solution[0]) > -tmpreal))) { + return( FALSE ); + } + + /* Check if solution contains enough satisfied INT, SC and SOS variables */ + firstnint = 1; + lastnint = lp->columns; + + /* Collect violated SC variables (since they can also be real-valued); the + approach is to get them out of the way, since a 0-value is assumed to be "cheap" */ + if(lp->sc_count > 0) { + *varno = find_sc_bbvar(lp, &countnint); + if(*varno > 0) + *vartype = BB_SC; + } + + /* Look among SOS variables if no SC candidate was found */ + if((SOS_count(lp) > 0) && (*varno == 0)) { + *varno = find_sos_bbvar(lp, &countnint, FALSE); + if(*varno < 0) + *varno = 0; + else if(*varno > 0) + *vartype = BB_SOS; + } + + /* Then collect INTS that are not integer valued, and verify bounds */ + if((lp->int_count > 0) && (*varno == 0)) { + *varno = find_int_bbvar(lp, &countnint, BB->lowbo, BB->upbo, &firstnint, &lastnint); + if(*varno > 0) + *vartype = BB_INT; + } + + /* Check if the current MIP solution is optimal; equal or better */ + if(*varno == 0) { + is_better = (MYBOOL) (my_chsign(is_maxim(lp), lp->solution[0]-lp->best_solution[0]) < -tmpreal); + is_equal = !is_better && (MYBOOL) + (fabs(my_reldiff(lp->solution[0],lp->best_solution[0])) <= lp->mip_relgap); + + if(is_equal) { + if((lp->solutionlimit <= 0) || (lp->solutioncount < lp->solutionlimit)) { + lp->solutioncount++; + lp->bb_solutionlevel = MIN(lp->bb_solutionlevel, lp->bb_level); + if((lp->usermessage != NULL) && (lp->msgmask & MSG_MILPEQUAL)) + lp->usermessage(lp, lp->msghandle, MSG_MILPEQUAL); + } + } + + /* Current solution is better */ + else if(is_better) { + + /* Update grand total solution count and check if we should go from + depth-first to best-first variable selection mode */ + if(lp->bb_varactive != NULL) { + lp->bb_varactive[0]++; + if((lp->bb_varactive[0] == 1) && + is_bb_mode(lp, NODE_DEPTHFIRSTMODE) && is_bb_mode(lp, NODE_DYNAMICMODE)) + lp->bb_rule &= !NODE_DEPTHFIRSTMODE; + } + + lp->bb_status = FEASFOUND; + lp->solutioncount = 1; + if(lp->bb_trace || + ((lp->verbose>=NORMAL) && (lp->print_sol == FALSE) && (lp->lag_status != RUNNING))) { + report(lp, IMPORTANT, + "%s solution " MPSVALUEMASK " found at iteration %7d, %6d nodes explored (gap %.1f%%)\n", + (my_chsign(is_maxim(lp), lp->best_solution[0]) >= lp->infinite) ? "Feasible" : "Improved", + lp->solution[0], lp->total_iter, lp->bb_totalnodes, 100*fabs(my_reldiff(lp->solution[0], lp->bb_limitOF))); + } + if((lp->usermessage != NULL) && (MIP_count(lp) > 0)) { + if((lp->msgmask & MSG_MILPFEASIBLE) && + (my_chsign(is_maxim(lp), lp->best_solution[0]) >= lp->infinite)) + lp->usermessage(lp, lp->msghandle, MSG_MILPFEASIBLE); + else if((lp->msgmask & MSG_MILPBETTER) && (lp->msgmask & MSG_MILPBETTER)) + lp->usermessage(lp, lp->msghandle, MSG_MILPBETTER); + } + + lp->bb_solutionlevel = lp->bb_level; + if(lp->break_at_first || + (!(fabs(lp->break_at_value) == lp->infinite) && + (my_chsign(is_maxim(lp), /* lp->best_solution[0] */ lp->solution[0]-lp->break_at_value) <= 0))) /* Was > until v5.0.6.0 */ + lp->bb_break = TRUE; + } + } + } + else { + is_better = TRUE; + lp->solutioncount = 1; + } + + /* Transfer the successful solution vector */ + if(is_better || is_equal) { +#ifdef Paranoia + if((lp->bb_level > 0) && + (check_solution(lp, lp->columns, lp->solution, + lp->orig_upbo, lp->orig_lowbo, DEF_EPSSOLUTION) != OPTIMAL)) { + lp->solutioncount = 0; + lp->spx_status = NUMFAILURE; + lp->bb_status = lp->spx_status; + lp->bb_break = TRUE; + return( FALSE ); + } +#endif + transfer_solution(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)); + if((MIP_count(lp) > 0) && (lp->bb_totalnodes > 0)) { + if ((!calculate_duals(lp)) || + (is_presolve(lp, PRESOLVE_SENSDUALS) && + (!calculate_sensitivity_duals(lp) || !calculate_sensitivity_obj(lp)) + ) + ) { + report(lp, IMPORTANT, "findnode_BB: Unable to allocate working memory for duals.\n"); +/* lp->spx_status = NOMEMORY; */ + } + } + if(lp->print_sol != FALSE) { + print_objective(lp); + print_solution(lp, 1); + } + } + + /* Do tracing and determine if we have arrived at the estimated lower MIP limit */ + *varcus = countnint; + if(MIP_count(lp) > 0) { + if((lp->solutioncount == 1) && (lp->solutionlimit == 1) && + ((fabs(lp->bb_limitOF-lp->best_solution[0]) < lp->mip_absgap) || + (fabs(my_reldiff(lp->bb_limitOF, lp->best_solution[0])) < lp->mip_relgap))) { + lp->bb_break = TRUE; + return( FALSE ); + } + else if(lp->bb_level > 0) + if(lp->spx_trace) + report(lp, DETAILED, "B&B level %d OPT %s value " MPSVALUEMASK "\n", + lp->bb_level, (*varno) ? " " : "INT", lp->solution[0]); + return( (MYBOOL) (*varno > 0)); + } + else + return( FALSE ); + +} + +STATIC int solve_BB(BBrec *BB, REAL **UB_active, REAL **LB_active) +{ + int K, status; + lprec *lp = BB->lp; + + /* Protect against infinite recursions do to integer rounding effects */ + status = PROCFAIL; + + /* Shortcut variables, set default bounds */ + K = BB->varno; + *UB_active = BB->upbo; + *LB_active = BB->lowbo; + + /* Load MIP bounds */ + if(K > 0) { + + /* Set inverse and solution vector update parameters */ +#ifdef FastMIP + lp->bb_action = ACTION_ACTIVE | ACTION_RECOMPUTE; + if((lp->bb_totalnodes == 0) || (lp->spx_status == OPTIMAL)) + lp->bb_action |= ACTION_REINVERT | ACTION_REBASE; + else { + if((BB->vartype == BB_SOS) || + (!lp->is_basic[K] && ((lp->is_lower[K] && !BB->isfloor) || + (!lp->is_lower[K] && BB->isfloor)) )) + lp->bb_action |= ACTION_REBASE; + } +#else + lp->bb_action = ACTION_ACTIVE | ACTION_REINVERT | ACTION_REBASE | ACTION_RECOMPUTE; +#endif + + /* BRANCH_FLOOR: Force the variable to be smaller than the B&B upper bound */ + if(BB->isfloor) { + + (*UB_active)[K] = BB->UPbound; + *LB_active = BB->LB_base; + + } + + /* BRANCH_CEILING: Force the variable to be greater than the B&B lower bound */ + else { + + if(!(BB->isSOS && (BB->vartype != BB_INT)) && !BB->isGUB) + *UB_active = BB->UB_base; + (*LB_active)[K] = BB->LObound; + + } + + /* Update MIP node count */ + BB->nodessolved++; + + } + + /* Solve! */ + status = solve_LP(lp, BB, *UB_active, *LB_active); + return( status ); +} + +/* This is the non-recursive B&B driver routine - beautifully simple, yet so subtle! */ +STATIC int run_BB(lprec *lp) +{ + BBrec *currentBB; + REAL *UB_active, *LB_active; + int varno, vartype, varcus, prevsolutions; + int status = NOTRUN; + + /* Initialize */ + prevsolutions = lp->solutioncount; + lp->rootbounds = currentBB = push_BB(lp, NULL, 0, BB_REAL, NULL, NULL, 0); + + /* Perform the branch & bound loop */ + while(lp->bb_level > 0) { + + status = solve_BB(currentBB, &UB_active, &LB_active); + + if((status == OPTIMAL) && findnode_BB(currentBB, &varno, &vartype, &varcus)) + currentBB = push_BB(lp, currentBB, varno, vartype, UB_active, LB_active, varcus); + + else while((lp->bb_level > 0) && !nextbranch_BB(currentBB)) + currentBB = pop_BB(currentBB); + + } + + /* Check if we should adjust status */ + if(lp->solutioncount > prevsolutions) { + if((status == PROCBREAK) || (status == USERABORT) || (status == TIMEOUT)) + status = SUBOPTIMAL; + else + status = OPTIMAL; + if(lp->bb_totalnodes > 0) + lp->spx_status = OPTIMAL; + } + return( status ); +} + Index: src/tools/solver/lp_solve/lp_mipbb.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_mipbb.h diff -N src/tools/solver/lp_solve/lp_mipbb.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_mipbb.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,59 @@ +#ifndef HEADER_lp_mipbb +#define HEADER_lp_mipbb + +#include "lp_types.h" +#include "lp_utils.h" + + +/* Bounds storage for B&B routines */ +typedef struct _BBrec +{ + lprec *lp; + int varno; + int vartype; + int lastvarcus; /* Count of non-int variables of the previous branch */ + REAL lastsolution; /* Optimal solution of the previous branch */ + REAL sc_bound; + MYBOOL sc_canset; + MYBOOL isSOS; + MYBOOL isGUB; + MYBOOL UBzerobased; + REAL *UB_base, *upbo, UPbound; + REAL *LB_base, *lowbo, LObound; + PVrec *saved_upbo, *saved_lowbo; + MYBOOL isfloor; + int nodesleft; + int nodessolved; + int nodestatus; + REAL noderesult; + struct _BBrec *parent; + struct _BBrec *child; +} BBrec; + +#ifdef __cplusplus +extern "C" { +#endif + +STATIC BBrec *create_BB(lprec *lp, BBrec *parentBB, REAL *UB_active, REAL *LB_active); +STATIC MYBOOL compress_BB(BBrec *BB); +STATIC MYBOOL uncompress_BB(BBrec *BB); +STATIC BBrec *push_BB(lprec *lp, BBrec *parentBB, int varno, int vartype, + REAL *UB_active, REAL *LB_active, int varcus); +STATIC MYBOOL initbranches_BB(BBrec *BB); +STATIC MYBOOL fillbranches_BB(BBrec *BB); +STATIC MYBOOL nextbranch_BB(BBrec *BB); +STATIC BBrec *findself_BB(BBrec *BB); +STATIC MYBOOL mergeshadow_BB(BBrec *BB); +STATIC int solve_LP(lprec *lp, BBrec *BB, REAL *upbo, REAL *lowbo); +STATIC MYBOOL findnode_BB(BBrec *BB, int *varno, int *vartype, int *varcus); +STATIC int solve_BB(BBrec *BB, REAL **UB_active, REAL **LB_active); +STATIC void free_BB(BBrec **BB); +STATIC BBrec *pop_BB(BBrec *BB); + +STATIC int run_BB(lprec *lp); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_mipbb */ Index: src/tools/solver/lp_solve/lp_presolve.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_presolve.c diff -N src/tools/solver/lp_solve/lp_presolve.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_presolve.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1349 @@ + +/* ------------------------------------------------------------------------- + Presolve routines for lp_solve v5.0+ + ------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h, lp_presolve, lp_crash.h, lp_scale.h + + Release notes: + v5.0.0 1 January 2004 Significantly expanded and repackaged + presolve routines. + v5.0.1 1 April 2004 Added reference to new crash module + + ------------------------------------------------------------------------- */ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_presolve.h" +#include "lp_crash.h" +#include "lp_scale.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +#define DoPresolveRounding + +STATIC REAL presolve_round(lprec *lp, REAL value, MYBOOL isGE) +{ +#ifdef DoPresolveRoundingTight + REAL epsvalue = lp->epsmachine; +#else + REAL epsvalue = lp->epsprimal; +#endif + +#ifdef DoPresolveRounding + value += my_chsign(isGE, epsvalue/SCALEDINTFIXRANGE); + value = restoreINT(value, epsvalue); +#endif + return( value ); +} +STATIC REAL presolve_precision(lprec *lp, REAL value) +{ +#ifdef DoPresolveRoundingTight + REAL epsvalue = lp->epsmachine; +#else + REAL epsvalue = lp->epsprimal; +#endif + +#ifdef DoPresolveRounding + value = restoreINT(value, epsvalue); +#endif + return( value ); +} + +STATIC int presolve_validate(lprec *lp, LLrec *rowmap, LLrec *colmap) +{ + int i, j, errc = 0; + + /* Validate constraint bounds */ + for(i = 1; i < lp->rows; i++) { + if((rowmap != NULL) && !isActiveLink(rowmap, i)) + continue; + /* Check if we have a negative range */ + if(lp->orig_upbo[i] < 0) { + errc++; + report(lp, SEVERE, "presolve_validate: Detected negative range %g for row %d\n", + lp->orig_upbo[i], i); + } + } + /* Validate variables */ + for(j = 1; j < lp->columns; j++) { + if((colmap != NULL) && !isActiveLink(colmap, j)) + continue; + i = lp->rows+j; + /* Check if we have infeasible bounds */ + if(lp->orig_lowbo[i] > lp->orig_upbo[i]) { + errc++; + report(lp, SEVERE, "presolve_validate: Detected UB < LB for column %d\n", + j); + } + } + /* Return total number of errors */ + return( errc ); +} + + +STATIC int presolve_tighten(lprec *lp, int i, int j, LLrec *colmap, + int items, + REAL *UPpluvalue, REAL *UPnegvalue, + REAL *LOpluvalue, REAL *LOnegvalue, int *count) +{ + REAL RHlow, RHup, RHrange, LObound, UPbound, Value, margin; + MYBOOL SCvar; + int elmnr, elmend, k, oldcount; + MATitem *matelem; + MATrec *mat = lp->matA; + +#ifdef DoPresolveRoundingTight + margin = lp->epsmachine; +#else + margin = lp->epsprimal; +#endif +#ifdef Paranoia + if(!isActiveLink(colmap, j)) + report(lp, SEVERE, "presolve_tighten: The selected column %d was already eliminated\n", + j); +#endif + + Value = get_mat(lp,i,j); + if(Value == 0) + return(RUNNING); + + /* Initialize and identify semicontinuous variable */ + LObound = get_lowbo(lp, j); + UPbound = get_upbo(lp, j); + SCvar = is_semicont(lp, j); + if(SCvar && (UPbound > LObound)) { + if(LObound > 0) + LObound = 0; + else if(UPbound < 0) + UPbound = 0; + } + + /* Get singleton variable bounds */ + if(items == 1) { + RHlow = get_rh_lower(lp, i); + RHup = get_rh_upper(lp, i); + if(Value < 0) { + RHrange = RHup; + RHup = -RHlow; + RHlow = -RHrange; + Value = -Value; + } + if(RHlow <= -lp->infinite) + RHrange = my_chsign(Value < 0, -lp->infinite); + else + RHrange = RHlow / Value; + if(RHrange > -lp->infinite) + RHlow = MAX(LObound, RHrange); + + if(RHup >= lp->infinite) + RHrange = my_chsign(Value < 0, lp->infinite); + else + RHrange = RHup / Value; + if(RHrange < lp->infinite) + RHup = MIN(UPbound, RHrange); + } + else { + RHlow = -lp->infinite; + RHup = lp->infinite; + } + + /* Look for opportunity to tighten upper and lower variable and constraint bounds */ + oldcount = (*count); + if((RHup < lp->infinite) && (RHup+margin < UPbound)) { + if(is_int(lp, j)) { + if(lp->columns_scaled && is_integerscaling(lp) && (ceil(RHup) - RHup > margin)) + RHup = floor(RHup) + margin; + else + RHup = floor(RHup); + } + if(UPbound < lp->infinite) { + elmnr = mat->col_end[j-1]; + elmend = mat->col_end[j]; + for(matelem = mat->col_mat+elmnr; elmnr < elmend; elmnr++, matelem++) { + k = (*matelem).row_nr; + Value = my_chsign(is_chsign(lp, k), (*matelem).value); + Value = unscaled_mat(lp, Value, k, j); + if((Value > 0) && (UPpluvalue[k] < lp->infinite)) + UPpluvalue[k] += (RHup-UPbound)*Value; + else if((Value < 0) && (UPnegvalue[i] < lp->infinite)) + UPnegvalue[k] += (RHup-UPbound)*Value; + } + } + if(RHup < UPbound) { + UPbound = RHup; + (*count)++; + } + } + if((RHlow > -lp->infinite) && (RHlow-margin > LObound)) { + if(is_int(lp, j)) { + if(lp->columns_scaled && is_integerscaling(lp) && (RHlow - floor(RHlow) > margin)) + RHlow = ceil(RHlow)-margin; + else + RHlow = ceil(RHlow); + } + if(LObound > -lp->infinite) { + elmnr = mat->col_end[j-1]; + elmend = mat->col_end[j]; + for(matelem = mat->col_mat+elmnr; elmnr < elmend; elmnr++, matelem++) { + k = (*matelem).row_nr; + Value = my_chsign(is_chsign(lp, k), (*matelem).value); + Value = unscaled_mat(lp, Value, k, j); + if((Value > 0) && (LOpluvalue[k] > -lp->infinite)) + LOpluvalue[k] += (RHlow-LObound)*Value; + else if((Value < 0) && (LOnegvalue[k] > -lp->infinite)) + LOnegvalue[k] += (RHlow-LObound)*Value; + } + } + if(RHlow > LObound) { + LObound = RHlow; + (*count)++; + } + } + + /* Now set the new variable bounds, if they are tighter */ + if((*count) > oldcount) { +#if 0 /* Experimental new version */ + UPbound = presolve_round(lp, UPbound-margin, FALSE); + LObound = presolve_round(lp, LObound+margin, TRUE); +#else /* Safe new version */ + UPbound = presolve_precision(lp, UPbound); + LObound = presolve_precision(lp, LObound); +#endif + if(LObound > UPbound) { + if(LObound-UPbound < margin) { + LObound = UPbound; + } + else { + report(lp, IMPORTANT, "presolve_tighten: Found LB %g > UB %g in row %d, column %d\n", + LObound, UPbound, i, j); + return(INFEASIBLE); + } + } + if(lp->spx_trace || (lp->verbose > DETAILED)) + report(lp, NORMAL, "presolve_tighten: Replaced bounds on column %d to [%g ... %g]\n", + j, LObound, UPbound); + set_bounds(lp, j, LObound, UPbound); + } + + return( RUNNING ); +} + +STATIC void presolve_init(lprec *lp, int *plucount, int *negcount, int *pluneg) +{ + int i, ix, colnr; + MYBOOL isMI; + REAL value, lobound, upbound; + MATrec *mat = lp->matA; + + /* First do tallies; loop over nonzeros by column */ + for(colnr = 1; colnr <= lp->columns; colnr++) { + + upbound = get_upbo(lp, colnr); + lobound = get_lowbo(lp, colnr); + + /* Handle strictly negative variables as converted to positive range with negative sign */ + isMI = is_negative(lp, colnr); + + if(is_semicont(lp, colnr) && (upbound > lobound)) { + if(lobound > 0) + lobound = 0; + else if(upbound < 0) + upbound = 0; + } + + for(ix = mat->col_end[colnr - 1]; ix < mat->col_end[colnr]; ix++) { + + /* Retrieve row data and prepare */ + i = mat->col_mat[ix].row_nr; + value = my_chsign(isMI, mat->col_mat[ix].value); + value = my_chsign(is_chsign(lp, i), value); + + /* Cumulate counts */ + if(value > 0) + plucount[i]++; + else + negcount[i]++; + if((lobound < 0) && (upbound > 0)) + pluneg[i]++; + } + } +} + +STATIC int presolve_nextcol(MATrec *mat, int rownr, int prevcol, LLrec *colmap) +/* Find the first active (non-eliminated) nonzero column in rownr after prevcol */ +{ + int j, jb = 0, je = mat->row_end[rownr]; + + if(rownr > 0) + jb = mat->row_end[rownr-1]; + for(; jb < je; jb++) { + j = ROW_MAT_COL(mat->row_mat[jb]); + if((j > prevcol) && isActiveLink(colmap, j)) + return( jb ); + } + return( je ); +} +STATIC int presolve_nextrow(MATrec *mat, int colnr, int prevrow, LLrec *rowmap) +/* Find the first active (non-eliminated) nonzero row in colnr after prevrow */ +{ + int i, ib = mat->col_end[colnr-1], ie = mat->col_end[colnr]; + + for(; ib < ie; ib++) { + i = mat->col_mat[ib].row_nr; + if((i > prevrow) && isActiveLink(rowmap, i)) + return( ib ); + } + return( ie ); +} + +STATIC void presolve_rowupdate(lprec *lp, int rownr, int *collength, MYBOOL remove) +{ + if(collength != NULL) { + if(remove) { + int i, ie; + MATrec *mat = lp->matA; + + i = mat->row_end[rownr-1]; + ie = mat->row_end[rownr]; + for(; i < ie; i++) + collength[ROW_MAT_COL(mat->row_mat[i])]--; + } + } +} + +STATIC void presolve_colupdate(lprec *lp, int colnr, int *pluneg, int *rowlength, + int *plucount, int *negcount, + REAL *pluupper, REAL *negupper, + REAL *plulower, REAL *neglower, MYBOOL remove) +{ + int i, ix, ie; + MYBOOL isneg, isMI; + REAL lobound, upbound, lovalue, upvalue, + value, fixvalue, mult, epsvalue; + MATitem *matelem; + MATrec *mat = lp->matA; + +#ifdef DoPresolveRoundingTight + epsvalue = lp->epsmachine; +#else + epsvalue = lp->epsprimal; +#endif + + if(remove) + mult = -1; + else + mult = 1; + upbound = get_upbo(lp, colnr); + lobound = get_lowbo(lp, colnr); + + /* Handle strictly negative variables as converted to positive range with negative sign */ + isMI = is_negative(lp, colnr); + + /* Set "exiting" value in case we are deleting a variable */ + if(upbound-lobound < epsvalue) + fixvalue = lp->orig_lowbo[lp->rows+colnr]; + else + fixvalue = 0; + + /* Adjust semi-continuous variable bounds to zero-base */ + if(is_semicont(lp, colnr) && (upbound > lobound)) { + if(lobound > 0) + lobound = 0; + else if(upbound < 0) + upbound = 0; + } + + ix = mat->col_end[colnr - 1]; + ie = mat->col_end[colnr]; + for(matelem = mat->col_mat+ix; ix < ie; ix++, matelem++) { + + /* Retrieve row data and adjust RHS if we are deleting a variable */ + i = (*matelem).row_nr; + value = (*matelem).value; + + if(remove && (fixvalue != 0)) { + lp->orig_rh[i] -= value * fixvalue; + my_roundzero(lp->orig_rh[i], epsvalue); + } + + /* Prepare for further processing */ + value = unscaled_mat(lp, value, i, colnr); + value = my_chsign(is_chsign(lp, i), value); + isneg = (MYBOOL) (value < 0); + if(isMI) + isneg = !isneg; + + /* Reduce row variable counts */ + if(remove) { + if(rowlength != NULL) + rowlength[i]--; + if(isneg) + negcount[i]--; + else + plucount[i]--; + if((lobound < 0) && (upbound > 0)) + pluneg[i]--; + } + + /* Compute associated constraint contribution values */ + if(isneg) { + chsign_bounds(&lobound, &upbound); + value = -value; + } + upvalue = my_if(upbound < lp->infinite, value*upbound, lp->infinite); + lovalue = my_if(lobound > -lp->infinite, value*lobound, -lp->infinite); + + /* Cumulate effective upper row bound (only bother about non-finite bound) */ + if(isneg) { + if((negupper[i] < lp->infinite) && (upvalue < lp->infinite)) { + negupper[i] += mult*upvalue; + negupper[i] = presolve_round(lp, negupper[i], FALSE); + } + else if(!remove) + negupper[i] = lp->infinite; + } + else { + if((pluupper[i] < lp->infinite) && (upvalue < lp->infinite)) { + pluupper[i] += mult*upvalue; + pluupper[i] = presolve_round(lp, pluupper[i], FALSE); + } + else if(!remove) + pluupper[i] = lp->infinite; + } + + /* Cumulate effective lower row bound (only bother about non-finite bound) */ + if(isneg) { + if((neglower[i] > -lp->infinite) && (lovalue > -lp->infinite)) { + neglower[i] += mult*lovalue; + neglower[i] = presolve_round(lp, neglower[i], TRUE); + } + else if(!remove) + neglower[i] = -lp->infinite; + + /* Remember to reset reversed bounds */ + chsign_bounds(&lobound, &upbound); + } + else { + if((plulower[i] > -lp->infinite) && (lovalue > -lp->infinite)) { + plulower[i] += mult*lovalue; + plulower[i] = presolve_round(lp, plulower[i], TRUE); + } + else if(!remove) + plulower[i] = -lp->infinite; + } + } +} + + +STATIC void presolve_finalize(lprec *lp, LLrec *rowmap, LLrec *colmap) +{ + int i, ke, kb, n; + + if(colmap != NULL) { + ke = lastInactiveLink(colmap); + n = countInactiveLink(colmap); + if((n > 0) && (ke > 0)) { + kb = lastActiveLink(colmap); + while(kb > ke) + kb = prevActiveLink(colmap, kb); + ke++; + while ((n > 0) && (ke > 0)) { + for(i = ke-1; i > kb; i--) { + del_column(lp, i); + n--; + } + ke = kb; + kb = prevActiveLink(colmap, kb); + } + } + freeLink(&colmap); + } + if(rowmap != NULL) { + ke = lastInactiveLink(rowmap); + n = countInactiveLink(rowmap); + if((n > 0) && (ke > 0)) { + kb = lastActiveLink(rowmap); + while(kb > ke) + kb = prevActiveLink(rowmap, kb); + ke++; + while ((n > 0) && (ke > 0)) { + for(i = ke-1; i > kb; i--) { +#ifdef DeferredCompactA + del_constraint(lp, -i); +#else + del_constraint(lp, i); +#endif + n--; + } + ke = kb; + kb = prevActiveLink(rowmap, kb); + } + } + freeLink(&rowmap); +#ifdef DeferredCompactA + mat_rowcompact(lp->matA); +#endif + } + mat_validate(lp->matA); +} + +STATIC REAL sumplumin(lprec *lp, int item, REAL *plu, REAL *neg) +{ + if(fabs(plu[item]) >= lp->infinite) + return( plu[item] ); + else if(fabs(neg[item]) >= lp->infinite) + return( neg[item] ); + else + return( plu[item]+neg[item] ); +} + +STATIC int presolve_collength(MATrec *mat, int column, LLrec *rowmap, int *collength) +{ + if(collength != NULL) + column = collength[column]; + else if(rowmap == NULL) + column = mat_collength(mat, column); + else { + int ib = mat->col_end[column-1], + ie = mat->col_end[column]; + MATitem *item; + column = 0; + for(item = mat->col_mat+ib; ib < ie; ib++, item++) { + if(((*item).row_nr == 0) || + isActiveLink(rowmap, (*item).row_nr)) + column++; + } + } + return( column ); +} +STATIC int presolve_rowlength(MATrec *mat, int row, LLrec *colmap, int *rowlength) +{ + if(rowlength != NULL) + row = rowlength[row]; + else if(colmap == NULL) + row = mat_rowlength(mat, row); + else { + int ib = 0, ie = mat->row_end[row]; + if(row > 0) + ib = mat->row_end[row-1], + row = 0; + for(; ib < ie; ib++) { + if(isActiveLink(colmap, ROW_MAT_COL(mat->row_mat[ib]))) + row++; + } + } + return( row ); +} + +STATIC int presolve(lprec *lp) +{ + MYBOOL candelete; + int i,j,ix,iix,jx,jjx, n,nm,nn=0, nc,nv,nb,nr,ns,nt,nl; + int status; + int *plucount, *negcount, *pluneg; + int *rowlength = NULL, *collength = NULL; + REAL *pluupper, *negupper, + *plulower, *neglower, + value, bound, test, epsvalue = lp->epsprimal; + MATrec *mat = lp->matA; + LLrec *rowmap = NULL, *colmap = NULL; + + /* Check if we have already done presolve */ + status = RUNNING; + if(lp->wasPresolved) + return(status); + + /* Finalize basis indicators; if no basis was created earlier via + set_basis or crash_basis then simply set the default basis. */ + if(!lp->basis_valid) + lp->var_basic[0] = AUTOMATIC; /* Flag that we are presolving */ + + /* Lock the variable mapping arrays and counts ahead of any row/column + deletion or creation in the course of presolve, solvelp or postsolve */ + varmap_lock(lp); + mat_validate(mat); + + /* Do the scaling of the problem (can also be moved to the end of the + presolve block (before "Finish:") to possibly reduce rounding errors */ +#ifndef PostScale + lp_solve_auto_scale(lp); +#endif + +#if 0 +write_lp(lp, "test_in.lp"); /* Write to lp-formatted file for debugging */ +/*write_mps(lp, "test_in.mps");*/ /* Write to lp-formatted file for debugging */ +#endif + + /* Do traditional simple presolve */ + yieldformessages(lp); + if((lp->do_presolve & PRESOLVE_LASTMASKMODE) == PRESOLVE_NONE) + mat_checkcounts(mat, NULL, NULL, TRUE); + + else { + + if(lp->full_solution == NULL) + allocREAL(lp, &lp->full_solution, lp->sum_alloc+1, TRUE); + + nm = 0; + nl = 0; + nv = 0; + nc = 0; + nb = 0; + nr = 0; + ns = 0; + nt = 0; + + /* Identify infeasible SOS'es prior to any pruning */ + for(i = 1; i <= SOS_count(lp); i++) { + nn = SOS_infeasible(lp->SOS, i); + if(nn > 0) { + report(lp, NORMAL, "presolve: Found SOS %d (type %d) to be range-infeasible on variable %d\n", + i, SOS_get_type(lp->SOS, i), nn); + status = INFEASIBLE; + ns++; + } + } + if(ns > 0) + goto Finish; + + /* Create row and column counts */ +#if 1 + allocINT(lp, &rowlength, lp->rows + 1, TRUE); + for(i = 1; i <= lp->rows; i++) + rowlength[i] = presolve_rowlength(mat, i, NULL, NULL); + allocINT(lp, &collength, lp->columns + 1, TRUE); + for(j = 1; j <= lp->columns; j++) + collength[j] = presolve_collength(mat, j, NULL, NULL); +#endif + + /* Create NZ count and sign arrays, and do general initialization of row bounds */ + allocINT(lp, &plucount, lp->rows + 1, TRUE); + allocINT(lp, &negcount, lp->rows + 1, TRUE); + allocINT(lp, &pluneg, lp->rows + 1, TRUE); + allocREAL(lp, &pluupper, lp->rows + 1, TRUE); + allocREAL(lp, &negupper, lp->rows + 1, TRUE); + allocREAL(lp, &plulower, lp->rows + 1, TRUE); + allocREAL(lp, &neglower, lp->rows + 1, TRUE); + createLink(lp->rows, &rowmap, NULL); + fillLink(rowmap); + createLink(lp->columns, &colmap, NULL); + fillLink(colmap); + presolve_init(lp, plucount, negcount, pluneg); + + /* Accumulate constraint bounds based on bounds on individual variables */ +Restart: + nm++; + for(j = firstActiveLink(colmap); j != 0; j = nextActiveLink(colmap, j)) { + presolve_colupdate(lp, j, pluneg, NULL, + plucount, negcount, + pluupper, negupper, + plulower, neglower, FALSE); + if((status == RUNNING) && userabort(lp, -1)) + goto Complete; + } + + /* Reentry point */ +Redo: + nl++; + nn = 0; + + /* Eliminate empty or fixed columns (including trivial OF column singletons) */ + if(is_presolve(lp, PRESOLVE_COLS) && mat_validate(mat)) { + if(userabort(lp, -1)) + goto Complete; + n = 0; + for(j = lastActiveLink(colmap); j > 0; ) { + candelete = FALSE; + ix = lp->rows + j; + iix = presolve_collength(mat, j, rowmap, collength); + if(iix == 0) { + if(SOS_is_member(lp->SOS, 0, j)) + report(lp, NORMAL, "presolve: Found empty variable %d as member of a SOS\n", + get_col_name(lp,j)); + else { + if(lp->orig_lowbo[ix] != 0) + report(lp, DETAILED, "presolve: Found empty non-zero variable %s\n", + get_col_name(lp,j)); + candelete = TRUE; + } + } + else if(isOrigFixed(lp, ix)) { + if(SOS_is_member(lp->SOS, 0, j)) + continue; + report(lp, DETAILED, "presolve: Eliminated variable %s fixed at %g\n", + get_col_name(lp,j), get_lowbo(lp, j)); + candelete = TRUE; + } + else if((iix == 1) && (mat->col_mat[iix = mat->col_end[j-1]].row_nr == 0) && + !SOS_is_member(lp->SOS, 0, j)) { + + if(get_OF_raw(lp, ix) > 0) + test = get_lowbo(lp, j); + else + test = get_upbo(lp, j); + if(isInf(lp, fabs(test))) { + report(lp, DETAILED, "presolve: Unbounded variable %s\n", + get_col_name(lp,j)); + status = UNBOUNDED; + } + else { + /* Fix the value at its best bound */ + set_bounds(lp, j, test, test); + report(lp, DETAILED, "presolve: Eliminated trivial variable %s fixed at %g\n", + get_col_name(lp,j), test); + candelete = TRUE; + } + } + + ix = j; + j = prevActiveLink(colmap, j); + if(candelete) { + value = get_lowbo(lp, ix); + lp->full_solution[lp->orig_rows + lp->var_to_orig[lp->rows + ix]] = value; + presolve_colupdate(lp, ix, pluneg, rowlength, + plucount, negcount, + pluupper, negupper, + plulower, neglower, TRUE); + removeLink(colmap, ix); + n++; + nv++; + nn++; + } + } + } + + /* Eliminate linearly dependent rows; loop backwards over every row */ + if(is_presolve(lp, PRESOLVE_LINDEP) && mat_validate(mat)) { + int firstix, RT1, RT2; + if(userabort(lp, -1)) + goto Complete; + n = 0; + for(i = lastActiveLink(rowmap); (i > 0) && (status == RUNNING); ) { + + /* First scan for rows with identical row lengths */ + ix = prevActiveLink(rowmap, i); + if(ix == 0) + break; + + /* Don't bother about empty rows or row singletons since they are + handled by PRESOLVE_ROWS */ + j = presolve_rowlength(mat, i, colmap, rowlength); + if(j <= 1) { + i = ix; + continue; + } + +#if 0 + /* Enable this to scan all rows back */ + RT2 = lp->rows; +#else + RT2 = 1; +#endif + firstix = ix; + for(RT1 = 0; (ix > 0) && (RT1 < RT2) && (status == RUNNING); + ix = prevActiveLink(rowmap, ix), RT1++) { + candelete = FALSE; + if(presolve_rowlength(mat, ix, colmap, rowlength) != j) + continue; + + /* Check if the beginning columns are identical; if not, continue */ + iix = presolve_nextcol(mat, ix, 0, colmap); + jjx = presolve_nextcol(mat, i, 0, colmap); + + if(ROW_MAT_COL(mat->row_mat[iix]) != ROW_MAT_COL(mat->row_mat[jjx])) + continue; + + /* We have a candidate row; check if the entries have a fixed non-zero ratio */ + test = get_mat_byindex(lp, iix, TRUE); + value = get_mat_byindex(lp, jjx, TRUE); + bound = test / value; + value = bound; + + /* Loop over remaining entries */ + jx = mat->row_end[i]; + jjx = presolve_nextcol(mat, i, ROW_MAT_COL(mat->row_mat[jjx]), colmap); + for(; (jjx < jx) && (value == bound); + jjx = presolve_nextcol(mat, i, ROW_MAT_COL(mat->row_mat[jjx]), colmap)) { + iix = presolve_nextcol(mat, ix, ROW_MAT_COL(mat->row_mat[iix]), colmap); + if(ROW_MAT_COL(mat->row_mat[iix]) != ROW_MAT_COL(mat->row_mat[jjx])) + break; + test = get_mat_byindex(lp, iix, TRUE); + value = get_mat_byindex(lp, jjx, TRUE); + + /* If the ratio is different from the reference value we have a mismatch */ + value = test / value; + if(bound == lp->infinite) + bound = value; + else if(fabs(value - bound) > epsvalue) + break; + } + + /* Check if we found a match (we traversed all active columns without a break) */ + if(jjx >= jx) { + + /* Get main reference values */ + test = lp->orig_rh[ix]; + value = lp->orig_rh[i] * bound; + + /* First check for inconsistent equalities */ + if((fabs(test - value) > epsvalue) && + ((get_constr_type(lp, ix) == EQ) && (get_constr_type(lp, i) == EQ))) + status = INFEASIBLE; + + else { + + /* Update lower and upper bounds */ + if(is_chsign(lp, i) != is_chsign(lp, ix)) + bound = -bound; + + test = get_rh_lower(lp, i); + if(test <= -lp->infinite) + test *= my_sign(bound); + else + test *= bound; + + value = get_rh_upper(lp, i); + if(value >= lp->infinite) + value *= my_sign(bound); + else + value *= bound; + + if((bound < 0)) + swapREAL(&test, &value); + + if(get_rh_lower(lp, ix) < test) + set_rh_lower(lp, ix, test); + if(get_rh_upper(lp, ix) > value) + set_rh_upper(lp, ix, value); + + /* Check results */ + test = get_rh_lower(lp, ix); + value = get_rh_upper(lp, ix); + if(fabs(value-test) < epsvalue) + lp_solve_set_constr_type(lp, ix, EQ); + else if(value < test) + status = INFEASIBLE; + + /* Verify if we can continue */ + candelete = (MYBOOL) (status == RUNNING); + if(!candelete) { + report(lp, IMPORTANT, "presolve: Range infeasibility found involving rows %d and %d\n", + ix, i); + } + } + } + /* Perform i-row deletion if authorized */ + if(candelete) { + presolve_rowupdate(lp, i, collength, TRUE); + removeLink(rowmap, i); + n++; + nc++; + } + } + i = firstix; + } + } + + /* Aggregate and tighten bounds using 2-element EQs */ + if(FALSE && + (lp->equalities > 0) && is_presolve(lp, PRESOLVE_AGGREGATE) && mat_validate(mat)) { + if(userabort(lp, -1)) + goto Complete; + n = 0; + for(i = lastActiveLink(rowmap); (i > 0) && (status == RUNNING); ) { + /* Find an equality constraint with 2 elements; the pivot row */ + if(!is_constr_type(lp, i, EQ) || (presolve_rowlength(mat, i, colmap, rowlength) != 2)) { + i = prevActiveLink(rowmap, i); + continue; + } + /* Get the column indeces of NZ-values of the pivot row */ + jx = mat->row_end[i-1]; + j = mat->row_end[i]; + for(; jx < j; jx++) + if(isActiveLink(colmap, ROW_MAT_COL(mat->row_mat[jx]))) + break; + jjx = jx+1; + for(; jjx < j; jjx++) + if(isActiveLink(colmap, ROW_MAT_COL(mat->row_mat[jjx]))) + break; + jx = ROW_MAT_COL(mat->row_mat[jx]); + jjx = ROW_MAT_COL(mat->row_mat[jjx]); + if(SOS_is_member(lp->SOS, 0, jx) && SOS_is_member(lp->SOS, 0, jjx)) { + i = prevActiveLink(rowmap, i); + continue; + } + /* Determine which column we should eliminate (index in jx) : + 1) the longest column + 2) the variable not being a SOS member + 3) an integer variable */ + if(presolve_collength(mat, jx, rowmap, collength) < + presolve_collength(mat, jjx, rowmap, collength)) + swapINT(&jx, &jjx); + if(SOS_is_member(lp->SOS, 0, jx)) + swapINT(&jx, &jjx); + if(!is_int(lp, jx) && is_int(lp, jjx)) + swapINT(&jx, &jjx); + /* Whatever the priority above, we must have bounds to work with; + give priority to the variable with the smallest bound */ + test = get_upbo(lp, jjx)-get_lowbo(lp, jjx); + value = get_upbo(lp, jx)-get_lowbo(lp, jx); + if(test < value) + swapINT(&jx, &jjx); + /* Try to set tighter bounds on the non-eliminated variable (jjx) */ + test = get_mat(lp, i, jjx); /* Non-eliminated variable coefficient a */ + value = get_mat(lp, i, jx); /* Eliminated variable coefficient b */ +#if 1 + bound = get_lowbo(lp, jx); + if((bound > -lp->infinite)) { + bound = (get_rh(lp, i)-value*bound) / test; + if(bound < get_upbo(lp, jjx)-epsvalue) + lp_solve_set_upbo(lp, jjx, presolve_round(lp, bound, FALSE)); + } + bound = get_upbo(lp, jx); + if((bound < lp->infinite)) { + bound = (get_rh(lp, i)-value*bound) / test; + if(bound > get_lowbo(lp, jjx)+epsvalue) + lp_solve_set_lowbo(lp, jjx, presolve_round(lp, bound, TRUE)); + } + i = prevActiveLink(rowmap, i); +#else + /* Loop over the non-zero rows of the column to be eliminated; + substitute jx-variable by updating rhs and jjx coefficients */ + for(ix = mat->col_end[jx-1]; ix < mat->col_end[jx]; ix++) { + REAL newvalue; + iix = mat->col_mat[ix].row_nr; + if((iix == i) || + ((iix > 0) && !isActiveLink(rowmap, iix))) + continue; + /* Do the update */ + bound = unscaled_mat(lp, mat->col_mat[ix].value, iix, jx)/value; + bound = my_chsign(is_chsign(lp, iix), bound); + newvalue = get_mat(lp, iix, jjx) - bound*test; + lp_solve_set_mat(lp, iix, jjx, presolve_precision(lp, newvalue)); + newvalue = get_rh(lp, iix) - bound*get_rh(lp, i); + lp_solve_set_rh(lp, iix, presolve_precision(lp, newvalue)); + } + /* Delete the column */ + removeLink(colmap, jx); + nc++; + n++; + /* Delete the row */ + ix = i; + i = prevActiveLink(rowmap, i); + presolve_rowupdate(lp, ix, collength, TRUE); + removeLink(rowmap, ix); + nr++; + n++; + mat_validate(mat); +#endif + } + } + +#if 0 + /* Increase A matrix sparsity by discovering common subsets using 2-element EQs */ + if((lp->equalities > 0) && is_presolve(lp, PRESOLVE_SPARSER) && mat_validate(mat)) { + if(userabort(lp, -1)) + goto Complete; + n = 0; + for(i = lastActiveLink(rowmap); (i > 0) && (status == RUNNING); ) { + candelete = FALSE; + /* Find an equality constraint with 2 elements; the pivot row */ + if(!is_constr_type(lp, i, EQ) || (mat_rowlength(mat, i) != 2)) { + i = prevActiveLink(rowmap, i); + continue; + } + /* Get the column indeces of NZ-values of the pivot row */ + jx = mat->row_end[i-1]; + jx = ROW_MAT_COL(mat->row_mat[jx]); + jjx = mat->row_end[i]; + jjx = ROW_MAT_COL(mat->row_mat[jjx]); + /* Scan to find a row with matching column entries */ + for(ix = lp->col_end[jx-1]; ix < lp->col_end[jx]; ix++) { + if(lp->col_mat[ix].row_nr == i) + continue; + /* We now have a single matching value, find the next */ + if(ix < lp->col_end[jx]) { + for(iix = lp->col_end[jjx-1]; iix < lp->col_end[jjx]; iix++) + if(lp->col_mat[iix].row_nr >= ix) + break; + /* Abort this row if there was no second column match */ + if((iix >= lp->col_end[jjx]) || (lp->col_mat[iix].row_nr > ix) ) + break; + /* Otherwise, do variable subsitution and mark pivot row for deletion */ + candelete = TRUE; + nc++; + /* + ... Add remaining logic later! + */ + } + } + ix = i; + i = prevActiveLink(rowmap, i); + if(candelete) { + presolve_rowupdate(lp, ix, collength, TRUE); + removeLink(rowmap, ix); + n++; + } + } + } +#endif + + /* Eliminate empty rows, convert row singletons to bounds, + tighten bounds, and remove always satisfied rows */ + if(is_presolve(lp, PRESOLVE_ROWS) && mat_validate(mat)) { + if(userabort(lp, -1)) + goto Complete; + n = 0; + for(i = lastActiveLink(rowmap); i > 0; ) { + candelete = FALSE; + + /* First identify any full row infeasibilities */ + if(!is_constr_type(lp, i, LE)) + if(MAX(sumplumin(lp, i, plulower,neglower), + sumplumin(lp, i, pluupper,negupper)) < get_rh(lp, i)-lp->infinite) { + report(lp, NORMAL, "presolve: Found upper bound infeasibility in row %d\n", i); + return(INFEASIBLE); + } + if(!is_constr_type(lp, i, GE)) + if(MIN(sumplumin(lp, i, plulower,neglower), + sumplumin(lp, i, pluupper,negupper)) > get_rh(lp, i)+lp->infinite) { + report(lp, NORMAL, "presolve: Found lower bound infeasibility in row %d\n", i); + return(INFEASIBLE); + } + + j = plucount[i]+negcount[i]; + + /* Delete non-zero rows and variables that are completely determined; + note that this step can provoke infeasibility in some tight models */ + if((j > 0) /* Only examine non-empty rows, */ + && (fabs(lp->orig_rh[i]) < epsvalue) /* .. and the current RHS is zero, */ + && ((plucount[i] == 0) || (negcount[i] == 0)) /* .. and the parameter signs are all equal, */ + && (pluneg[i] == 0) /* .. and no (quasi) free variables, */ + && (is_constr_type(lp, i, EQ) +#if 1 + || (fabs(get_rh_lower(lp, i)-sumplumin(lp, i, pluupper,negupper)) < epsvalue) /* Convert to equalities */ + || (fabs(get_rh_upper(lp, i)-sumplumin(lp, i, plulower,neglower)) < epsvalue) /* Convert to equalities */ +#endif + ) + ) { + /* Delete the columns of this row, but make sure we don't delete SOS variables */ + for(ix = mat->row_end[i]-1; ix >= mat->row_end[i-1]; ix--) { + jx = ROW_MAT_COL(mat->row_mat[ix]); + if(isActiveLink(colmap, jx) && !SOS_is_member(lp->SOS, 0, jx)) { +#if 0 + if(get_OF_raw(lp, lp->rows+jx) > 0) + test = get_lowbo(lp, jx); + else + test = get_upbo(lp, jx); + set_bounds(lp, jx, test, test); +#endif + presolve_colupdate(lp, jx, pluneg, rowlength, + plucount, negcount, + pluupper, negupper, + plulower, neglower, TRUE); + removeLink(colmap, jx); + nv++; + } + } + /* Then delete the row, which is redundant */ + candelete = TRUE; + nc++; + } + else + + /* Then delete any empty or always satisfied / redundant row that cannot at + the same time guarantee that we can also delete associated variables */ + if((j == 0) || /* Always delete an empty row */ + ((j > 1) && + (pluneg[i] == 0) && ((plucount[i] == 0) || + (negcount[i] == 0)) && /* Consider removing if block above is ON! */ + (sumplumin(lp, i, pluupper,negupper)-sumplumin(lp, i, plulower,neglower) < epsvalue)) /* .. or if it is always satisfied (redundant) */ + ) { + candelete = TRUE; + nc++; + } + + /* Convert row singletons to bounds (delete fixed columns in columns section); + creates numeric instability in BOEING1.MPS */ + else if((j == 1) && + (sumplumin(lp, i, pluupper,negupper)-sumplumin(lp, i, plulower,neglower) >= epsvalue)) { + j = presolve_nextcol(mat, i, 0, colmap); + j = ROW_MAT_COL(mat->row_mat[j]); + status = presolve_tighten(lp, i, j, colmap, plucount[i]+negcount[i], + pluupper, negupper, + plulower, neglower, &nt); + if(status == INFEASIBLE) { + nn = 0; + break; + } + candelete = TRUE; + nb++; + } + + /* Check if we have a constraint made redundant through bounds on individual variables */ + else if((sumplumin(lp, i, plulower,neglower) >= get_rh_lower(lp, i)-epsvalue) && + (sumplumin(lp, i, pluupper,negupper) <= get_rh_upper(lp, i)+epsvalue)) { + candelete = TRUE; + nc++; + } + +#ifdef AggressiveRowPresolve + /* Look for opportunity to tighten constraint bounds; + know to create problems with scaled ADLittle.mps */ + else if(j > 1) { + test = sumplumin(lp, i, plulower,neglower); + if(test > get_rh_lower(lp, i)+epsvalue) { + set_rh_lower(lp, i, presolve_round(lp, test, TRUE)); + nr++; + } + test = sumplumin(lp, i, pluupper,negupper); + if(test < get_rh_upper(lp, i)-epsvalue) { + set_rh_upper(lp, i, presolve_round(lp, test, FALSE)); + nr++; + } + } +#endif + + /* Get next row and do the deletion of the previous, if indicated */ + ix = i; + i = prevActiveLink(rowmap, i); + if(candelete) { + presolve_rowupdate(lp, ix, collength, TRUE); + removeLink(rowmap, ix); + n++; + nn++; + } + /* Look for opportunity to convert ranged constraint to equality-type */ + else if(!is_constr_type(lp, ix, EQ) && (get_rh_range(lp, ix) < epsvalue)) + lp_solve_set_constr_type(lp, ix, EQ); + } + } + + /* Check if we can tighten bounds on individual variables; + note that this can create degeneracy in some models */ + if(FALSE) { + REAL rhup, rhlo, varup, varlo; + for(j = firstActiveLink(colmap); j > 0; j = nextActiveLink(colmap, j)) { + varup = get_upbo(lp, j); + varlo = get_lowbo(lp, j); + for(ix = mat->col_end[j-1]; ix < mat->col_end[j]; ix++) { + iix = mat->col_mat[ix].row_nr; + if((iix == 0) || !isActiveLink(rowmap, iix) || + (pluneg[iix] != 0) || + (plucount[iix]*negcount[iix] > 0)) + continue; + + /* Retrieve coefficients and standardize to positive */ + value = my_chsign(is_chsign(lp, iix), get_mat_byindex(lp, ix, FALSE)); + rhup = get_rh_upper(lp, iix); + rhlo = get_rh_lower(lp, iix); + if(negcount[iix] > 0) { + value = -value; + rhup = -rhup; + rhlo = -rhlo; + swapREAL(&rhup, &rhlo); + } + + if(!isInf(lp,rhup) && (isInf(lp,varup) || (value*varup > rhup+epsvalue))) { + varup = rhup / value; + lp_solve_set_upbo(lp, j, varup); + nt++; + nn++; + } +/* if(!isInf(lp,rhlo) && (isInf(lp,varlo) || (value*varlo < rhlo-epsvalue))) { + varlo = rhlo / value; + lp_solve_set_lowbo(lp, j, varlo); + nt++; + nn++; + }*/ + if(varup-varlo < 0) { + status = INFEASIBLE; + break; + } + } + } + } + + + /* Try again if we were successful in this presolve loop */ + if((status == RUNNING) && !userabort(lp, -1)) { + if(nn > 0) goto Redo; + + /* Optionally do an extra loop from scratch */ + if((nm < 1) && (nc+nb+nt+nv+nr > 0)) { + MEMCLEAR(plucount, lp->rows+1); + MEMCLEAR(negcount, lp->rows+1); + MEMCLEAR(pluneg , lp->rows+1); + MEMCLEAR(pluupper, lp->rows+1); + MEMCLEAR(negupper, lp->rows+1); + MEMCLEAR(plulower, lp->rows+1); + MEMCLEAR(neglower, lp->rows+1); + goto Restart; + } + } + +Complete: + /* See if we can convert some constraints to SOSes (only SOS1 handled) */ + if(is_presolve(lp, PRESOLVE_SOS) && + (MIP_count(lp) > 0) && mat_validate(mat)) { + n = 0; + for(i = lastActiveLink(rowmap); i > 0; ) { + candelete = FALSE; + test = get_rh(lp, i); + jx = get_constr_type(lp, i); +#ifdef EnableBranchingOnGUB + if((test == 1) && (jx != GE)) { +#else + if((test == 1) && (jx == LE)) { +#endif + jjx = mat->row_end[i-1]; + iix = mat->row_end[i]; + for(; jjx < iix; jjx++) { + j = ROW_MAT_COL(mat->row_mat[jjx]); + if(!isActiveLink(colmap, j)) + continue; + if(!is_binary(lp, j) || (get_mat(lp, i, j) != 1)) + break; + } + if(jjx >= iix) { + char SOSname[16]; + + /* Define a new SOS instance */ + sprintf(SOSname, "SOS_%d", SOS_count(lp) + 1); + ix = add_SOS(lp, SOSname, 1, 1, 0, NULL, NULL); + if(jx == EQ) + SOS_set_GUB(lp->SOS, ix, TRUE); + value = 0; + jjx = mat->row_end[i-1]; + for(; jjx < iix; jjx++) { + j = ROW_MAT_COL(mat->row_mat[jjx]); + if(!isActiveLink(colmap, j)) + continue; + value += 1; + append_SOSrec(lp->SOS->sos_list[ix-1], 1, &j, &value); + } + candelete = TRUE; + nc++; + } + } + + /* Get next row and do the deletion of the previous, if indicated */ + ix = i; + i = prevActiveLink(rowmap, i); + if(candelete) { + presolve_rowupdate(lp, ix, collength, TRUE); + removeLink(rowmap, ix); + n++; + nn++; + } + } + if(n) + report(lp, NORMAL, "presolve: Converted %5d constraints to SOS1.\n", n); + } + + /* Finalize presolve */ +#ifdef Paranoia + i = presolve_validate(lp, rowmap, colmap); + if(i > 0) + report(lp, SEVERE, "presolve: %d internal consistency failure(s) detected\n", i); +#endif + presolve_finalize(lp, rowmap, colmap); + + /* Tighten MIP bound if possible (should ideally use some kind of smart heuristic) */ +#ifndef PostScale + if((MIP_count(lp) > 0) || (get_Lrows(lp) > 0)) { + if(is_maxim(lp)) + lp->bb_heuristicOF = MAX(lp->bb_heuristicOF, sumplumin(lp, 0, plulower, neglower)); + else + lp->bb_heuristicOF = MIN(lp->bb_heuristicOF, sumplumin(lp, 0, pluupper, negupper)); + } +#endif + + /* Report summary information */ + if(nv) + report(lp, NORMAL, "presolve: Removed %5d empty or fixed variables.\n", nv); + if(nb) + report(lp, NORMAL, "presolve: Converted %5d row singletons to variable bounds.\n", nb); + if(nt) + report(lp, NORMAL, "presolve: Tightened %5d other variable bounds.\n", nt); + if(nc) + report(lp, NORMAL, "presolve: Removed %5d empty or redundant constraints.\n", nc); + if(nr) + report(lp, NORMAL, "presolve: Tightened %5d constraint bounds.\n", nr); + + if(nc+nb+nt+nv+nr > 0) + report(lp, NORMAL, " \n"); + + /* Report optimality or infeasibility */ + if(sumplumin(lp, 0, pluupper,negupper)-sumplumin(lp, 0, plulower,neglower) < epsvalue) { + report(lp, NORMAL, "presolve: Identified optimal OF value %g\n", + sumplumin(lp, 0, pluupper,negupper)); +#ifndef PostScale + lp->bb_limitOF = sumplumin(lp, 0, pluupper,negupper); +#endif +#if 0 + status = OPTIMAL; +#endif + } + else if(status != RUNNING) + report(lp, NORMAL, "presolve: Infeasibility or unboundedness detected.\n"); + + /* Clean up */ + FREE(rowlength); + FREE(collength); + FREE(plucount); + FREE(negcount); + FREE(pluneg); + FREE(plulower); + FREE(neglower); + FREE(pluupper); + FREE(negupper); + + } + + /* Signal that we are done presolving */ + if((lp->usermessage != NULL) && + ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != 0) && (lp->msgmask & MSG_PRESOLVE)) + lp->usermessage(lp, lp->msghandle, MSG_PRESOLVE); + + /* Clean out empty SOS records */ + if(SOS_count(lp) > 0) { + clean_SOSgroup(lp->SOS); + if(lp->SOS->sos_count == 0) + free_SOSgroup(&(lp->SOS)); + } + + /* Create master SOS variable list */ + if(SOS_count(lp) > 0) + make_SOSchain(lp, (MYBOOL) ((lp->do_presolve & PRESOLVE_LASTMASKMODE) != PRESOLVE_NONE)); + + /* Crash the basis, if specified */ + crash_basis(lp); + +#ifdef PostScale + if(status == RUNNING) + lp_solve_auto_scale(lp); +#endif + +Finish: + lp->wasPresolved = TRUE; + +#if 0 + write_lp(lp, "test_out.lp"); /* Must put here due to variable name mapping */ +#endif +#if 0 + REPORT_debugdump(lp, "testint2.txt", FALSE); +#endif + + return( status ); + +} + Index: src/tools/solver/lp_solve/lp_presolve.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_presolve.h diff -N src/tools/solver/lp_solve/lp_presolve.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_presolve.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,17 @@ +#ifndef HEADER_lp_presolve +#define HEADER_lp_presolve + +#include "lp_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Put function headers here */ +STATIC int presolve(lprec *lp); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_presolve */ Index: src/tools/solver/lp_solve/lp_price.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_price.c diff -N src/tools/solver/lp_solve/lp_price.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_price.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1010 @@ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_price.h" +#include "lp_pricePSE.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +/* + Simplex pricing utility module - w/interface for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h, commonlib.h + + Release notes: + v1.0.0 1 July 2004 Routines extracted from lp_lib. + v1.0.1 10 July 2004 Added comparison operators for determination of + entering and leaving variables. + Added routines for multiple and partial pricing + and made corresponding changes to colprim and + rowdual. + ---------------------------------------------------------------------------------- +*/ + +/* Comparison operators for entering and leaving variables for both the primal and + dual simplexes. The functions compare a candidate variable with an incumbent + and can return the values: + + 1 for the candidate being "better", + -1 for the candidate being "worse", and + 0 for the candidate being "equal" to the incumbent. +*/ +int CMP_CALLMODEL compareImprovementVar(const pricerec *current, const pricerec *candidate) +{ + static REAL testvalue; + static int result; + static lprec *lp; + + result = 0; + lp = current->lp; + + if(lp->_piv_rule_ != PRICER_FIRSTINDEX) { + + /* Compute the ranking test metric. */ + testvalue = candidate->pivot - current->pivot; + if(current->isdual) + testvalue = -testvalue; + + /* Find the largest value */ + if(testvalue > lp->epsvalue) + result = 1; + else if (testvalue < -lp->epsmachine) + result = -1; + } + + /* Handle ties via index ordinal */ + if(result == 0) { + int varno = current->varno, candidatevarno = candidate->varno; + + if(current->isdual) { + candidatevarno = lp->var_basic[candidatevarno]; + varno = lp->var_basic[varno]; + } + if(candidatevarno < varno) + result = 1; + else if(candidatevarno > varno) + result = -1; + if(lp->_piv_left_) + result = -result; + } + + return( result ); + +} + +int CMP_CALLMODEL compareSubstitutionVar(pricerec *current, pricerec *candidate) +{ + static REAL testvalue, margin; + static int result; + static lprec *lp; + + result = 0; + lp = current->lp; + + /* Compute the ranking test metric. */ +#if 0 + testvalue = candidate->theta - current->theta; +#else + testvalue = my_reldiff(candidate->theta, current->theta); +#endif + + /* Find if the new Theta is smaller or near equal (i.e. testvalue <= eps) + compared to the previous best; ties will be broken by pivot size or index */ + margin = lp->epsvalue; + if(testvalue < -margin) + result = 1; + else if(testvalue > margin) + result = -1; + + /* Resolve a tie */ + if(result == 0) { + REAL pivot = fabs(current->pivot), candidatepivot = fabs(candidate->pivot); + + if(lp->_piv_rule_ == PRICER_FIRSTINDEX) { +#if 1 + /* Special secondary selection by pivot size (stability protection) */ + margin = lp->epspivot; + if((candidatepivot >= margin) && (pivot < margin)) + result = 1; +#endif + } + + else { + + /* General secondary selection based on pivot size */ + if(candidatepivot > pivot+margin) + result = 1; + else if(candidatepivot < pivot-margin) + result = -1; + } + + /* Tertiary selection by index value */ + if(result == 0) { + int varno = current->varno, candidatevarno = candidate->varno; + + if(!current->isdual) { + candidatevarno = lp->var_basic[candidatevarno]; + varno = lp->var_basic[varno]; + } + if(candidatevarno < varno) + result = 1; + else if(candidatevarno > varno) + result = -1; + if(lp->_piv_left_) + result = -result; + } + } + + return( result ); +} + +/* Validity operators for entering and leaving columns for both the primal and dual + simplex. All candidates must satisfy these tests to qualify to be allowed to be + a subject for the comparison functions/operators. */ +STATIC MYBOOL validImprovementVar(pricerec *candidate) +{ + REAL candidatepivot; + + if(candidate->isdual) + candidatepivot = -candidate->pivot; + else + candidatepivot = candidate->pivot; + +#ifdef Paranoia + return( (MYBOOL) ((candidate->varno > 0) && (candidatepivot > candidate->lp->epsvalue)) ); +#else + return( (MYBOOL) (candidatepivot > candidate->lp->epsvalue) ); +#endif +} + +STATIC MYBOOL validSubstitutionVar(pricerec *candidate) +{ + static lprec *lp; + + lp = candidate->lp; + +#ifdef Paranoia + if(candidate->varno <= 0) + return( FALSE ); + else +#endif + if(candidate->pivot >= lp->infinite) + return( (MYBOOL) (candidate->theta < lp->infinite) ); + else + return( (MYBOOL) ((candidate->theta < lp->infinite) && + (fabs(candidate->pivot) >= lp->epspivot)) ); +} + +/* Function to add a valid improvement candidate into the + sorted multiple price list at the proper location */ +STATIC int addImprovementVar(pricerec *candidate) +{ + int i, insertpos, delta; + lprec *lp = candidate->lp; + + /* Find the insertion point (if any) */ + if(lp->multiused > 0) { + delta = 1; + i = sizeof(*candidate); + insertpos = findIndexEx(candidate, lp->multivar-delta, lp->multiused, delta, i, + (findCompare_func *)compareImprovementVar); + if(insertpos > 0) + return( -1 ); + insertpos = -insertpos - delta; + } + else + insertpos = 0; + + /* Shift the existing data down to make space for the new record */ + if(insertpos < lp->multiused) { + delta = MIN(lp->multisize, lp->multiused) - insertpos; + if(delta > 0) + MEMMOVE(lp->multivar + insertpos + 1, lp->multivar + insertpos, delta); + } + + /* Insert the new record */ + if(insertpos >= 0) { + MEMCOPY(lp->multivar + insertpos, candidate, 1); + if(lp->multiused < lp->multisize) + lp->multiused++; + } + + return( insertpos ); +} + +STATIC MYBOOL findImprovementVar(pricerec *current, pricerec *candidate, MYBOOL collectMP) +/* PRIMAL: Find a variable to enter the basis + DUAL: Find a variable to leave the basis + + Allowed variable set: Any pivot PRIMAL:larger or DUAL:smaller than threshold value of 0 */ +{ + MYBOOL Accept, Action = FALSE; + + /* Check for validity and comparison result with previous best */ + Accept = validImprovementVar(candidate); + if(Accept) { + if(collectMP) + addImprovementVar(candidate); + if(current->varno > 0) + Accept = (MYBOOL) (compareImprovementVar(current, candidate) > 0); + } + + /* Apply candidate if accepted */ + if(Accept) { + (*current) = *candidate; + +#ifdef ForceEarlyBlandRule + /* Force immediate acceptance for Bland's rule using the primal simplex */ + if(!candidate->isdual) + Action = (MYBOOL) (candidate->lp->_piv_rule_ == PRICER_FIRSTINDEX); +#endif + } + return(Action); +} + +STATIC MYBOOL findSubstitutionVar(pricerec *current, pricerec *candidate) +/* PRIMAL: Find a variable to leave the basis + DUAL: Find a variable to enter the basis + + Allowed variable set: Equal-valued smallest thetas! */ +{ + MYBOOL Accept, Action = FALSE; + + /* Check for validity and comparison result with previous best */ + Accept = validSubstitutionVar(candidate); + if(Accept && (current->varno != 0)) + Accept = (MYBOOL) (compareSubstitutionVar(current, candidate) > 0); + + /* Apply candidate if accepted */ + if(Accept) { + (*current) = *candidate; + + /* Force immediate acceptance for Bland's rule using the dual simplex */ +#ifdef ForceEarlyBlandRule + if(candidate->isdual) + Action = (MYBOOL) (candidate->lp->_piv_rule_ == PRICER_FIRSTINDEX); +#endif + } + return(Action); +} + +/* Partial pricing management routines */ +STATIC partialrec *partial_createBlocks(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = (partialrec *) calloc(1, sizeof(*blockdata)); + blockdata->lp = lp; + blockdata->blockcount = 1; + blockdata->blocknow = 1; + blockdata->isrow = isrow; + + return(blockdata); +} +STATIC int partial_countBlocks(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + + if(blockdata == NULL) + return( 1 ); + else + return( blockdata->blockcount ); +} +STATIC int partial_activeBlocks(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + + if(blockdata == NULL) + return( 1 ); + else + return( blockdata->blocknow ); +} +STATIC void partial_freeBlocks(partialrec **blockdata) +{ + if((blockdata == NULL) || (*blockdata == NULL)) + return; + FREE((*blockdata)->blockend); + FREE((*blockdata)->blockpos); + FREE(*blockdata); +} + + +/* Function to provide for left-right or right-left scanning of entering/leaving + variables; note that *end must have been initialized by the calling routine! */ +STATIC void makePriceLoop(lprec *lp, int *start, int *end, int *delta) +{ + int offset = is_piv_mode(lp, PRICE_LOOPLEFT); + + if((offset) || +#if 0 + TRUE || +#endif + (((lp->total_iter+offset) % 2 == 0) && is_piv_mode(lp, PRICE_LOOPALTERNATE))) { + *delta = -1; /* Step backwards - "left" */ + swapINT(start, end); + lp->_piv_left_ = TRUE; + } + else { + *delta = 1; /* Step forwards - "right" */ + lp->_piv_left_ = FALSE; + } +} + +/* Find the primal simplex entering non-basic column variable */ +STATIC int colprim(lprec *lp, MYBOOL minit, REAL *drow, int *nzdrow) +{ + int i, ix, iy, iz; + REAL f; + pricerec current, candidate; + MYBOOL collectMP = FALSE; + int *multivars = NULL; + + /* Compute reduced costs c - c*Inv(B), if necessary + (i.e. the previous iteration was not a minor iteration) */ + if(!minit) { +#ifdef UsePrimalReducedCostUpdate + /* Recompute from scratch only at the beginning, otherwise update */ + if((lp->current_iter > 0) && !lp->justInverted) +#endif + compute_reducedcosts(lp, FALSE, 0, NULL, NULL, + drow, nzdrow); + } + + /* Identify pivot column according to pricing strategy; set + entering variable initial threshold reduced cost value to "0" */ + current.pivot = lp->epsprimal; /* Minimum acceptable improvement */ + current.varno = 0; + current.lp = lp; + current.isdual = FALSE; + candidate.lp = lp; + candidate.isdual = FALSE; + + /* Update local value of pivot setting and determine active multiple pricing mode */ + lp->_piv_rule_ = get_piv_rule(lp); + if(lp->multisize > 0) { + collectMP = lp->justInverted; + if(!collectMP) { + multivars = multi_createVarList(lp); + lp->multiused = 0; + ix = 1; + iy = lp->multiused; + goto doLoop; + } + } +#ifdef UseSparseReducedCost + ix = 1; + iy = nzdrow[0]; + + /* Loop over active partial row set; we presume that reduced costs + have only been updated for columns in the active partial range. */ +doLoop: + makePriceLoop(lp, &ix, &iy, &iz); + for(; ix*iz <= iy*iz; ix += iz) { + i = nzdrow[ix]; +#if 0 + if(i > lp->sum-abs(lp->Extrap)) + continue; +#endif + +#else + ix = partial_blockStart(lp, FALSE); + iy = partial_blockEnd(lp, FALSE); + if(iy == lp->sum) + iy -= abs(lp->Extrap); + + /* Loop over active partial row set */ +doLoop: + makePriceLoop(lp, &ix, &iy, &iz); + for(; ix*iz <= iy*iz; ix += iz) { + + /* Map loop variable to target */ + if(multivars == NULL) + i = ix; + else + i = multivars[ix]; + + /* Only scan non-basic variables */ + if(lp->is_basic[i]) + continue; + + /* Never pivot in fixed variables/equality slacks */ + if(lp->upbo[i] == 0) + continue; + +#endif + + /* Check if the pivot candidate is on the block-list */ + if(lp->rejectpivot[0] > 0) { + int k; + for(k = 1; (k <= lp->rejectpivot[0]) && (i != lp->rejectpivot[k]); k++); + if(k <= lp->rejectpivot[0]) + continue; + } + + /* Retrieve the applicable reduced cost */ + f = my_chsign(lp->is_lower[i], drow[i]); + if(f <= lp->epsprimal) /* Threshold should not be smaller than 0 */ + continue; + + /* Find entering variable according to strategy (largest positive f) */ + candidate.pivot = normalizeEdge(lp, i, f, FALSE); + candidate.varno = i; + if(findImprovementVar(¤t, &candidate, collectMP)) + break; + } + + /* Check if we should loop again after a multiple pricing update */ + if(multivars != NULL) { + FREE(multivars); + ix = partial_blockStart(lp, FALSE); + iy = partial_blockEnd(lp, FALSE); + if(iy == lp->sum) + iy -= abs(lp->Extrap); + if(lp->multiused+iy-ix < lp->multisize) + goto doLoop; + } + + if(lp->spx_trace) { + if(current.varno > 0) + report(lp, DETAILED, "col_prim: Column %d reduced cost = " MPSVALUEMASK "\n", + current.varno, current.pivot); + else + report(lp, DETAILED, "col_prim: No positive reduced costs found, optimality!\n"); + } + if(current.varno == 0) { + lp->doIterate = FALSE; + lp->doInvert = FALSE; + lp->spx_status = OPTIMAL; + } + + return(current.varno); +} /* colprim */ + +/* Find the primal simplex leaving basic column variable */ +STATIC int rowprim(lprec *lp, int colnr, LREAL *theta, REAL *pcol) +{ + int i, iy, iz, pass, k; + LREAL f, savef; + REAL Hscale, Hlimit; + pricerec current, candidate; + + /* Update local value of pivot setting */ + lp->_piv_rule_ = get_piv_rule(lp); + +#ifdef UseHarrisTwoPass + pass = 2; +#else + pass = 1; +#endif + current.theta = lp->infinite; + current.pivot = 0; + current.varno = 0; + current.isdual = FALSE; + current.lp = lp; + candidate.isdual = FALSE; + candidate.lp = lp; + savef = 0; + for(; pass > 0; pass--) { + if(pass == 2) { + Hlimit = lp->infinite; + Hscale = 1000; + } + else { + Hlimit = fabs(current.theta); + Hscale = 0; + } + current.theta = lp->infinite; + current.pivot = 0; + current.varno = 0; + savef = 0; + + i = 1; + iy = lp->rows; + makePriceLoop(lp, &i, &iy, &iz); + for(; i*iz <= iy*iz; i += iz) { + f = pcol[i]; + if(fabs(f) < lp->epspivot) { + if(lp->spx_trace) + report(lp, FULL, "rowprim: Pivot " MPSVALUEMASK " rejected, too small (limit " MPSVALUEMASK ")\n", + f, lp->epspivot); + } + else { /* Pivot possibly alright */ + candidate.theta = f; + candidate.pivot = f; + candidate.varno = i; + k = compute_theta(lp, i, &candidate.theta, Hscale, TRUE); + if(fabs(candidate.theta) >= lp->infinite) { + savef = f; + candidate.theta = 2*lp->infinite; + continue; + } + +#if 0 + /* Give priority to equality slack and fixed variable removal */ + if((lp->upbo[k] < lp->epsmachine) && + (candidate.pivot > 0.6 * MAX(current.pivot, lp->epspivot))) { + current = candidate; + break; + } +#endif + + /* Find the candidate leaving variable according to strategy (smallest theta) */ + if(fabs(candidate.theta) > Hlimit) + continue; + if(findSubstitutionVar(¤t, &candidate)) + break; + } + } + } + + /* Handle case of no available leaving variable */ + if(current.varno == 0) { + if(lp->upbo[colnr] >= lp->infinite) { + lp->doIterate = FALSE; + lp->doInvert = FALSE; + lp->spx_status = UNBOUNDED; + } + else { + i = 1; + while((pcol[i] >= 0) && (i <= lp->rows)) + i++; + if(i > lp->rows) { /* Empty column with upper bound! */ + lp->is_lower[colnr] = FALSE; + lp->rhs[0] += lp->upbo[colnr]*pcol[0]; + lp->doIterate = FALSE; + lp->doInvert = FALSE; + } + else /* if(pcol[i]<0) */ + { + current.varno = i; + } + } + } + else if(current.theta >= lp->infinite) { + report(lp, IMPORTANT, "rowprim: Numeric instability pcol[%d] = %g, rhs[%d] = %g, upbo = %g\n", + current.varno, savef, current.varno, lp->rhs[current.varno], + lp->upbo[lp->var_basic[current.varno]]); + } + + /* Flag that we can do a major iteration */ + if(current.varno > 0) + lp->doIterate = TRUE; + + if(lp->spx_trace) + report(lp, DETAILED, "row_prim: %d, pivot size = " MPSVALUEMASK "\n", + current.varno, pcol[current.varno]); + + *theta = current.theta; + return(current.varno); +} /* rowprim */ + +/* Find the dual simplex leaving basic variable */ +STATIC int rowdual(lprec *lp, MYBOOL eliminate) +{ + int k, i, iy, iz, ii; + REAL rh, up, epsvalue; + pricerec current, candidate; + MYBOOL collectMP = FALSE; + int *multivars = NULL; + + /* Initialize */ + epsvalue = lp->epsprimal; + current.pivot = +epsvalue; /* Initialize leaving variable threshold; "less than 0" */ + current.varno = 0; + current.isdual = TRUE; + current.lp = lp; + candidate.isdual = TRUE; + candidate.lp = lp; + + /* Update local value of pivot setting and determine active multiple pricing mode */ + lp->_piv_rule_ = get_piv_rule(lp); + if(lp->multisize > 0) { + collectMP = lp->justInverted; + if(!collectMP) { + multivars = multi_createVarList(lp); + lp->multiused = 0; + k = 1; + iy = lp->multiused; + goto doLoop; + } + } + k = partial_blockStart(lp, TRUE); + iy = partial_blockEnd(lp, TRUE); + + /* Loop over active partial row set */ +doLoop: + makePriceLoop(lp, &k, &iy, &iz); + for(; k*iz <= iy*iz; k += iz) { + + /* Map loop variable to target */ + if(multivars == NULL) + i = k; + else + i = multivars[k]; + + /* Set local variables */ + ii = lp->var_basic[i]; + up = lp->upbo[ii]; + rh = lp->rhs[i]; + + /* Analyze relevant constraints ... + KE version skips uninteresting alternatives and gives a noticeable speedup */ + if((rh < 0) || (rh > up)) { + if(rh > up) + rh = up-rh; + + /* Give a slight preference to fixed variables (mainly equality slacks) */ + if(up < epsvalue) { + rh *= 1.0+lp->epspivot; + /* Give an extra boost to equality slack elimination, if specified */ + if(eliminate) + rh *= 10.0; + } + + /* Select leaving variable according to strategy (the most negative g/largest violation) */ + candidate.pivot = normalizeEdge(lp, i, rh, TRUE); + candidate.varno = i; + if(findImprovementVar(¤t, &candidate, collectMP)) + break; + } + } + + /* Check if we should loop again after a multiple pricing update */ + if(multivars != NULL) { + FREE(multivars); + k = partial_blockStart(lp, TRUE); + iy = partial_blockEnd(lp, TRUE); + if(lp->multiused+iy-k < lp->multisize) + goto doLoop; + } + + /* Produce statistics */ + if(lp->spx_trace) { + if(current.varno > 0) { + report(lp, DETAILED, "row_dual: rhs[%d] = " MPSVALUEMASK "\n", + current.varno, lp->rhs[current.varno]); + if(lp->upbo[lp->var_basic[current.varno]] < lp->infinite) + report(lp, DETAILED, "\t\tupper bound of is_basic variable: %18g\n", + lp->upbo[lp->var_basic[current.varno]]); + } + else + report(lp, FULL, "rowdual: No infeasibilities found\n"); + } + + return(current.varno); +} /* rowdual */ + +/* Find the dual simplex entering non-basic variable */ +STATIC int coldual(lprec *lp, int row_nr, MYBOOL minit, REAL *prow, int *nzprow, + REAL *drow, int *nzdrow) +{ + int i, iy, iz, k; +#ifdef UseSparseReducedCost + int ix; +#endif + LREAL w, g, quot; + REAL epsvalue = lp->epsprimal; + pricerec current, candidate; + + /* Initialize */ + current.theta = lp->infinite; + current.pivot = 0; + current.varno = 0; + current.isdual = TRUE; + current.lp = lp; + candidate.isdual = TRUE; + candidate.lp = lp; + + /* Determine the current loading state of the outgoing variable; note that the + basic variables are always lower-bounded, by lp_solve convention */ + w = lp->rhs[row_nr]; + g = -lp->upbo[lp->var_basic[row_nr]]; + if(g > -lp->infinite) + g += w; + my_roundzero(w, epsvalue); + my_roundzero(g, epsvalue); + if(g >= 0) /* The leaving variable is at or above its upper bound */ + g = -1; + else if(w <= 0) /* The leaving variable is at or below its lower bound */ + g = 1; + else { /* Check for accumulation of numerical errors */ + g = 0; + report(lp, DETAILED, "coldual: Leaving variable %d does not violate bounds (minit=%s)!\n", + row_nr, my_boolstr(minit)); +#ifndef FixInaccurateDualMinit + report(lp, DETAILED, " Try to enable FixInaccurateDualMinit compiler directive.\n"); +#endif + return( 0 ); + } + + /* Update local value of pivot setting */ + lp->_piv_rule_ = get_piv_rule(lp); + + /* Compute reduced costs */ + if(!minit) { +#ifdef UseDualReducedCostUpdate + /* Recompute from scratch only at the beginning, otherwise update */ + if((lp->current_iter > 0) && !lp->justInverted) + compute_reducedcosts(lp, TRUE, row_nr, prow, nzprow, + NULL, NULL); + else +#endif + compute_reducedcosts(lp, TRUE, row_nr, prow, nzprow, + drow, nzdrow); + } + + /* Loop over all entering column candidates */ +#ifdef UseSparseReducedCost + ix = 1; + iy = nzprow[0]; + makePriceLoop(lp, &ix, &iy, &iz); + for(; ix*iz <= iy*iz; ix += iz) { + i = nzprow[ix]; + +#else + i = 1; + iy = lp->sum; + makePriceLoop(lp, &i, &iy, &iz); + for(; i*iz <= iy*iz; i += iz) { + + /* We are only looking for non-basic, non-equality slack / non-fixed variables to enter */ + if(lp->is_basic[i] || (fabs(lp->upbo[i]) < epsvalue)) + continue; + +#endif + + /* Check if the pivot candidate is on the block-list */ + for(k = 1; (k <= lp->rejectpivot[0]) && (i != lp->rejectpivot[k]); k++); + if(k <= lp->rejectpivot[0]) + continue; + + /* We have a candidate; check if it is good enough and better than the last; + note that prow = w and drow = cbar in Chvatal's "nomenclatura" */ + w = prow[i]; + if(lp->is_lower[i]) + w *= g; + else + w *= -g; + + if(w > -lp->epspivot) { + if(lp->spx_trace) + report(lp, FULL, "coldual: Pivot %g rejected as too small (limit=%g)\n", + (double)w, (double)lp->epspivot); + } + else { + quot = drow[i]; + if(lp->is_lower[i]) + quot /= -w; + else + quot /= w; + + /* Apply the selected pivot strategy (smallest theta) */ +#if 1 + quot = fabs(quot); /* *** Should validate and test if this is necessary - KE */ +#else + if(quot < 0) + continue; +#endif + candidate.theta = quot; + candidate.pivot = w; + candidate.varno = i; + if(findSubstitutionVar(¤t, &candidate)) + break; + } + } + + i = current.varno; + if(lp->spx_trace) + report(lp, NORMAL, "col_dual: Entering column %d, reduced cost %g, pivot element value %g\n", + i, drow[i], prow[i]); + + return( i ); +} /* coldual */ + + +STATIC REAL normalizeEdge(lprec *lp, int item, REAL edge, MYBOOL isdual) +{ + + edge /= getPricer(lp, item, isdual); +#ifdef EnableRandomizedPricing + if(is_piv_strategy(lp, PRICE_RANDOMIZE)) + edge *= (1.0-PRICER_RANDFACT) + PRICER_RANDFACT*(rand() % RANDSCALE) / (REAL) RANDSCALE; +#endif + return( edge ); + +} + +/* Support routines for block detection and partial pricing */ +STATIC int partial_findBlocks(lprec *lp, MYBOOL autodefine, MYBOOL isrow) +{ + int i, jj, n, nb, ne, items; + REAL hold, biggest, *sum = NULL; + MATrec *mat = lp->matA; + partialrec *blockdata; + + if(!mat_validate(mat)) + return( 1 ); + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + items = IF(isrow, lp->rows, lp->columns); + allocREAL(lp, &sum, items+1, FALSE); + + /* Loop over items and compute the average column index for each */ + sum[0] = 0; + for(i = 1; i <= items; i++) { + n = 0; + if(isrow) { + nb = mat->row_end[i-1]; + ne = mat->row_end[i]; + } + else { + nb = mat->col_end[i-1]; + ne = mat->col_end[i]; + } + n = ne-nb; + sum[i] = 0; + if(n > 0) { + if(isrow) + for(jj = nb; jj < ne; jj++) + sum[i] += ROW_MAT_COL(jj); + else + for(jj = nb; jj < ne; jj++) + sum[i] += COL_MAT_ROW(mat->col_mat[jj]); + sum[i] /= n; + } + else + sum[i] = sum[i-1]; + } + + /* Loop over items again, find largest difference and make monotone */ + hold = 0; + biggest = 0; + for(i = 2; i <= items; i++) { + hold = sum[i] - sum[i-1]; + if(hold > 0) { + if(hold > biggest) + biggest = hold; + } + else + hold = 0; + sum[i-1] = hold; + } + + /* Loop over items again and find differences exceeding threshold; + the discriminatory power of this routine depends strongly on the + magnitude of the scaling factor - from empirical evidence > 0.9 */ + biggest = MAX(1, 0.9*biggest); + n = 0; + nb = 0; + ne = 0; + for(i = 1; i < items; i++) + if(sum[i] > biggest) { + ne += i-nb; /* Compute sum of index gaps between maxima */ + nb = i; + n++; /* Increment count */ + } + + /* Clean up */ + FREE(sum); + + /* Require that the maxima are spread "nicely" across the columns, + otherwise return that there is only one monolithic block. + (This is probably an area for improvement in the logic!) */ + if(n > 0) { + ne /= n; /* Average index gap between maxima */ + i = IF(isrow, lp->columns, lp->rows); + nb = i / ne; /* Another estimated block count */ + if(abs(nb - n) > 2) /* Probably Ok to require equality (nb==n)*/ + n = 1; + else if(autodefine) /* Generate row/column break-indeces for partial pricing */ + set_partialprice(lp, nb, NULL, isrow); + } + else + n = 1; + + return( n ); +} +STATIC int partial_blockStart(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + if(blockdata == NULL) + return( 1 ); + else + return( blockdata->blockend[blockdata->blocknow-1] ); +} +STATIC int partial_blockEnd(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + if(blockdata == NULL) + return( IF(isrow, lp->rows, lp->sum) ); + else + return( blockdata->blockend[blockdata->blocknow]-1 ); +} +STATIC int partial_blockNextPos(lprec *lp, int block, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); +#ifdef Paranoia + if((blockdata == NULL) || (block <= 1) || (block > blockdata->blockcount)) { + report(lp, SEVERE, "partial_blockNextPos: Invalid block %d specified.\n", + block); + return( -1 ); + } +#endif + block--; + if(blockdata->blockpos[block] == blockdata->blockend[block+1]) + blockdata->blockpos[block] = blockdata->blockend[block]; + else + blockdata->blockpos[block]++; + return( blockdata->blockpos[block] ); +} +STATIC MYBOOL partial_blockStep(lprec *lp, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + if(blockdata == NULL) + return( FALSE ); + else if(blockdata->blocknow < blockdata->blockcount) { + blockdata->blocknow++; + return( FALSE); + } + else { + blockdata->blocknow = 1; + return( TRUE ); + } +} +STATIC MYBOOL partial_isVarActive(lprec *lp, int varno, MYBOOL isrow) +{ + partialrec *blockdata; + + blockdata = IF(isrow, lp->rowblocks, lp->colblocks); + if(blockdata == NULL) + return( TRUE ); + else { + return( (MYBOOL) ((varno >= blockdata->blockend[blockdata->blocknow-1]) && + (varno < blockdata->blockend[blockdata->blocknow])) ); + } +} +STATIC MYBOOL multi_prepareVector(lprec *lp, int blocksize) +{ + if((blocksize > 1) && (lp->multiblockdiv > 1)) { + lp->multisize = blocksize; + lp->multisize += lp->multisize/lp->multiblockdiv; + lp->multivar = (pricerec *) realloc(lp->multivar, (lp->multisize+1)*sizeof(*(lp->multivar))); + } + else { + lp->multisize = 0; + FREE(lp->multivar); + } + lp->multinow = 1; + + return( TRUE ); +} + +STATIC int *multi_createVarList(lprec *lp) +{ + int *list = NULL; + + if((lp->multiused > 0) && allocINT(lp, &list, lp->multiused+1, FALSE)) { + int i; + + list[0] = lp->multiused; + for(i = 1; i <= lp->multiused; i++) + list[i] = lp->multivar[i-1].varno; + } + return( list ); +} + Index: src/tools/solver/lp_solve/lp_price.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_price.h diff -N src/tools/solver/lp_solve/lp_price.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_price.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,64 @@ +#ifndef HEADER_lp_price +#define HEADER_lp_price + +#include "lp_types.h" + +/* Define pivot definition */ +/* +typedef struct _pricerec +{ + REAL theta; + REAL pivot; + int varno; + lprec *lp; + MYBOOL isdual; +} pricerec; +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Comparison and validity routines */ +int CMP_CALLMODEL compareImprovementVar(const pricerec *current, const pricerec *candidate); +int CMP_CALLMODEL compareSubstitutionVar(pricerec *current, pricerec *candidate); +STATIC MYBOOL validImprovementVar(pricerec *candidate); +STATIC MYBOOL validSubstitutionVar(pricerec *candidate); + +/* Row+column selection routines */ +STATIC int addImprovementVar(pricerec *candidate); +STATIC MYBOOL findImprovementVar(pricerec *current, pricerec *candidate, MYBOOL collectMP); +STATIC MYBOOL findSubstitutionVar(pricerec *current, pricerec *candidate); +STATIC REAL normalizeEdge(lprec *lp, int item, REAL edge, MYBOOL isdual); +STATIC void makePriceLoop(lprec *lp, int *start, int *end, int *delta); + +/* Leaving variable selection and entering column pricing loops */ +STATIC int colprim(lprec *lp, MYBOOL minit, REAL *drow, int *nzdrow); +STATIC int rowprim(lprec *lp, int colnr, LREAL *theta, REAL *pcol); +STATIC int rowdual(lprec *lp, MYBOOL eliminate); +STATIC int coldual(lprec *lp, int row_nr, MYBOOL minit, REAL *prow, int *nzprow, + REAL *drow, int *nzdrow); + +/* Partial pricing management routines */ +STATIC partialrec *partial_createBlocks(lprec *lp, MYBOOL isrow); +STATIC int partial_countBlocks(lprec *lp, MYBOOL isrow); +STATIC int partial_activeBlocks(lprec *lp, MYBOOL isrow); +STATIC void partial_freeBlocks(partialrec **blockdata); + +/* Partial and multiple pricing utility routines */ +STATIC int partial_findBlocks(lprec *lp, MYBOOL autodefine, MYBOOL isrow); +STATIC int partial_blockStart(lprec *lp, MYBOOL isrow); +STATIC int partial_blockEnd(lprec *lp, MYBOOL isrow); +STATIC int partial_blockNextPos(lprec *lp, int block, MYBOOL isrow); + +STATIC MYBOOL partial_blockStep(lprec *lp, MYBOOL isrow); +STATIC MYBOOL partial_isVarActive(lprec *lp, int varno, MYBOOL isrow); + +STATIC MYBOOL multi_prepareVector(lprec *lp, int blocksize); +STATIC int *multi_createVarList(lprec *lp); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_price */ Index: src/tools/solver/lp_solve/lp_pricePSE.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_pricePSE.c diff -N src/tools/solver/lp_solve/lp_pricePSE.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_pricePSE.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,492 @@ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_pricePSE.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* + Advanced simplex price scaling modules - w/interface for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h + + Release notes: + v1.0.0 1 September 2003 Implementation of DEVEX and STEEPEST EDGE + routines for the primal and dual simplex. + v1.0.1 1 January 2004 Made initial value of weight of ingoing + variable for the standard mode of DEVEX + consistent with the initialization at restart; + original version could at worst contribute + to cycling. + v1.0.2 23 March 2004 Added floors to Steepest Edge updates and + moved tests for tiny update higher. Previous + logic can be simulated by disabling the compiler + define ApplySteepestEdgeMinimum. + v1.1.0 1 July 2004 Renamed from lp_pricerPSE to lp_pricePSE in + conjuction with the creation of a separate + price library. + + ---------------------------------------------------------------------------------- +*/ + +STATIC MYBOOL applyPricer(lprec *lp) +{ + int rule = get_piv_rule(lp); + return( (MYBOOL) ((rule == PRICER_DEVEX) || (rule == PRICER_STEEPESTEDGE)) ); +} + + +STATIC void simplexPricer(lprec *lp, MYBOOL isdual) +{ + if(lp->edgeVector != NULL) + lp->edgeVector[0] = (REAL) isdual; +} + + +STATIC void freePricer(lprec *lp) +{ + if(lp->edgeVector != NULL) { + FREE(lp->edgeVector); + } +} + + +STATIC void resizePricer(lprec *lp) +{ + if(!applyPricer(lp)) + return; + + /* Reallocate vector for new size */ + lp->edgeVector = (REAL *) realloc(lp->edgeVector, (lp->sum_alloc + 1) * sizeof(*lp->edgeVector)); + + /* Signal that we have not yet initialized the price vector */ + MEMCLEAR(lp->edgeVector, lp->sum_alloc+1); + lp->edgeVector[0] = -1; +} + + +STATIC void initPricer(lprec *lp) +{ + if(!applyPricer(lp)) + return; + + /* Free any pre-existing pricer */ + freePricer(lp); + /* Allocate vector to fit current problem size */ + resizePricer(lp); +} + + +STATIC REAL getPricer(lprec *lp, int item, MYBOOL isdual) +{ + REAL value = 1.0; + + if(!applyPricer(lp)) + return( value ); + + value = *lp->edgeVector; + + /* Make sure we have a price vector to use */ + if(value < 0) { +#ifdef Paranoia + report(lp, SEVERE, "getPricer: Called without having being initialized!\n"); +#endif + return( 1.0 ); + } + /* We may be calling the primal from the dual (and vice-versa) for validation + of feasibility; ignore calling origin and simply return 1 */ + else if(isdual != value) { + return( 1.0 ); + } + /* Do the normal norm retrieval */ + else { + + if(isdual) + item = lp->var_basic[item]; + + value = lp->edgeVector[item]; + + if(value == 0) { + value = 1.0; + report(lp, SEVERE, "getPricer: Detected a zero-valued price at index %d\n", item); + } +#ifdef Paranoia + else if(value < 0) + report(lp, SEVERE, "getPricer: Invalid %s reduced cost norm %g at index %d\n", + my_if(isdual, "dual", "primal"), value, item); +#endif + + /* Return the norm */ + return( sqrt(value) ); + } +} + +STATIC void restartPricer(lprec *lp, MYBOOL isdual) +{ + REAL *sEdge, seNorm, hold; + int i, j, m; + MYBOOL isDEVEX; + + if(!applyPricer(lp)) + return; + + /* Store the active/current pricing type */ + if(isdual == AUTOMATIC) + isdual = (MYBOOL) lp->edgeVector[0]; + else + lp->edgeVector[0] = isdual; + + m = lp->rows; + + /* Determine strategy and check if we have strategy fallback for the primal */ + isDEVEX = is_piv_rule(lp, PRICER_DEVEX); + if(!isDEVEX && !isdual) + isDEVEX = is_piv_mode(lp, PRICE_PRIMALFALLBACK); + + /* Check if we only need to do the simple DEVEX initialization */ + if(isDEVEX && !DEVEX_ENHANCED) { + if(isdual) { + for(i = 1; i <= m; i++) + lp->edgeVector[lp->var_basic[i]] = 1.0; + } + else { + for(i = 1; i <= lp->sum; i++) + if(!lp->is_basic[i]) + lp->edgeVector[i] = 1.0; + } + return; + } + + /* Otherwise do the full Steepest Edge norm initialization */ + sEdge = (REAL *) malloc((m + 1) * sizeof(*sEdge)); + + if(isdual) { + + /* Extract the rows of the basis inverse and compute their squared norms */ + + for(i = 1; i <= m; i++) { + + bsolve(lp, i, sEdge, NULL, 0, 0.0); + + /* Compute the edge norm */ + seNorm = 0; + for(j = 1; j <= m; j++) { + hold = sEdge[j]; + seNorm += hold*hold; + } + + j = lp->var_basic[i]; + lp->edgeVector[j] = seNorm; + } + + } + else { + + /* Solve a=Bb for b over all non-basic variables and compute their squared norms */ + + for(i = 1; i <= lp->sum; i++) { + if(lp->is_basic[i]) + continue; + + fsolve(lp, i, sEdge, NULL, 0, 0.0, FALSE); + + /* Compute the edge norm */ + seNorm = 1; + for(j = 1; j <= m; j++) { + hold = sEdge[j]; + seNorm += hold*hold; + } + + lp->edgeVector[i] = seNorm; + } + + } + + free(sEdge); + +} + + +STATIC void formWeights(lprec *lp, int colnr, REAL *pcol, REAL **w) +/* This computes Bw = a, where B is the basis and a is a column of A */ +{ + allocREAL(lp, w, lp->rows+1, FALSE); + if(pcol == NULL) + fsolve(lp, colnr, *w, NULL, 0.0, 0.0, FALSE); + else { + MEMCOPY(*w, pcol, lp->rows+1); +/* *w[0] = 0; */ /* Test */ + } +/* + if(pcol != NULL) { + REAL cEdge, hold; + int i; + + cEdge = 0; + for(i = 1; i <= m; i++) { + hold = *w[i]-pcol[i]; + cEdge += hold*hold; + } + cEdge /= m; + cEdge = sqrt(cEdge); + if(cEdge > lp->epspivot) + report(lp, SEVERE, "updatePricer: MRS error is %g\n", cEdge); + } +*/ +} +STATIC void freeWeights(REAL *w) +{ + FREE(w); +} + + +STATIC void updatePricer(lprec *lp, int rownr, int colnr, REAL *pcol, REAL *prow, int *nzprow) +{ + REAL *vEdge = NULL, cEdge, hold, *newEdge, *w = NULL; + int i, m, n, exitcol, errlevel = DETAILED; + MYBOOL forceRefresh = FALSE, isDual, isDEVEX; + + if(!applyPricer(lp)) + return; + + /* Make sure we have something to update */ + hold = lp->edgeVector[0]; + if(hold < 0) + return; + isDual = (MYBOOL) (hold > 0); + + /* Do common initializations and computations */ + m = lp->rows; + n = lp->sum; + isDEVEX = is_piv_rule(lp, PRICER_DEVEX); + exitcol = lp->var_basic[rownr]; + + /* Solve/copy Bw = a */ +/* formWeights(lp, colnr, NULL, &w); Experimental */ + formWeights(lp, colnr, pcol, &w); + + /* Price norms for the dual simplex - the basic columns */ + if(isDual) { + REAL rw; + int targetcol; + + /* Don't need to compute cross-products with DEVEX */ + if(!isDEVEX) { + allocREAL(lp, &vEdge, m+1, FALSE); + + /* Extract the row of the inverse containing the leaving variable + and then form the dot products against the other variables, i.e. "Tau" */ +#if 0 /* Extract row explicitly */ + bsolve(lp, rownr, vEdge, 0, 0.0); +#else /* Reuse previously extracted row data */ + MEMCOPY(vEdge, prow, m+1); + vEdge[0] = 0; +#endif + lp->bfp_ftran_normal(lp, vEdge, NULL); + } + + /* Deal with the variable entering the basis to become a new leaving candidate */ + cEdge = lp->edgeVector[exitcol]; + rw = w[rownr]; + hold = 1 / rw; + lp->edgeVector[colnr] = (hold*hold) * cEdge; + + /* Possibly adjust initial value in case of Devex */ + if(isDEVEX && !DEVEX_ENHANCED && (lp->edgeVector[colnr] < DEVEX_MINVALUE)) + lp->edgeVector[colnr] = DEVEX_MINVALUE; + +#ifdef Paranoia + if(lp->edgeVector[colnr] <= lp->epsmachine) + report(lp, errlevel, "updatePricer: Invalid dual norm %g at entering index %d - iteration %d\n", + lp->edgeVector[colnr], rownr, lp->total_iter+lp->current_iter); +#endif + + /* Then loop over all basic variables, but skip the leaving row */ + for(i = 1; i <= m; i++) { + if(i == rownr) + continue; + targetcol = lp->var_basic[i]; + hold = w[i]; + if(hold == 0) + continue; + hold /= rw; + if(fabs(hold) < lp->epsmachine) + continue; + + newEdge = &(lp->edgeVector[targetcol]); + *newEdge += (hold*hold) * cEdge; + if(isDEVEX) { + if((*newEdge) > DEVEX_RESTARTLIMIT) { + forceRefresh = TRUE; + break; + } + } + else { + *newEdge -= 2*hold*vEdge[i]; +#ifdef xxApplySteepestEdgeMinimum + *newEdge = my_max(*newEdge, hold*hold+1); /* Kludge; use the primal lower bound */ +#else + if(*newEdge <= 0) { + report(lp, errlevel, "updatePricer: Invalid dual norm %g at index %d - iteration %d\n", + *newEdge, i, lp->total_iter+lp->current_iter); + forceRefresh = TRUE; + break; + } +#endif + } + } + + + } + /* Price norms for the primal simplex - the non-basic columns */ + else { + + REAL *vTemp, *vAlpha, cAlpha; + int varScope = SCAN_ALLVARS+USE_NONBASICVARS; + + allocREAL(lp, &vTemp, m+1, TRUE); + allocREAL(lp, &vAlpha, n+1, TRUE); + + /* Check if we have strategy fallback for the primal */ + if(!isDEVEX) + isDEVEX = is_piv_mode(lp, PRICE_PRIMALFALLBACK); + + /* Don't need to compute cross-products with DEVEX */ + if(!isDEVEX) { + vEdge = (REAL *) calloc((n + 1), sizeof(*vEdge)); + + /* Compute v and then N'v */ + MEMCOPY(vTemp, w, m+1); + bsolve(lp, -1, vTemp, NULL, lp->epsmachine*DOUBLEROUND, 0.0); + vTemp[0] = 0; + prod_xA(lp, varScope, vTemp, NULL, XRESULT_FREE, lp->epsmachine, 0.0, + vEdge, NULL); + } + + /* Compute Sigma and then Alpha */ + bsolve(lp, rownr, vTemp, NULL, 0*DOUBLEROUND, 0.0); + vTemp[0] = 0; + prod_xA(lp, varScope, vTemp, NULL, XRESULT_FREE, lp->epsmachine, 0.0, + vAlpha, NULL); + + /* Update the squared steepest edge norms; first store some constants */ + cEdge = lp->edgeVector[colnr]; + cAlpha = vAlpha[colnr]; + + /* Deal with the variable leaving the basis to become a new entry candidate */ + hold = 1 / cAlpha; + lp->edgeVector[exitcol] = (hold*hold) * cEdge; + + /* Possibly adjust initial value in case of Devex */ + if(isDEVEX && !DEVEX_ENHANCED && (lp->edgeVector[exitcol] < DEVEX_MINVALUE)) + lp->edgeVector[exitcol] = DEVEX_MINVALUE; + +#ifdef Paranoia + if(lp->edgeVector[exitcol] <= lp->epsmachine) + report(lp, errlevel, "updatePricer: Invalid primal norm %g at leaving index %d - iteration %d\n", + lp->edgeVector[exitcol], exitcol, lp->total_iter+lp->current_iter); +#endif + + /* Then loop over all non-basic variables, but skip the entering column */ + for(i = 1; i <= lp->sum; i++) { + if(lp->is_basic[i] || (i == colnr)) + continue; + hold = vAlpha[i]; + if(hold == 0) + continue; + hold /= cAlpha; + if(fabs(hold) < lp->epsmachine) + continue; + + newEdge = &(lp->edgeVector[i]); + *newEdge += (hold*hold) * cEdge; + if(isDEVEX) { + if((*newEdge) > DEVEX_RESTARTLIMIT) { + forceRefresh = TRUE; + break; + } + } + else { + *newEdge -= 2*hold*vEdge[i]; +#ifdef ApplySteepestEdgeMinimum + *newEdge = my_max(*newEdge, hold*hold+1); +#else + if(*newEdge < 0) { + report(lp, errlevel, "updatePricer: Invalid primal norm %g at index %d - iteration %d\n", + *newEdge, i, lp->total_iter+lp->current_iter); + if(lp->spx_trace) + report(lp, errlevel, "Error detail: (RelAlpha=%g, vEdge=%g, cEdge=%g)\n", hold, vEdge[i], cEdge); + forceRefresh = TRUE; + break; + } +#endif + } + } + + FREE(vAlpha); + FREE(vTemp); + + } + + if(vEdge != NULL) + FREE(vEdge); + freeWeights(w); + + if(forceRefresh) + restartPricer(lp, AUTOMATIC); + +} + + +STATIC void verifyPricer(lprec *lp) +{ + REAL value; + int i, n; + + if(!applyPricer(lp)) + return; + + /* Verify */ + if(lp->edgeVector == NULL) + return; + value = *lp->edgeVector; + if(value < 0) + return; + + /* Check the primal */ + n = 1; + if(value == 0) { + + for(n = lp->sum; n > 0; n--) { + if(lp->is_basic[n]) + continue; + value = lp->edgeVector[n]; + if(value <= 0) + break; + } + } + /* Check the dual */ + else { + for(i = lp->rows; i > 0; i--) { + n = lp->var_basic[i]; + value = lp->edgeVector[n]; + if(value <= 0) + break; + } + } + +#ifdef Paranoia + if(n > 0) + report(lp, SEVERE, "verifyPricer: Invalid norm %g at index %d\n", + value, n); +#endif +} + Index: src/tools/solver/lp_solve/lp_pricePSE.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_pricePSE.h diff -N src/tools/solver/lp_solve/lp_pricePSE.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_pricePSE.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,27 @@ +#ifndef HEADER_lp_pricePSE +#define HEADER_lp_pricePSE + +#include "lp_types.h" + +#define ApplySteepestEdgeMinimum + +#ifdef __cplusplus +extern "C" { +#endif + +/* Price norm management routines */ +STATIC void initPricer(lprec *lp); +STATIC MYBOOL applyPricer(lprec *lp); +STATIC void simplexPricer(lprec *lp, MYBOOL isdual); +STATIC void freePricer(lprec *lp); +STATIC void resizePricer(lprec *lp); +STATIC REAL getPricer(lprec *lp, int item, MYBOOL isdual); +STATIC void restartPricer(lprec *lp, MYBOOL isdual); +STATIC void updatePricer(lprec *lp, int rownr, int colnr, REAL *pcol, REAL *prow, int *nzprow); +STATIC void verifyPricer(lprec *lp); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_pricePSE */ Index: src/tools/solver/lp_solve/lp_report.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_report.c diff -N src/tools/solver/lp_solve/lp_report.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_report.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,599 @@ + +/* + Mixed integer programming optimization drivers for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Michel Berkelaar (to lp_solve v3.2), + Kjell Eikland + Contact: + License terms: LGPL. + + Requires: stdarg.h, lp_lib.h + + Release notes: + v5.0.0 31 January 2004 New unit isolating B&B routines. + v5.1.0 01 February 2004 Complete rewrite into non-recursive version. + + ---------------------------------------------------------------------------------- +*/ + +#include +#include +#include "lp_lib.h" +#include "commonlib.h" +#include "lp_report.h" + + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +#if defined _MSC_VER +# define vsnprintf _vsnprintf +#endif + +/* Various reporting functions for lp_solve */ +/* ------------------------------------------------------------------------- */ + +/* First define general utilties for reporting and output */ +void __WINAPI report(lprec *lp, int level, const char *format, ...) +{ + static char buff[DEF_STRBUFSIZE+1]; + static va_list ap; + + if(lp == NULL) { + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + } + else if(level <= lp->verbose) { + va_start(ap, format); + if(lp->writelog != NULL) { + vsnprintf(buff, DEF_STRBUFSIZE, format, ap); + lp->writelog(lp, lp->loghandle, buff); + } + if(lp->outstream != NULL) { + vfprintf(lp->outstream, format, ap); + if(lp->outstream != stdout) + fflush(lp->outstream); + } + va_end(ap); + } +#ifdef xParanoia + if(level == CRITICAL) + raise(SIGSEGV); +#endif +} + +STATIC void print_indent(lprec *lp) +{ + int i; + + report(lp, NEUTRAL, "%2d", lp->bb_level); + if(lp->bb_level < 50) /* useless otherwise */ + for(i = lp->bb_level; i > 0; i--) + report(lp, NEUTRAL, "--"); + else + report(lp, NEUTRAL, " *** too deep ***"); + report(lp, NEUTRAL, "> "); +} /* print_indent */ + +STATIC void debug_print(lprec *lp, char *format, ...) +{ + va_list ap; + + if(lp->bb_trace) { + print_indent(lp); + va_start(ap, format); + if (lp == NULL) + { + vfprintf(stderr, format, ap); + fputc('\n', stderr); + } + else if(lp->debuginfo != NULL) + { + char buff[DEF_STRBUFSIZE+1]; + vsnprintf(buff, DEF_STRBUFSIZE, format, ap); + lp->debuginfo(lp, lp->loghandle, buff); + } + va_end(ap); + } +} /* debug_print */ + +STATIC void debug_print_solution(lprec *lp) +{ + int i; + + if(lp->bb_trace) + for (i = lp->rows + 1; i <= lp->sum; i++) { + print_indent(lp); + report(lp, NEUTRAL, "%s " MPSVALUEMASK "\n", + get_col_name(lp, i - lp->rows), + (double)lp->solution[i]); + } +} /* debug_print_solution */ + +STATIC void debug_print_bounds(lprec *lp, REAL *upbo, REAL *lowbo) +{ + int i; + + if(lp->bb_trace) + for(i = lp->rows + 1; i <= lp->sum; i++) { + if(lowbo[i] == upbo[i]) { + print_indent(lp); + report(lp, NEUTRAL, "%s = " MPSVALUEMASK "\n", get_col_name(lp, i - lp->rows), + (double)lowbo[i]); + } + else { + if(lowbo[i] != 0) { + print_indent(lp); + report(lp, NEUTRAL, "%s > " MPSVALUEMASK "\n", get_col_name(lp, i - lp->rows), + (double)lowbo[i]); + } + if(upbo[i] != lp->infinite) { + print_indent(lp); + report(lp, NEUTRAL, "%s < " MPSVALUEMASK "\n", get_col_name(lp, i - lp->rows), + (double)upbo[i]); + } + } + } +} /* debug_print_bounds */ + +/* List a vector of LREAL values for the given index range */ +void blockWriteLREAL(FILE *output, char *label, LREAL *vector, int first, int last) +{ + int i, k = 0; + + fprintf(output, label); + fprintf(output, "\n"); + for(i = first; i <= last; i++) { + fprintf(output, " %18g", vector[i]); + k++; + if(my_mod(k, 4) == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) + fprintf(output, "\n"); +} + +/* List the current user data matrix columns over the selected row range */ +void blockWriteAMAT(FILE *output, const char *label, lprec* lp, int first, int last) +{ + int i, j, k = 0; + int nzb, nze, jb; + double hold; + MATrec *mat = lp->matA; + + if(!mat_validate(mat)) + return; + if(first < 0) + first = 0; + if(last < 0) + last = lp->rows; + + fprintf(output, label); + fprintf(output, "\n"); + + if(first == 0) + nze = 0; + else + nze = mat->row_end[first-1]; + for(i = first; i <= last; i++) { + nzb = nze; + nze = mat->row_end[i]; + if(nzb >= nze) + jb = lp->columns+1; + else + jb = ROW_MAT_COL(mat->row_mat[nzb]); + for(j = 1; j <= lp->columns; j++) { + if(j < jb) + hold = 0; + else { + hold = get_mat(lp, i, j); + nzb++; + if(nzb < nze) + jb = ROW_MAT_COL(mat->row_mat[nzb]); + else + jb = lp->columns+1; + } + fprintf(output, " %18g", hold); + k++; + if(my_mod(k, 4) == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) + fprintf(output, "\n"); +} + +/* List the current basis matrix columns over the selected row range */ +void blockWriteBMAT(FILE *output, const char *label, lprec* lp, int first, int last) +{ + int i, j, jb, k = 0; + double hold; + + if(first < 0) + first = 0; + if(last < 0) + last = lp->rows; + + fprintf(output, label); + fprintf(output, "\n"); + + for(i = first; i <= last; i++) { + for(j = 1; j <= lp->rows; j++) { + jb = lp->var_basic[j]; + if(jb <= lp->rows) { + if(jb == i) + hold = 1; + else + hold = 0; + } + else + hold = get_mat(lp, i, j); + if(i == 0) + modifyOF1(lp, jb, &hold, 1); + hold = unscaled_mat(lp, hold, i, jb); + fprintf(output, " %18g", hold); + k++; + if(my_mod(k, 4) == 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) { + fprintf(output, "\n"); + k = 0; + } + } + if(my_mod(k, 4) != 0) + fprintf(output, "\n"); +} + +/* Do a generic readable data dump of key lp_solve model variables; + principally for run difference and debugging purposes */ +MYBOOL REPORT_debugdump(lprec *lp, char *filename, MYBOOL livedata) +{ + FILE *output = stdout; + MYBOOL ok; + + ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); + if(!ok) + return(ok); + if((filename == NULL) && (lp->outstream != NULL)) + output = lp->outstream; + + fprintf(output, "\nGENERAL INFORMATION\n-------------------\n\n"); + fprintf(output, "Model size: %d rows (%d equalities, %d Lagrangean), %d columns (%d integers, %d SC, %d SOS, %d GUB)\n", + lp->rows, lp->equalities, get_Lrows(lp), lp->columns, + lp->int_count, lp->sc_count, SOS_count(lp), GUB_count(lp)); + fprintf(output, "Data size: %d model non-zeros, %d invB non-zeros (engine is %s)\n", + get_nonzeros(lp), my_if(lp->invB == NULL, 0, lp->bfp_nonzeros(lp, FALSE)), lp->bfp_name()); + fprintf(output, "Internal sizes: %d rows allocated, %d columns allocated, %d columns used, %d eta length\n", + lp->rows_alloc, lp->columns_alloc, lp->columns, my_if(lp->invB == NULL, 0, lp->bfp_colcount(lp))); + fprintf(output, "Memory use: %d sparse matrix, %d eta\n", + lp->matA->mat_alloc, my_if(lp->invB == NULL, 0, lp->bfp_memallocated(lp))); + fprintf(output, "Parameters: Maximize=%d, Names used=%d, Scalingmode=%d, Presolve=%d, SimplexPivot=%d\n", + is_maxim(lp), lp->names_used, lp->scalemode, lp->do_presolve, lp->piv_strategy); + fprintf(output, "Precision: EpsValue=%g, EpsPrimal=%g, EpsDual=%g, EpsPivot=%g, EpsPerturb=%g\n", + lp->epsvalue, lp->epsprimal, lp->epsdual, lp->epspivot, lp->epsperturb); + fprintf(output, "Stability: AntiDegen=%d, Improvement=%d, Split variables at=%g\n", + lp->improve, lp->anti_degen, lp->negrange); + fprintf(output, "B&B settings: BB pivot rule=%d, BB branching=%s, BB strategy=%d, Integer precision=%g, MIP gaps=%g,%g\n", + lp->bb_rule, my_boolstr(lp->bb_varbranch), lp->bb_floorfirst, lp->epsint, lp->mip_absgap, lp->mip_relgap); + + fprintf(output, "\nCORE DATA\n---------\n\n"); + blockWriteINT(output, "Column starts", lp->matA->col_end, 0, lp->columns); + blockWriteINT(output, "row_type", lp->row_type, 0, lp->rows); + blockWriteREAL(output, "orig_rh", lp->orig_rh, 0, lp->rows); + blockWriteREAL(output, "orig_lowbo", lp->orig_lowbo, 0, lp->sum); + blockWriteREAL(output, "orig_upbo", lp->orig_upbo, 0, lp->sum); + blockWriteINT(output, "row_type", lp->row_type, 0, lp->rows); + blockWriteBOOL(output, "must_be_int", lp->must_be_int, 0, lp->columns, TRUE); + blockWriteAMAT(output, "A", lp, 0, lp->rows); + + if(livedata) { + fprintf(output, "\nPROCESS DATA\n------------\n\n"); + blockWriteREAL(output, "Active rhs", lp->rhs, 0, lp->rows); + blockWriteINT(output, "Basic variables", lp->var_basic, 0, lp->rows); + blockWriteBOOL(output, "is_basic", lp->is_basic, 0, lp->sum, TRUE); + blockWriteREAL(output, "lowbo", lp->lowbo, 0, lp->sum); + blockWriteREAL(output, "upbo", lp->upbo, 0, lp->sum); + if(lp->scalars != NULL) + blockWriteREAL(output, "scalars", lp->scalars, 0, lp->sum); + } + + if(filename != NULL) + fclose(output); + return(ok); +} + + +/* High level reports for model results */ + +void REPORT_objective(lprec *lp) +{ + fprintf(lp->outstream, "\nValue of objective function: %g\n", + (double)lp->best_solution[0]); + fflush(lp->outstream); +} + +void REPORT_solution(lprec *lp, int columns) +{ + int i, n; + REAL value; + MYBOOL NZonly = (MYBOOL) ((lp->print_sol & AUTOMATIC) > 0); + + fprintf(lp->outstream, "\nActual values of the variables:\n"); + if(columns <= 0) + columns = 2; + n = 0; + for(i = 1; i <= lp->orig_columns; i++) { + value = get_var_primalresult(lp, lp->orig_rows + i); + if(NZonly && (fabs(value) < lp->epsprimal)) + continue; + n = (n+1) % columns; + fprintf(lp->outstream, "%-20s %12g", get_origcol_name(lp, i), (double) value); + if(n == 0) + fprintf(lp->outstream, "\n"); + else + fprintf(lp->outstream, " "); + } + + fflush(lp->outstream); +} /* REPORT_solution */ + +void REPORT_constraints(lprec *lp, int columns) +{ + int i, n; + REAL value; + MYBOOL NZonly = (MYBOOL) ((lp->print_sol & AUTOMATIC) > 0); + + if(columns <= 0) + columns = 2; + + fprintf(lp->outstream, "\nActual values of the constraints:\n"); + n = 0; + for(i = 1; i <= lp->rows; i++) { + value = (double)lp->best_solution[i]; + if(NZonly && (fabs(value) < lp->epsprimal)) + continue; + n = (n+1) % columns; + fprintf(lp->outstream, "%-20s %12g", get_row_name(lp, i), value); + if(n == 0) + fprintf(lp->outstream, "\n"); + else + fprintf(lp->outstream, " "); + } + + fflush(lp->outstream); +} + +void REPORT_duals(lprec *lp) +{ + int i; + REAL *duals, *dualsfrom, *dualstill, *objfrom, *objtill; + MYBOOL ret; + + ret = get_ptr_sensitivity_obj(lp, &objfrom, &objtill); + if(ret) { + fprintf(lp->outstream, "\nObjective function limits:\n"); + fprintf(lp->outstream, " From Till\n"); + for(i = 1; i <= lp->columns; i++) + if(!is_splitvar(lp, i)) + fprintf(lp->outstream, "%-20s %15.7g %15.7g\n", get_col_name(lp, i), + (double)objfrom[i - 1], (double)objtill[i - 1]); + } + + ret = get_ptr_sensitivity_rhs(lp, &duals, &dualsfrom, &dualstill); + if(ret) { + fprintf(lp->outstream, "\nDual values with from - till limits:\n"); + fprintf(lp->outstream, " Dual value From Till\n"); + for(i = 1; i <= lp->sum; i++) + fprintf(lp->outstream, "%-20s %8g %8g %8g\n", + (i <= lp->rows) ? get_row_name(lp, i) : get_col_name(lp, i - lp->rows), + (double)duals[i - 1], (double)dualsfrom[i - 1], (double)dualstill[i - 1]); + fflush(lp->outstream); + } +} + +/* Printing of sensitivity analysis reports */ +void REPORT_extended(lprec *lp) +{ + int i, j; + REAL hold; + REAL *duals, *dualsfrom, *dualstill, *objfrom, *objtill; + MYBOOL ret; + + ret = get_ptr_sensitivity_obj(lp, &objfrom, &objtill); + report(lp, NORMAL, " \n"); + report(lp, NORMAL, "Primal objective:\n"); + report(lp, NORMAL, " \n"); + report(lp, NORMAL, " Column name Value Objective Min Max\n"); + report(lp, NORMAL, " --------------------------------------------------------------------------\n"); + for(j = 1; j <= lp->columns; j++) { + hold = get_mat(lp,0,j); + report(lp, NORMAL, " %-25s " MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK "\n", + get_col_name(lp,j), + my_precision(hold,lp->epsprimal), + my_precision(hold*lp->best_solution[lp->rows+j],lp->epsprimal), + my_precision((ret) ? objfrom[j - 1] : 0.0,lp->epsprimal), + my_precision((ret) ? objtill[j - 1] : 0.0,lp->epsprimal)); + } + report(lp, NORMAL, " \n"); + + ret = get_ptr_sensitivity_rhs(lp, &duals, &dualsfrom, &dualstill); + report(lp, NORMAL, "Primal variables:\n"); + report(lp, NORMAL, " \n"); + report(lp, NORMAL, " Column name Value Slack Min Max\n"); + report(lp, NORMAL, " --------------------------------------------------------------------------\n"); + for(j = 1; j <= lp->columns; j++) + report(lp, NORMAL, " %-25s " MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK "\n", + get_col_name(lp,j), + my_precision(lp->best_solution[lp->rows+j],lp->epsprimal), + my_precision(my_inflimit((ret) ? duals[lp->rows+j-1] : 0.0),lp->epsprimal), + my_precision((ret) ? dualsfrom[lp->rows+j-1] : 0.0,lp->epsprimal), + my_precision((ret) ? dualstill[lp->rows+j-1] : 0.0,lp->epsprimal)); + + report(lp, NORMAL, " \n"); + report(lp, NORMAL, "Dual variables:\n"); + report(lp, NORMAL, " \n"); + report(lp, NORMAL, " Row name Value Slack Min Max\n"); + report(lp, NORMAL, " --------------------------------------------------------------------------\n"); + for(i = 1; i <= lp->rows; i++) + report(lp, NORMAL, " %-25s " MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK MPSVALUEMASK "\n", + get_row_name(lp,i), + my_precision((ret) ? duals[i - 1] : 0.0, lp->epsprimal), + my_precision(lp->best_solution[i], lp->epsprimal), + my_precision((ret) ? dualsfrom[i - 1] : 0.0,lp->epsprimal), + my_precision((ret) ? dualstill[i - 1] : 0.0,lp->epsprimal)); + + report(lp, NORMAL, " \n"); +} + +/* A more readable lp-format report of the model; antiquated and not updated */ +void REPORT_lp(lprec *lp) +{ + int i, j; + +#ifdef Paranoia + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "REPORT_lp: Cannot print lp while in row entry mode.\n"); + return; + } +#endif + + fprintf(lp->outstream, "Model name: %s\n", lp->lp_name); + fprintf(lp->outstream, " "); + + for(j = 1; j <= lp->columns; j++) + fprintf(lp->outstream, "%8s ", get_col_name(lp,j)); + + fprintf(lp->outstream, "\n%s ", (is_maxim(lp) ? "Maximize" : "Minimize")); + for(j = 1; j <= lp->columns; j++) + fprintf(lp->outstream, "%8g ", get_mat(lp, 0, j)); + fprintf(lp->outstream, "\n"); + + for(i = 1; i <= lp->rows; i++) { + fprintf(lp->outstream, "%-9s ", get_row_name(lp, i)); + for(j = 1; j <= lp->columns; j++) + fprintf(lp->outstream, "%8g ", get_mat(lp, i, j)); + if(is_constr_type(lp, i, GE)) + fprintf(lp->outstream, ">= "); + else if(is_constr_type(lp, i, LE)) + fprintf(lp->outstream, "<= "); + else + fprintf(lp->outstream, " = "); + fprintf(lp->outstream, "%8g", get_rh(lp, i)); + + if(is_constr_type(lp, i, GE)) { + if(get_rh_upper(lp, i) < lp->infinite) + fprintf(lp->outstream, " %s = %8g", "upbo", get_rh_upper(lp, i)); + } + else if(is_constr_type(lp, i, LE)) { + if(get_rh_lower(lp, i) > -lp->infinite) + fprintf(lp->outstream, " %s = %8g", "lowbo", get_rh_lower(lp, i)); + } + fprintf(lp->outstream, "\n"); + } + + fprintf(lp->outstream, "Type "); + for(i = 1; i <= lp->columns; i++) { + if(is_int(lp,i)) + fprintf(lp->outstream, " Int "); + else + fprintf(lp->outstream, " Real "); + } + + fprintf(lp->outstream, "\nupbo "); + for(i = 1; i <= lp->columns; i++) + if(get_upbo(lp, i) >= lp->infinite) + fprintf(lp->outstream, " Inf "); + else + fprintf(lp->outstream, "%8g ", get_upbo(lp, i)); + fprintf(lp->outstream, "\nlowbo "); + for(i = 1; i <= lp->columns; i++) + if(get_lowbo(lp, i) <= -lp->infinite) + fprintf(lp->outstream, " -Inf "); + else + fprintf(lp->outstream, "%8g ", get_lowbo(lp, i)); + fprintf(lp->outstream, "\n"); + + fflush(lp->outstream); +} + +/* Report the scaling factors used; extremely rarely used */ +void REPORT_scales(lprec *lp) +{ + int i, colMax; + + colMax = lp->columns; + + if(lp->scaling_used) { + fprintf(lp->outstream, "\nScale factors:\n"); + for(i = 0; i <= lp->rows + colMax; i++) + fprintf(lp->outstream, "%-20s scaled at %g\n", + (i <= lp->rows) ? get_row_name(lp, i) : get_col_name(lp, i - lp->rows), + (double)lp->scalars[i]); + } + fflush(lp->outstream); +} + +/* Report the traditional tableau corresponding to the current basis */ +MYBOOL REPORT_tableau(lprec *lp) +{ + int j, row_nr; + REAL *prow = NULL; + FILE *stream = lp->outstream; + + if(!lp->model_is_valid || !has_BFP(lp) || + (get_total_iter(lp) == 0) || (lp->spx_status == NOTRUN)) { + lp->spx_status = NOTRUN; + return(FALSE); + } + if(!allocREAL(lp, &prow,lp->sum + 1, TRUE)) { + lp->spx_status = NOMEMORY; + return(FALSE); + } + + fprintf(stream, "\n"); + fprintf(stream, "Tableau at iteration %d:\n", get_total_iter(lp)); + + for(j = 1; j <= lp->sum; j++) + if (!lp->is_basic[j]) + fprintf(stream, "%15d", (j <= lp->rows ? + (j + lp->columns) * ((lp->orig_upbo[j] == 0) || + (is_chsign(lp, j)) ? 1 : -1) : j - lp->rows) * + (lp->is_lower[j] ? 1 : -1)); + fprintf(stream, "\n"); + + for(row_nr = 1; (row_nr <= lp->rows + 1); row_nr++) { + if (row_nr <= lp->rows) + fprintf(stream, "%3d", (lp->var_basic[row_nr] <= lp->rows ? + (lp->var_basic[row_nr] + lp->columns) * ((lp->orig_upbo[lp->var_basic [row_nr]] == 0) || + (is_chsign(lp, lp->var_basic[row_nr])) ? 1 : -1) : lp->var_basic[row_nr] - lp->rows) * + (lp->is_lower[lp->var_basic [row_nr]] ? 1 : -1)); + else + fprintf(stream, " "); + bsolve(lp, row_nr <= lp->rows ? row_nr : 0, prow, NULL, lp->epsmachine*DOUBLEROUND, 1.0); + prod_xA(lp, SCAN_USERVARS+USE_NONBASICVARS, prow, NULL, XRESULT_FREE, lp->epsmachine, 1.0, + prow, NULL); + + for(j = 1; j <= lp->rows + lp->columns; j++) + if (!lp->is_basic[j]) + fprintf(stream, "%15.7f", prow[j] * (lp->is_lower[j] ? 1 : -1) * + (row_nr <= lp->rows ? 1 : -1)); + fprintf(stream, "%15.7f", lp->rhs[row_nr <= lp->rows ? row_nr : 0] * + (REAL) ((row_nr <= lp->rows) || (is_maxim(lp)) ? 1 : -1)); + fprintf(stream, "\n"); + } + FREE(prow); + return(TRUE); +} Index: src/tools/solver/lp_solve/lp_report.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_report.h diff -N src/tools/solver/lp_solve/lp_report.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_report.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,33 @@ +#ifndef HEADER_lp_report +#define HEADER_lp_report + +#ifdef __cplusplus +extern "C" { +#endif + +/* Prototypes for debugging and general data dumps */ +void debug_print(lprec *lp, char *format, ...); +void debug_print_solution(lprec *lp); +void debug_print_bounds(lprec *lp, REAL *upbo, REAL *lowbo); +void blockWriteLREAL(FILE *output, char *label, LREAL *vector, int first, int last); +void blockWriteAMAT(FILE *output, const char *label, lprec* lp, int first, int last); +void blockWriteBMAT(FILE *output, const char *label, lprec* lp, int first, int last); + +/* Model reporting headers */ +void REPORT_objective(lprec *lp); +void REPORT_solution(lprec *lp, int columns); +void REPORT_constraints(lprec *lp, int columns); +void REPORT_duals(lprec *lp); +void REPORT_extended(lprec *lp); + +/* Other rarely used, but sometimes extremely useful reports */ +void REPORT_lp(lprec *lp); +MYBOOL REPORT_tableau(lprec *lp); +void REPORT_scales(lprec *lp); +MYBOOL REPORT_debugdump(lprec *lp, char *filename, MYBOOL livedata); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_report */ Index: src/tools/solver/lp_solve/lp_rlp.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_rlp.c diff -N src/tools/solver/lp_solve/lp_rlp.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_rlp.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1512 @@ + +/* A Bison parser, made from lp_rlp.y + by GNU Bison version 1.28 */ + +#define YYBISON 1 /* Identify Bison output. */ + +#define VAR 257 +#define CONS 258 +#define INTCONS 259 +#define VARIABLECOLON 260 +#define INF 261 +#define SEC_INT 262 +#define SEC_SEC 263 +#define SEC_SOS 264 +#define SOSDESCR 265 +#define SIGN 266 +#define AR_M_OP 267 +#define RE_OPLE 268 +#define RE_OPGE 269 +#define END_C 270 +#define COMMA 271 +#define COLON 272 +#define MINIMISE 273 +#define MAXIMISE 274 +#define UNDEFINED 275 + + +#include +#include + +#include "lpkit.h" +#include "yacc_read.h" + +static int HadVar0, HadVar1, HadVar2, HasAR_M_OP, do_add_row, Had_lineair_sum0, HadSign; +static char Last_var[NAMELEN], Last_var0[NAMELEN]; +static REAL f, f0, f1; +static int x; +static int state, state0; +static int Sign; +static int isign, isign0; /* internal_sign variable to make sure nothing goes wrong */ + /* with lookahead */ +static int make_neg; /* is true after the relational operator is seen in order */ + /* to remember if lin_term stands before or after re_op */ +static int Within_int_decl = FALSE; /* TRUE when we are within an int declaration */ +static int Within_sec_decl = FALSE; /* TRUE when we are within an sec declaration */ +static int Within_sos_decl = FALSE; /* TRUE when we are within an sos declaration */ +static int Within_sos_decl1; +static short SOStype0; /* SOS type */ +static short SOStype; /* SOS type */ +static int SOSNr; +static int weight; /* SOS weight */ +static int SOSweight = 0; /* SOS weight */ + +static int HadConstraint; +static int HadVar; +static int Had_lineair_sum; + +#define YY_FATAL_ERROR lex_fatal_error + +/* let's please C++ users */ +#ifdef __cplusplus +extern "C" { +#endif + +static int wrap(void) +{ + return(1); +} + +#ifdef __cplusplus +}; +#endif + +#define lp_yywrap wrap +#define lp_yyerror read_error + +#include "lp_rlp.h" + +#ifndef YYSTYPE +#define YYSTYPE int +#endif +#include + +#ifndef __cplusplus +#ifndef __STDC__ +#define const +#endif +#endif + + + +#define YYFINAL 120 +#define YYFLAG -32768 +#define YYNTBASE 22 + +#define YYTRANSLATE(x) ((unsigned)(x) <= 275 ? lp_yytranslate[x] : 77) + +static const char lp_yytranslate[] = { 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21 +}; + +#if YYDEBUG != 0 +static const short lp_yyprhs[] = { 0, + 0, 1, 2, 7, 10, 13, 15, 18, 20, 22, + 24, 26, 28, 31, 33, 34, 38, 39, 40, 41, + 50, 52, 53, 54, 60, 62, 64, 66, 67, 71, + 72, 75, 77, 80, 83, 85, 86, 90, 92, 94, + 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, + 117, 119, 122, 124, 126, 128, 129, 133, 134, 140, + 142, 145, 147, 148, 152, 154, 155, 160, 162, 165, + 167, 169, 171, 175, 177, 179, 181, 182, 184, 186, + 189, 193, 196, 199, 202 +}; + +static const short lp_yyrhs[] = { -1, + 0, 24, 25, 28, 54, 0, 20, 26, 0, 19, + 26, 0, 26, 0, 27, 16, 0, 22, 0, 42, + 0, 22, 0, 29, 0, 30, 0, 29, 30, 0, + 32, 0, 0, 6, 31, 32, 0, 0, 0, 0, + 39, 33, 48, 34, 40, 35, 36, 16, 0, 22, + 0, 0, 0, 48, 37, 49, 38, 53, 0, 22, + 0, 40, 0, 42, 0, 0, 7, 41, 53, 0, + 0, 43, 44, 0, 45, 0, 44, 45, 0, 51, + 46, 0, 50, 0, 0, 52, 47, 3, 0, 14, + 0, 15, 0, 51, 50, 0, 7, 0, 5, 0, + 4, 0, 22, 0, 12, 0, 22, 0, 13, 0, + 22, 0, 22, 0, 55, 0, 57, 0, 55, 57, + 0, 8, 0, 9, 0, 10, 0, 0, 56, 58, + 61, 0, 0, 60, 62, 67, 64, 16, 0, 59, + 0, 61, 59, 0, 22, 0, 0, 11, 63, 73, + 0, 22, 0, 0, 14, 5, 65, 66, 0, 22, + 0, 18, 5, 0, 22, 0, 68, 0, 74, 0, + 68, 69, 74, 0, 22, 0, 17, 0, 22, 0, + 0, 22, 0, 22, 0, 3, 70, 0, 6, 71, + 75, 0, 5, 72, 0, 73, 76, 0, 3, 70, + 0, 6, 71, 5, 72, 0 +}; + +#endif + +#if YYDEBUG != 0 +static const short lp_yyrline[] = { 0, + 66, 69, 78, 92, 96, 100, 103, 114, 115, 145, + 146, 149, 150, 154, 155, 162, 164, 170, 177, 199, + 215, 221, 232, 236, 257, 267, 273, 274, 279, 281, + 286, 300, 301, 305, 341, 345, 353, 358, 358, 361, + 363, 374, 374, 377, 382, 389, 393, 399, 416, 418, + 421, 422, 425, 425, 425, 428, 434, 436, 446, 458, + 460, 463, 464, 469, 471, 478, 492, 494, 498, 505, + 506, 509, 510, 515, 516, 519, 546, 564, 588, 603, + 606, 611, 613, 617, 620 +}; +#endif + + +#if YYDEBUG != 0 || defined (YYERROR_VERBOSE) + +static const char * const lp_yytname[] = { "$","error","$undefined.","VAR","CONS", +"INTCONS","VARIABLECOLON","INF","SEC_INT","SEC_SEC","SEC_SOS","SOSDESCR","SIGN", +"AR_M_OP","RE_OPLE","RE_OPGE","END_C","COMMA","COLON","MINIMISE","MAXIMISE", +"UNDEFINED","EMPTY","inputfile","@1","objective_function","real_of","lineair_sum", +"constraints","x_constraints","constraint","@2","real_constraint","@3","@4", +"@5","optionalrange","@6","@7","x_lineair_sum2","x_lineair_sum3","@8","x_lineair_sum", +"@9","x_lineair_sum1","x_lineair_term","x_lineair_term1","@10","RE_OP","cons_term", +"REALCONS","x_SIGN","optional_AR_M_OP","RHS_STORE","int_sec_sos_declarations", +"real_int_sec_sos_decls","SEC_INT_SEC_SOS","int_sec_sos_declaration","@11","xx_int_sec_sos_declaration", +"@12","x_int_sec_sos_declaration","optionalsos","@13","optionalsostype","@14", +"optionalSOSweight","vars","x_vars","optionalcomma","variable","variablecolon", +"intcons","sosdescr","onevarwithoptionalweight","INTCONSorVARIABLE","x_onevarwithoptionalweight", NULL +}; +#endif + +static const short lp_yyr1[] = { 0, + 22, 24, 23, 25, 25, 25, 26, 27, 27, 28, + 28, 29, 29, 30, 31, 30, 33, 34, 35, 32, + 36, 37, 38, 36, 39, 39, 40, 41, 40, 43, + 42, 44, 44, 45, 46, 47, 46, 48, 48, 49, + 49, 50, 50, 51, 51, 52, 52, 53, 54, 54, + 55, 55, 56, 56, 56, 58, 57, 60, 59, 61, + 61, 62, 63, 62, 64, 65, 64, 66, 66, 67, + 67, 68, 68, 69, 69, 70, 71, 72, 73, 74, + 74, 75, 75, 76, 76 +}; + +static const short lp_yyr2[] = { 0, + 0, 0, 4, 2, 2, 1, 2, 1, 1, 1, + 1, 1, 2, 1, 0, 3, 0, 0, 0, 8, + 1, 0, 0, 5, 1, 1, 1, 0, 3, 0, + 2, 1, 2, 2, 1, 0, 3, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 0, 3, 0, 5, 1, + 2, 1, 0, 3, 1, 0, 4, 1, 2, 1, + 1, 1, 3, 1, 1, 1, 0, 1, 1, 2, + 3, 2, 2, 2, 4 +}; + +static const short lp_yydefact[] = { 2, + 30, 30, 30, 8, 1, 6, 0, 9, 1, 5, + 4, 15, 28, 10, 1, 30, 12, 14, 17, 26, + 27, 7, 45, 44, 1, 32, 1, 30, 1, 53, + 54, 55, 49, 3, 50, 56, 51, 25, 13, 0, + 33, 43, 42, 47, 46, 34, 35, 36, 16, 48, + 29, 52, 58, 38, 39, 18, 0, 60, 1, 58, + 30, 37, 63, 62, 1, 61, 19, 1, 1, 77, + 70, 1, 1, 72, 1, 79, 64, 76, 80, 1, + 0, 65, 0, 75, 74, 0, 21, 0, 22, 1, + 0, 81, 66, 59, 73, 20, 1, 78, 82, 1, + 77, 83, 1, 41, 23, 0, 84, 0, 0, 68, + 67, 1, 40, 1, 69, 24, 85, 0, 0, 0 +}; + +static const short lp_yydefgoto[] = { 4, + 118, 1, 5, 6, 7, 15, 16, 17, 28, 18, + 40, 61, 75, 88, 97, 112, 19, 20, 29, 21, + 9, 25, 26, 46, 57, 56, 105, 47, 27, 48, + 51, 34, 35, 36, 37, 53, 58, 59, 60, 65, + 68, 83, 103, 111, 72, 73, 86, 79, 80, 99, + 77, 74, 92, 102 +}; + +static const short lp_yypact[] = {-32768, + 33, 16, 16,-32768, 2,-32768, 21,-32768, 29,-32768, +-32768,-32768,-32768, 36, -7, 19,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768, 32,-32768, 8, 28,-32768,-32768, +-32768,-32768,-32768,-32768, -7,-32768,-32768,-32768,-32768, 57, +-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768,-32768, 42,-32768, 50, 30, + 62,-32768,-32768,-32768, 53,-32768,-32768,-32768,-32768,-32768, +-32768, 52, 41,-32768, 57,-32768,-32768,-32768,-32768, 73, + 75,-32768, 63,-32768,-32768, 53,-32768, 65,-32768,-32768, + 59,-32768,-32768,-32768,-32768,-32768, 24,-32768,-32768,-32768, +-32768,-32768, 64,-32768,-32768, 69,-32768, 78, 79,-32768, +-32768,-32768,-32768,-32768,-32768,-32768,-32768, 86, 87,-32768 +}; + +static const short lp_yypgoto[] = { -5, +-32768,-32768,-32768, 74,-32768,-32768,-32768, 72,-32768, 61, +-32768,-32768,-32768,-32768,-32768,-32768,-32768, 35,-32768, 15, +-32768,-32768, 66,-32768,-32768, 18,-32768, -16, -3,-32768, + -15,-32768,-32768,-32768, 67,-32768, 39,-32768,-32768,-32768, +-32768,-32768,-32768,-32768,-32768,-32768,-32768, 0, 3, -13, + 23, 20,-32768,-32768 +}; + + +#define YYLAST 109 + + +static const short lp_yytable[] = { 14, + 30, 31, 32, 24, -30, -30, -30, 12, 13, 33, + 38, 42, 43, -30, -30, 8, 8, 8, -11, 24, + 44, 45, 38, 50, 12, 13, -11, -11, -11, -57, + 104, -1, -1, -1, 13, 23, 22, -57, -57, -57, + 23, -1, -1, 23, 62, -31, -31, -31, -1, -25, + -25, 2, 3, 64, -71, 69, -71, 84, 70, 71, + 63, 100, 76, 78, 101, 81, 82, 85, 13, 87, + 54, 55, 42, 43, 76, 10, 11, 90, 94, 93, + 96, 109, 114, 115, 98, 119, 120, 39, 49, 113, + 41, 24, 89, 106, 78, 67, 116, 110, 66, 107, + 117, 52, 91, 108, 0, 95, 50, 0, 98 +}; + +static const short lp_yycheck[] = { 5, + 8, 9, 10, 9, 3, 4, 5, 6, 7, 15, + 16, 4, 5, 12, 13, 1, 2, 3, 0, 25, + 13, 27, 28, 29, 6, 7, 8, 9, 10, 0, + 7, 16, 14, 15, 7, 12, 16, 8, 9, 10, + 12, 14, 15, 12, 3, 14, 15, 16, 16, 14, + 15, 19, 20, 59, 14, 3, 16, 17, 6, 65, + 11, 3, 68, 69, 6, 14, 72, 73, 7, 75, + 14, 15, 4, 5, 80, 2, 3, 5, 16, 5, + 16, 18, 5, 5, 90, 0, 0, 16, 28, 106, + 25, 97, 75, 97, 100, 61, 112, 103, 60, 100, + 114, 35, 80, 101, -1, 86, 112, -1, 114 +}; +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ + +/* This file comes from bison-1.28. */ + +/* Skeleton output parser for bison, + Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* This is the parser code that is written into each bison parser + when the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +#ifndef YYSTACK_USE_ALLOCA +#ifdef alloca +#define YYSTACK_USE_ALLOCA +#else /* alloca not defined */ +#ifdef __GNUC__ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#else /* not GNU C. */ +#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386)) +#define YYSTACK_USE_ALLOCA +#include +#else /* not sparc */ +/* We think this test detects Watcom and Microsoft C. */ +/* This used to test MSDOS, but that is a bad idea + since that symbol is in the user namespace. */ +#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__) +#if 0 /* No need for malloc.h, which pollutes the namespace; + instead, just don't use alloca. */ +#include +#endif +#else /* not MSDOS, or __TURBOC__ */ +#if defined(_AIX) +/* I don't know what this was needed for, but it pollutes the namespace. + So I turned it off. rms, 2 May 1997. */ +/* #include */ + #pragma alloca +#define YYSTACK_USE_ALLOCA +#else /* not MSDOS, or __TURBOC__, or _AIX */ +#if 0 +#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up, + and on HPUX 10. Eventually we can turn this on. */ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#endif /* __hpux */ +#endif +#endif /* not _AIX */ +#endif /* not MSDOS, or __TURBOC__ */ +#endif /* not sparc */ +#endif /* not GNU C */ +#endif /* alloca not defined */ +#endif /* YYSTACK_USE_ALLOCA not defined */ + +#ifdef YYSTACK_USE_ALLOCA +#define YYSTACK_ALLOC alloca +#else +#define YYSTACK_ALLOC malloc +#endif + +/* Note: there must be only one dollar sign in this file. + It is replaced by the list of actions, each action + as one case of the switch. */ + +#define lp_yyerrok (lp_yyerrstatus = 0) +#define lp_yyclearin (lp_yychar = YYEMPTY) +#define YYEMPTY -2 +#define YYEOF 0 +#define YYACCEPT goto lp_yyacceptlab +#define YYABORT goto lp_yyabortlab +#define YYERROR goto lp_yyerrlab1 +/* Like YYERROR except do call lp_yyerror. + This remains here temporarily to ease the + transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define YYFAIL goto lp_yyerrlab +#define YYRECOVERING() (!!lp_yyerrstatus) +#define YYBACKUP(token, value) \ +do \ + if (lp_yychar == YYEMPTY && lp_yylen == 1) \ + { lp_yychar = (token), lp_yylval = (value); \ + lp_yychar1 = YYTRANSLATE (lp_yychar); \ + YYPOPSTACK; \ + goto lp_yybackup; \ + } \ + else \ + { lp_yyerror ("syntax error: cannot back up"); YYERROR; } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +#ifndef YYPURE +#define YYLEX lp_yylex() +#endif + +#ifdef YYPURE +#ifdef YYLSP_NEEDED +#ifdef YYLEX_PARAM +#define YYLEX lp_yylex(&lp_yylval, &lp_yylloc, YYLEX_PARAM) +#else +#define YYLEX lp_yylex(&lp_yylval, &lp_yylloc) +#endif +#else /* not YYLSP_NEEDED */ +#ifdef YYLEX_PARAM +#define YYLEX lp_yylex(&lp_yylval, YYLEX_PARAM) +#else +#define YYLEX lp_yylex(&lp_yylval) +#endif +#endif /* not YYLSP_NEEDED */ +#endif + +/* If nonreentrant, generate the variables here */ + +#ifndef YYPURE + +int lp_yychar; /* the lookahead symbol */ +YYSTYPE lp_yylval; /* the semantic value of the */ + /* lookahead symbol */ + +#ifdef YYLSP_NEEDED +YYLTYPE lp_yylloc; /* location data for the lookahead */ + /* symbol */ +#endif + +int lp_yynerrs; /* number of parse errors so far */ +#endif /* not YYPURE */ + +#if YYDEBUG != 0 +int lp_yydebug; /* nonzero means print parse trace */ +/* Since this is uninitialized, it does not stop multiple parsers + from coexisting. */ +#endif + +/* YYINITDEPTH indicates the initial size of the parser's stacks */ + +#ifndef YYINITDEPTH +#define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH is the maximum size the stacks can grow to + (effective only if the built-in stack extension method is used). */ + +#if YYMAXDEPTH == 0 +#undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 10000 +#endif + +/* Define __lp_yy_memcpy. Note that the size argument + should be passed with type unsigned int, because that is what the non-GCC + definitions require. With GCC, __builtin_memcpy takes an arg + of type size_t, but it can handle unsigned int. */ + +#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */ +#define __lp_yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT) +#else /* not GNU C or C++ */ +#ifndef __cplusplus + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__lp_yy_memcpy (to, from, count) + char *to; + char *from; + unsigned int count; +{ + register char *f = from; + register char *t = to; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#else /* __cplusplus */ + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__lp_yy_memcpy (char *to, char *from, unsigned int count) +{ + register char *t = to; + register char *f = from; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#endif +#endif + + + +/* The user can define YYPARSE_PARAM as the name of an argument to be passed + into lp_yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef YYPARSE_PARAM +#ifdef __cplusplus +#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM +#define YYPARSE_PARAM_DECL +#else /* not __cplusplus */ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM +#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; +#endif /* not __cplusplus */ +#else /* not YYPARSE_PARAM */ +#define YYPARSE_PARAM_ARG +#define YYPARSE_PARAM_DECL +#endif /* not YYPARSE_PARAM */ + +/* Prevent warning if -Wstrict-prototypes. */ +#ifdef __GNUC__ +#ifdef YYPARSE_PARAM +int lp_yyparse (void *); +#else +int lp_yyparse (void); +#endif +#endif + +int +lp_yyparse(YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL +{ + register int lp_yystate; + register int lp_yyn; + register short *lp_yyssp; + register YYSTYPE *lp_yyvsp; + int lp_yyerrstatus; /* number of tokens to shift before error messages enabled */ + int lp_yychar1 = 0; /* lookahead token as an internal (translated) token number */ + + short lp_yyssa[YYINITDEPTH]; /* the state stack */ + YYSTYPE lp_yyvsa[YYINITDEPTH]; /* the semantic value stack */ + + short *lp_yyss = lp_yyssa; /* refer to the stacks thru separate pointers */ + YYSTYPE *lp_yyvs = lp_yyvsa; /* to allow lp_yyoverflow to reallocate them elsewhere */ + +#ifdef YYLSP_NEEDED + YYLTYPE lp_yylsa[YYINITDEPTH]; /* the location stack */ + YYLTYPE *lp_yyls = lp_yylsa; + YYLTYPE *lp_yylsp; + +#define YYPOPSTACK (lp_yyvsp--, lp_yyssp--, lp_yylsp--) +#else +#define YYPOPSTACK (lp_yyvsp--, lp_yyssp--) +#endif + + int lp_yystacksize = YYINITDEPTH; + int lp_yyfree_stacks = 0; + +#ifdef YYPURE + int lp_yychar; + YYSTYPE lp_yylval; + int lp_yynerrs; +#ifdef YYLSP_NEEDED + YYLTYPE lp_yylloc; +#endif +#endif + + YYSTYPE lp_yyval = 0; /* the variable used to return */ + /* semantic values from the action */ + /* routines */ + + int lp_yylen; + +#if YYDEBUG != 0 + if (lp_yydebug) + fprintf(stderr, "Starting parse\n"); +#endif + + lp_yystate = 0; + lp_yyerrstatus = 0; + lp_yynerrs = 0; + lp_yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + lp_yyssp = lp_yyss - 1; + lp_yyvsp = lp_yyvs; +#ifdef YYLSP_NEEDED + lp_yylsp = lp_yyls; +#endif + +/* Push a new state, which is found in lp_yystate . */ +/* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. */ +lp_yynewstate: + + *++lp_yyssp = lp_yystate; + + if (lp_yyssp >= lp_yyss + lp_yystacksize - 1) + { + /* Give user a chance to reallocate the stack */ + /* Use copies of these so that the &'s don't force the real ones into memory. */ + YYSTYPE *lp_yyvs1 = lp_yyvs; + short *lp_yyss1 = lp_yyss; +#ifdef YYLSP_NEEDED + YYLTYPE *lp_yyls1 = lp_yyls; +#endif + + /* Get the current used size of the three stacks, in elements. */ + int size = lp_yyssp - lp_yyss + 1; + +#ifdef lp_yyoverflow + /* Each stack pointer address is followed by the size of + the data in use in that stack, in bytes. */ +#ifdef YYLSP_NEEDED + /* This used to be a conditional around just the two extra args, + but that might be undefined if lp_yyoverflow is a macro. */ + lp_yyoverflow("parser stack overflow", + &lp_yyss1, size * sizeof (*lp_yyssp), + &lp_yyvs1, size * sizeof (*lp_yyvsp), + &lp_yyls1, size * sizeof (*lp_yylsp), + &lp_yystacksize); +#else + lp_yyoverflow("parser stack overflow", + &lp_yyss1, size * sizeof (*lp_yyssp), + &lp_yyvs1, size * sizeof (*lp_yyvsp), + &lp_yystacksize); +#endif + + lp_yyss = lp_yyss1; lp_yyvs = lp_yyvs1; +#ifdef YYLSP_NEEDED + lp_yyls = lp_yyls1; +#endif +#else /* no lp_yyoverflow */ + /* Extend the stack our own way. */ + if (lp_yystacksize >= YYMAXDEPTH) + { + lp_yyerror("parser stack overflow"); + if (lp_yyfree_stacks) + { + free (lp_yyss); + free (lp_yyvs); +#ifdef YYLSP_NEEDED + free (lp_yyls); +#endif + } + return 2; + } + lp_yystacksize *= 2; + if (lp_yystacksize > YYMAXDEPTH) + lp_yystacksize = YYMAXDEPTH; +#ifndef YYSTACK_USE_ALLOCA + lp_yyfree_stacks = 1; +#endif + lp_yyss = (short *) YYSTACK_ALLOC (lp_yystacksize * sizeof (*lp_yyssp)); + __lp_yy_memcpy ((char *)lp_yyss, (char *)lp_yyss1, + size * (unsigned int) sizeof (*lp_yyssp)); + lp_yyvs = (YYSTYPE *) YYSTACK_ALLOC (lp_yystacksize * sizeof (*lp_yyvsp)); + __lp_yy_memcpy ((char *)lp_yyvs, (char *)lp_yyvs1, + size * (unsigned int) sizeof (*lp_yyvsp)); +#ifdef YYLSP_NEEDED + lp_yyls = (YYLTYPE *) YYSTACK_ALLOC (lp_yystacksize * sizeof (*lp_yylsp)); + __lp_yy_memcpy ((char *)lp_yyls, (char *)lp_yyls1, + size * (unsigned int) sizeof (*lp_yylsp)); +#endif +#endif /* no lp_yyoverflow */ + + lp_yyssp = lp_yyss + size - 1; + lp_yyvsp = lp_yyvs + size - 1; +#ifdef YYLSP_NEEDED + lp_yylsp = lp_yyls + size - 1; +#endif + +#if YYDEBUG != 0 + if (lp_yydebug) + fprintf(stderr, "Stack size increased to %d\n", lp_yystacksize); +#endif + + if (lp_yyssp >= lp_yyss + lp_yystacksize - 1) + YYABORT; + } + +#if YYDEBUG != 0 + if (lp_yydebug) + fprintf(stderr, "Entering state %d\n", lp_yystate); +#endif + + goto lp_yybackup; + lp_yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* lp_yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + lp_yyn = lp_yypact[lp_yystate]; + if (lp_yyn == YYFLAG) + goto lp_yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* lp_yychar is either YYEMPTY or YYEOF + or a valid token in external form. */ + + if (lp_yychar == YYEMPTY) + { +#if YYDEBUG != 0 + if (lp_yydebug) + fprintf(stderr, "Reading a token: "); +#endif + lp_yychar = YYLEX; + } + + /* Convert token to internal form (in lp_yychar1) for indexing tables with */ + + if (lp_yychar <= 0) /* This means end of input. */ + { + lp_yychar1 = 0; + lp_yychar = YYEOF; /* Don't call YYLEX any more */ + +#if YYDEBUG != 0 + if (lp_yydebug) + fprintf(stderr, "Now at end of input.\n"); +#endif + } + else + { + lp_yychar1 = YYTRANSLATE(lp_yychar); + +#if YYDEBUG != 0 + if (lp_yydebug) + { + fprintf (stderr, "Next token is %d (%s", lp_yychar, lp_yytname[lp_yychar1]); + /* Give the individual parser a way to print the precise meaning + of a token, for further debugging info. */ +#ifdef YYPRINT + YYPRINT (stderr, lp_yychar, lp_yylval); +#endif + fprintf (stderr, ")\n"); + } +#endif + } + + lp_yyn += lp_yychar1; + if (lp_yyn < 0 || lp_yyn > YYLAST || lp_yycheck[lp_yyn] != lp_yychar1) + goto lp_yydefault; + + lp_yyn = lp_yytable[lp_yyn]; + + /* lp_yyn is what to do for this token type in this state. + Negative => reduce, -lp_yyn is rule number. + Positive => shift, lp_yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ + + if (lp_yyn < 0) + { + if (lp_yyn == YYFLAG) + goto lp_yyerrlab; + lp_yyn = -lp_yyn; + goto lp_yyreduce; + } + else if (lp_yyn == 0) + goto lp_yyerrlab; + + if (lp_yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + +#if YYDEBUG != 0 + if (lp_yydebug) + fprintf(stderr, "Shifting token %d (%s), ", lp_yychar, lp_yytname[lp_yychar1]); +#endif + + /* Discard the token being shifted unless it is eof. */ + if (lp_yychar != YYEOF) + lp_yychar = YYEMPTY; + + *++lp_yyvsp = lp_yylval; +#ifdef YYLSP_NEEDED + *++lp_yylsp = lp_yylloc; +#endif + + /* count tokens shifted since error; after three, turn off error status. */ + if (lp_yyerrstatus) lp_yyerrstatus--; + + lp_yystate = lp_yyn; + goto lp_yynewstate; + +/* Do the default action for the current state. */ +lp_yydefault: + + lp_yyn = lp_yydefact[lp_yystate]; + if (lp_yyn == 0) + goto lp_yyerrlab; + +/* Do a reduction. lp_yyn is the number of a rule to reduce with. */ +lp_yyreduce: + lp_yylen = lp_yyr2[lp_yyn]; + if (lp_yylen > 0) + lp_yyval = lp_yyvsp[1-lp_yylen]; /* implement default value of the action */ + +#if YYDEBUG != 0 + if (lp_yydebug) + { + int i; + + fprintf (stderr, "Reducing via rule %d (line %d), ", + lp_yyn, lp_yyrline[lp_yyn]); + + /* Print the symbols being reduced, and their result. */ + for (i = lp_yyprhs[lp_yyn]; lp_yyrhs[i] > 0; i++) + fprintf (stderr, "%s ", lp_yytname[lp_yyrhs[i]]); + fprintf (stderr, " -> %s\n", lp_yytname[lp_yyr1[lp_yyn]]); + } +#endif + + + switch (lp_yyn) { + +case 2: +{ + isign = 0; + make_neg = 0; + Sign = 0; + HadConstraint = FALSE; + HadVar = HadVar0 = FALSE; +; + break;} +case 4: +{ + set_obj_dir(TRUE); +; + break;} +case 5: +{ + set_obj_dir(FALSE); +; + break;} +case 7: +{ + add_row(); + HadConstraint = FALSE; + HadVar = HadVar0 = FALSE; + isign = 0; + make_neg = 0; +; + break;} +case 15: +{ + if(!add_constraint_name(Last_var)) + YYABORT; + HadConstraint = TRUE; +; + break;} +case 17: +{ + HadVar1 = HadVar0; + HadVar0 = FALSE; +; + break;} +case 18: +{ + if(!store_re_op((char *) lp_yytext, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + make_neg = 1; + f1 = 0; +; + break;} +case 19: +{ + Had_lineair_sum0 = Had_lineair_sum; + Had_lineair_sum = TRUE; + HadVar2 = HadVar0; + HadVar0 = FALSE; + do_add_row = FALSE; + if(HadConstraint && !HadVar ) { + /* it is a range */ + /* already handled */ + } + else if(!HadConstraint && HadVar) { + /* it is a bound */ + + if(!store_bounds(TRUE)) + YYABORT; + } + else { + /* it is a row restriction */ + do_add_row = TRUE; + } +; + break;} +case 20: +{ + if((!HadVar) && (!HadConstraint)) { + lp_yyerror("parse error"); + YYABORT; + } + if(do_add_row) + add_row(); + HadConstraint = FALSE; + HadVar = HadVar0 = FALSE; + isign = 0; + make_neg = 0; + null_tmp_store(TRUE); +; + break;} +case 21: +{ + if((!HadVar1) && (Had_lineair_sum0)) + if(!negate_constraint()) + YYABORT; +; + break;} +case 22: +{ + make_neg = 0; + isign = 0; + if(HadConstraint) + HadVar = Had_lineair_sum = FALSE; + HadVar0 = FALSE; + if(!store_re_op((char *) ((*lp_yytext == '<') ? ">" : (*lp_yytext == '>') ? "<" : lp_yytext), HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; +; + break;} +case 23: +{ + f -= f1; +; + break;} +case 24: +{ + if((HadVar1) || (!HadVar2) || (HadVar0)) { + lp_yyerror("parse error"); + YYABORT; + } + + if(HadConstraint && !HadVar ) { + /* it is a range */ + /* already handled */ + if(!negate_constraint()) + YYABORT; + } + else if(!HadConstraint && HadVar) { + /* it is a bound */ + + if(!store_bounds(TRUE)) + YYABORT; + } +; + break;} +case 25: +{ + /* to allow a range */ + /* constraint: < max */ + if(!HadConstraint) { + lp_yyerror("parse error"); + YYABORT; + } + Had_lineair_sum = FALSE; +; + break;} +case 26: +{ + Had_lineair_sum = TRUE; +; + break;} +case 28: +{ + isign = Sign; +; + break;} +case 30: +{ + state = state0 = 0; +; + break;} +case 31: +{ + if (state == 1) { + /* RHS_STORE */ + if ( (isign0 || !make_neg) + && !(isign0 && !make_neg)) /* but not both! */ + f0 = -f0; + if(make_neg) + f1 += f0; + if(!rhs_store(f0, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + } +; + break;} +case 34: +{ + if ((HadSign || state == 1) && (state0 == 1)) { + /* RHS_STORE */ + if ( (isign0 || !make_neg) + && !(isign0 && !make_neg)) /* but not both! */ + f0 = -f0; + if(make_neg) + f1 += f0; + if(!rhs_store(f0, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + } + if (state == 1) { + f0 = f; + isign0 = isign; + } + if (state == 2) { + if((HadSign) || (state0 != 1)) { + isign0 = isign; + f0 = 1.0; + } + if ( (isign0 || make_neg) + && !(isign0 && make_neg)) /* but not both! */ + f0 = -f0; + if(!var_store(Last_var, f0, HadConstraint, HadVar, Had_lineair_sum)) { + lp_yyerror("var_store failed"); + YYABORT; + } + HadConstraint |= HadVar; + HadVar = HadVar0 = TRUE; + } + state0 = state; +; + break;} +case 35: +{ + state = 1; +; + break;} +case 36: +{ + if ((HasAR_M_OP) && (state != 1)) { + lp_yyerror("parse error"); + YYABORT; + } +; + break;} +case 37: +{ + state = 2; +; + break;} +case 41: +{ + isign = Sign; +; + break;} +case 44: +{ + isign = 0; + HadSign = FALSE; +; + break;} +case 45: +{ + isign = Sign; + HadSign = TRUE; +; + break;} +case 46: +{ + HasAR_M_OP = FALSE; +; + break;} +case 47: +{ + HasAR_M_OP = TRUE; +; + break;} +case 48: +{ + if ( (isign || !make_neg) + && !(isign && !make_neg)) /* but not both! */ + f = -f; + if(!rhs_store(f, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + isign = 0; +; + break;} +case 56: +{ + Within_sos_decl1 = Within_sos_decl; +; + break;} +case 58: +{ + if((!Within_int_decl) && (!Within_sec_decl) && (!Within_sos_decl1)) { + lp_yyerror("parse error"); + YYABORT; + } + SOStype = SOStype0; + check_int_sec_sos_decl(Within_int_decl, Within_sec_decl, Within_sos_decl1 = (Within_sos_decl1 ? 1 : 0)); +; + break;} +case 59: +{ + if((Within_sos_decl1) && (SOStype == 0)) + { + lp_yyerror("Unsupported SOS type (0)"); + YYABORT; + } +; + break;} +case 63: +{ + strcpy(Last_var0, Last_var); +; + break;} +case 65: +{ + if(Within_sos_decl1) { + set_sos_type(SOStype); + set_sos_weight(SOSweight, 1); + } +; + break;} +case 66: +{ + if((Within_sos_decl1) && (!SOStype)) + { + set_sos_type(SOStype = (short) (f + .1)); + } + else + { + lp_yyerror("SOS type not expected"); + YYABORT; + } +; + break;} +case 68: +{ + set_sos_weight(SOSweight, 1); +; + break;} +case 69: +{ + set_sos_weight((int) (f + .1), 1); +; + break;} +case 76: +{ + if(Within_sos_decl1 == 1) + { + char buf[16]; + + SOSweight++; + sprintf(buf, "SOS%d", SOSweight); + storevarandweight(buf); + + check_int_sec_sos_decl(Within_int_decl, Within_sec_decl, 2); + Within_sos_decl1 = 2; + weight = 0; + SOSNr = 0; + } + + storevarandweight(Last_var); + + if(Within_sos_decl1 == 2) + { + SOSNr++; + weight = SOSNr; + set_sos_weight(weight, 2); + } +; + break;} +case 77: +{ + if(!Within_sos_decl1) { + lp_yyerror("parse error"); + YYABORT; + } + if(Within_sos_decl1 == 1) + strcpy(Last_var0, Last_var); + if(Within_sos_decl1 == 2) + { + storevarandweight(Last_var); + SOSNr++; + weight = SOSNr; + set_sos_weight(weight, 2); + } +; + break;} +case 78: +{ + if(Within_sos_decl1 == 1) + { + char buf[16]; + + SOSweight++; + sprintf(buf, "SOS%d", SOSweight); + storevarandweight(buf); + + check_int_sec_sos_decl(Within_int_decl, Within_sec_decl, 2); + Within_sos_decl1 = 2; + weight = 0; + SOSNr = 0; + + storevarandweight(Last_var0); + SOSNr++; + } + + weight = (int) (f + .1); + set_sos_weight(weight, 2); +; + break;} +case 79: +{ /* SOS name */ + if(Within_sos_decl1 == 1) + { + storevarandweight(Last_var0); + set_sos_type(SOStype); + check_int_sec_sos_decl(Within_int_decl, Within_sec_decl, 2); + Within_sos_decl1 = 2; + weight = 0; + SOSNr = 0; + SOSweight++; + } +; + break;} +} + /* the action file gets copied in in place of this dollarsign */ + + + lp_yyvsp -= lp_yylen; + lp_yyssp -= lp_yylen; +#ifdef YYLSP_NEEDED + lp_yylsp -= lp_yylen; +#endif + +#if YYDEBUG != 0 + if (lp_yydebug) + { + short *ssp1 = lp_yyss - 1; + fprintf (stderr, "state stack now"); + while (ssp1 != lp_yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + + *++lp_yyvsp = lp_yyval; + +#ifdef YYLSP_NEEDED + lp_yylsp++; + if (lp_yylen == 0) + { + lp_yylsp->first_line = lp_yylloc.first_line; + lp_yylsp->first_column = lp_yylloc.first_column; + lp_yylsp->last_line = (lp_yylsp-1)->last_line; + lp_yylsp->last_column = (lp_yylsp-1)->last_column; + lp_yylsp->text = 0; + } + else + { + lp_yylsp->last_line = (lp_yylsp+lp_yylen-1)->last_line; + lp_yylsp->last_column = (lp_yylsp+lp_yylen-1)->last_column; + } +#endif + + /* Now "shift" the result of the reduction. + Determine what state that goes to, + based on the state we popped back to + and the rule number reduced by. */ + + lp_yyn = lp_yyr1[lp_yyn]; + + lp_yystate = lp_yypgoto[lp_yyn - YYNTBASE] + *lp_yyssp; + if (lp_yystate >= 0 && lp_yystate <= YYLAST && lp_yycheck[lp_yystate] == *lp_yyssp) + lp_yystate = lp_yytable[lp_yystate]; + else + lp_yystate = lp_yydefgoto[lp_yyn - YYNTBASE]; + + goto lp_yynewstate; + +lp_yyerrlab: /* here on detecting error */ + + if (! lp_yyerrstatus) + /* If not already recovering from an error, report this error. */ + { + ++lp_yynerrs; + +#ifdef YYERROR_VERBOSE + lp_yyn = lp_yypact[lp_yystate]; + + if (lp_yyn > YYFLAG && lp_yyn < YYLAST) + { + int size = 0; + char *msg; + int x, count; + + count = 0; + /* Start X at -lp_yyn if nec to avoid negative indexes in lp_yycheck. */ + for (x = (lp_yyn < 0 ? -lp_yyn : 0); + x < (sizeof(lp_yytname) / sizeof(char *)); x++) + if (lp_yycheck[x + lp_yyn] == x) + size += strlen(lp_yytname[x]) + 15, count++; + msg = (char *) malloc(size + 15); + if (msg != 0) + { + strcpy(msg, "parse error"); + + if (count < 5) + { + count = 0; + for (x = (lp_yyn < 0 ? -lp_yyn : 0); + x < (sizeof(lp_yytname) / sizeof(char *)); x++) + if (lp_yycheck[x + lp_yyn] == x) + { + strcat(msg, count == 0 ? ", expecting `" : " or `"); + strcat(msg, lp_yytname[x]); + strcat(msg, "'"); + count++; + } + } + lp_yyerror(msg); + free(msg); + } + else + lp_yyerror ("parse error; also virtual memory exceeded"); + } + else +#endif /* YYERROR_VERBOSE */ + lp_yyerror("parse error"); + } + + goto lp_yyerrlab1; +lp_yyerrlab1: /* here on error raised explicitly by an action */ + + if (lp_yyerrstatus == 3) + { + /* if just tried and failed to reuse lookahead token after an error, discard it. */ + + /* return failure if at end of input */ + if (lp_yychar == YYEOF) + YYABORT; + +#if YYDEBUG != 0 + if (lp_yydebug) + fprintf(stderr, "Discarding token %d (%s).\n", lp_yychar, lp_yytname[lp_yychar1]); +#endif + + lp_yychar = YYEMPTY; + } + + /* Else will try to reuse lookahead token + after shifting the error token. */ + + lp_yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto lp_yyerrhandle; + +lp_yyerrdefault: /* current state does not do anything special for the error token. */ + +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + lp_yyn = lp_yydefact[lp_yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/ + if (lp_yyn) goto lp_yydefault; +#endif + +lp_yyerrpop: /* pop the current state because it cannot handle the error token */ + + if (lp_yyssp == lp_yyss) YYABORT; + lp_yyvsp--; + lp_yystate = *--lp_yyssp; +#ifdef YYLSP_NEEDED + lp_yylsp--; +#endif + +#if YYDEBUG != 0 + if (lp_yydebug) + { + short *ssp1 = lp_yyss - 1; + fprintf (stderr, "Error: state stack now"); + while (ssp1 != lp_yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + +lp_yyerrhandle: + + lp_yyn = lp_yypact[lp_yystate]; + if (lp_yyn == YYFLAG) + goto lp_yyerrdefault; + + lp_yyn += YYTERROR; + if (lp_yyn < 0 || lp_yyn > YYLAST || lp_yycheck[lp_yyn] != YYTERROR) + goto lp_yyerrdefault; + + lp_yyn = lp_yytable[lp_yyn]; + if (lp_yyn < 0) + { + if (lp_yyn == YYFLAG) + goto lp_yyerrpop; + lp_yyn = -lp_yyn; + goto lp_yyreduce; + } + else if (lp_yyn == 0) + goto lp_yyerrpop; + + if (lp_yyn == YYFINAL) + YYACCEPT; + +#if YYDEBUG != 0 + if (lp_yydebug) + fprintf(stderr, "Shifting error token, "); +#endif + + *++lp_yyvsp = lp_yylval; +#ifdef YYLSP_NEEDED + *++lp_yylsp = lp_yylloc; +#endif + + lp_yystate = lp_yyn; + goto lp_yynewstate; + + lp_yyacceptlab: + /* YYACCEPT comes here. */ + if (lp_yyfree_stacks) + { + free (lp_yyss); + free (lp_yyvs); +#ifdef YYLSP_NEEDED + free (lp_yyls); +#endif + } + return 0; + + lp_yyabortlab: + /* YYABORT comes here. */ + if (lp_yyfree_stacks) + { + free (lp_yyss); + free (lp_yyvs); +#ifdef YYLSP_NEEDED + free (lp_yyls); +#endif + } + return 1; +} + + +static void lp_yy_delete_allocated_memory(void) +{ + /* free memory allocated by flex. Otherwise some memory is not freed. + This is a bit tricky. There is not much documentation about this, but a lot of + reports of memory that keeps allocated */ + + /* If you get errors on this function call, just comment it. This will only result + in some memory that is not being freed. */ + +# if defined YY_CURRENT_BUFFER + /* flex defines the macro YY_CURRENT_BUFFER, so you should only get here if lp_rlp.h is + generated by flex */ + /* lex doesn't define this macro and thus should not come here, but lex doesn't has + this memory leak also ...*/ + + lp_yy_delete_buffer(YY_CURRENT_BUFFER); /* comment this line if you have problems with it */ + lp_yy_init = 1; /* make sure that the next time memory is allocated again */ + lp_yy_start = 0; +# endif +} + +static int parse(void) +{ + return(lp_yyparse()); +} + +lprec * __WINAPI read_lp(FILE *filename, int verbose, char *lp_name) +{ + lp_yyin = filename; + return(yacc_read(verbose, lp_name, &lp_yylineno, parse, lp_yy_delete_allocated_memory)); +} + +lprec * __WINAPI read_LP(char *filename, int verbose, char *lp_name) +{ + FILE *fpin; + lprec *lp = NULL; + + if((fpin = fopen(filename, "r")) != NULL) { + lp = read_lp(fpin, verbose, lp_name); + fclose(fpin); + } + return(lp); +} Index: src/tools/solver/lp_solve/lp_rlp.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_rlp.h diff -N src/tools/solver/lp_solve/lp_rlp.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_rlp.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1861 @@ +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header: /var/lib/cvs/oe-netbook/gnumeric/gnumeric-1.3.1/1.3.1-20040809.patch,v 1.1 2004/08/10 23:40:01 wookey Exp $ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include +#include +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define lp_yyconst const +#else +#define lp_yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN lp_yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((lp_yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE lp_yyrestart( lp_yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct lp_yy_buffer_state *YY_BUFFER_STATE; + +extern int lp_yyleng; +extern FILE *lp_yyin, *lp_yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * lp_yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the lp_yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define lp_yyless(n) \ + do \ + { \ + /* Undo effects of setting up lp_yytext. */ \ + *lp_yy_cp = lp_yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + lp_yy_c_buf_p = lp_yy_cp = lp_yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up lp_yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) lp_yyunput( c, lp_yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int lp_yy_size_t; + + +struct lp_yy_buffer_state + { + FILE *lp_yy_input_file; + + char *lp_yy_ch_buf; /* input buffer */ + char *lp_yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + lp_yy_size_t lp_yy_buf_size; + + /* Number of characters read into lp_yy_ch_buf, not including EOB + * characters. + */ + int lp_yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int lp_yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int lp_yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int lp_yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int lp_yy_fill_buffer; + + int lp_yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via lp_yyrestart()), so that the user can continue scanning by + * just pointing lp_yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE lp_yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER lp_yy_current_buffer + + +/* lp_yy_hold_char holds the character lost when lp_yytext is formed. */ +static char lp_yy_hold_char; + +static int lp_yy_n_chars; /* number of characters read into lp_yy_ch_buf */ + + +int lp_yyleng; + +/* Points to current character in buffer. */ +static char *lp_yy_c_buf_p = (char *) 0; +static int lp_yy_init = 1; /* whether we need to initialize */ +static int lp_yy_start = 0; /* start state number */ + +/* Flag which is used to allow lp_yywrap()'s to do buffer switches + * instead of setting up a fresh lp_yyin. A bit of a hack ... + */ +static int lp_yy_did_buffer_switch_on_eof; + +void lp_yyrestart YY_PROTO(( FILE *input_file )); + +void lp_yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void lp_yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE lp_yy_create_buffer YY_PROTO(( FILE *file, int size )); +void lp_yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void lp_yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void lp_yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER lp_yy_flush_buffer( lp_yy_current_buffer ) + +YY_BUFFER_STATE lp_yy_scan_buffer YY_PROTO(( char *base, lp_yy_size_t size )); +YY_BUFFER_STATE lp_yy_scan_string YY_PROTO(( lp_yyconst char *lp_yy_str )); +YY_BUFFER_STATE lp_yy_scan_bytes YY_PROTO(( lp_yyconst char *bytes, int len )); + +static void *lp_yy_flex_alloc YY_PROTO(( lp_yy_size_t )); +static void *lp_yy_flex_realloc YY_PROTO(( void *, lp_yy_size_t )); +static void lp_yy_flex_free YY_PROTO(( void * )); + +#define lp_yy_new_buffer lp_yy_create_buffer + +#define lp_yy_set_interactive(is_interactive) \ + { \ + if ( ! lp_yy_current_buffer ) \ + lp_yy_current_buffer = lp_yy_create_buffer( lp_yyin, YY_BUF_SIZE ); \ + lp_yy_current_buffer->lp_yy_is_interactive = is_interactive; \ + } + +#define lp_yy_set_bol(at_bol) \ + { \ + if ( ! lp_yy_current_buffer ) \ + lp_yy_current_buffer = lp_yy_create_buffer( lp_yyin, YY_BUF_SIZE ); \ + lp_yy_current_buffer->lp_yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (lp_yy_current_buffer->lp_yy_at_bol) + + +#define YY_USES_REJECT +typedef unsigned char YY_CHAR; +FILE *lp_yyin = (FILE *) 0, *lp_yyout = (FILE *) 0; +typedef int lp_yy_state_type; +#define YY_FLEX_LEX_COMPAT +extern int lp_yylineno; +int lp_yylineno = 1; +extern char lp_yytext[]; + + +static lp_yy_state_type lp_yy_get_previous_state YY_PROTO(( void )); +static lp_yy_state_type lp_yy_try_NUL_trans YY_PROTO(( lp_yy_state_type current_state )); +static int lp_yy_get_next_buffer YY_PROTO(( void )); +static void lp_yy_fatal_error YY_PROTO(( lp_yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up lp_yytext. + */ +#define YY_DO_BEFORE_ACTION \ + lp_yytext_ptr = lp_yy_bp; \ + lp_yyleng = (int) (lp_yy_cp - lp_yy_bp); \ + lp_yy_hold_char = *lp_yy_cp; \ + *lp_yy_cp = '\0'; \ + if ( lp_yyleng + lp_yy_more_offset >= YYLMAX ) \ + YY_FATAL_ERROR( "token too large, exceeds YYLMAX" ); \ + lp_yy_flex_strncpy( &lp_yytext[lp_yy_more_offset], lp_yytext_ptr, lp_yyleng + 1 ); \ + lp_yyleng += lp_yy_more_offset; \ + lp_yy_prev_more_offset = lp_yy_more_offset; \ + lp_yy_more_offset = 0; \ + lp_yy_c_buf_p = lp_yy_cp; + +#define YY_NUM_RULES 30 +#define YY_END_OF_BUFFER 31 +static lp_yyconst short int lp_yy_acclist[145] = + { 0, + 26, 26, 31, 29, 30, 10, 17, 29, 30, 10, + 17, 30, 25, 29, 30, 17, 29, 30, 11, 29, + 30, 29, 30, 29, 30, 14, 15, 29, 30, 24, + 29, 30, 28, 29, 30, 26, 29, 30, 26, 29, + 30, 27, 29, 30, 23, 29, 30, 23, 29, 30, + 10, 17, 29, 30, 23, 29, 30, 23, 29, 30, + 3, 30, 4, 30, 3, 5, 30, 3, 30, 9, + 30, 7, 30, 8, 9, 30, 10, 17, 17, 17, + 15, 1, 6, 14, 15, 26, 27, 23, 22, 23, + 23, 10, 17, 23, 23, 23, 2, 15, 21, 23, + + 23, 18, 23, 19, 23, 23, 23, 20, 23, 16, + 23, 13, 22, 23, 12, 22, 23, 18, 19, 20, + 23, 19, 23, 20, 23, 20, 23, 13, 23, 12, + 23, 20, 23, 23, 23, 23, 23, 23, 18, 23, + 23, 23, 18, 16 + } ; + +static lp_yyconst short int lp_yy_accept[123] = + { 0, + 1, 2, 3, 3, 3, 3, 3, 4, 6, 10, + 13, 16, 19, 22, 24, 26, 30, 33, 36, 39, + 42, 45, 48, 51, 55, 58, 61, 63, 65, 68, + 70, 72, 74, 77, 79, 80, 81, 81, 82, 83, + 84, 84, 86, 86, 87, 88, 89, 90, 90, 91, + 92, 94, 94, 94, 95, 96, 97, 98, 98, 98, + 99, 100, 100, 101, 102, 102, 102, 102, 104, 106, + 107, 108, 110, 111, 112, 112, 114, 115, 115, 117, + 118, 119, 120, 121, 122, 124, 126, 128, 128, 129, + 130, 131, 132, 132, 133, 134, 134, 134, 135, 136, + + 136, 137, 137, 137, 138, 139, 139, 141, 141, 141, + 142, 143, 144, 144, 145, 145, 145, 145, 145, 145, + 145, 145 + } ; + +static lp_yyconst int lp_yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 5, 5, 5, 5, 5, 1, + 1, 6, 7, 8, 9, 10, 11, 12, 13, 13, + 12, 12, 12, 12, 12, 12, 12, 14, 15, 16, + 17, 18, 1, 5, 19, 20, 21, 20, 22, 23, + 24, 20, 25, 20, 20, 20, 26, 27, 28, 20, + 20, 29, 30, 31, 32, 20, 20, 33, 34, 35, + 5, 1, 5, 5, 5, 1, 19, 20, 21, 20, + + 22, 23, 24, 20, 25, 20, 20, 20, 26, 27, + 28, 20, 20, 29, 30, 31, 32, 20, 20, 33, + 34, 35, 5, 1, 5, 5, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static lp_yyconst int lp_yy_meta[36] = + { 0, + 1, 2, 2, 2, 3, 1, 2, 1, 2, 3, + 3, 3, 3, 4, 1, 4, 1, 1, 3, 3, + 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3 + } ; + +static lp_yyconst short int lp_yy_base[127] = + { 0, + 0, 34, 34, 38, 42, 44, 268, 269, 47, 58, + 269, 242, 269, 40, 52, 56, 269, 269, 249, 269, + 248, 41, 56, 80, 63, 72, 269, 269, 269, 252, + 269, 269, 269, 89, 232, 99, 229, 99, 269, 269, + 61, 103, 110, 269, 269, 81, 229, 0, 93, 104, + 0, 214, 102, 113, 119, 118, 269, 217, 124, 126, + 269, 221, 139, 140, 207, 216, 205, 135, 144, 145, + 152, 158, 208, 218, 161, 217, 153, 171, 203, 162, + 175, 269, 180, 167, 178, 170, 182, 163, 269, 185, + 269, 186, 156, 149, 190, 131, 122, 189, 193, 124, + + 200, 115, 97, 199, 204, 85, 211, 77, 155, 220, + 228, 269, 68, 269, 60, 53, 44, 43, 11, 9, + 269, 244, 249, 253, 256, 259 + } ; + +static lp_yyconst short int lp_yy_def[127] = + { 0, + 121, 1, 122, 122, 123, 123, 121, 121, 121, 121, + 121, 124, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 125, 125, 121, 125, 125, 121, 121, 121, 121, + 121, 121, 121, 121, 124, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 125, 121, 126, 125, 125, + 24, 121, 121, 125, 125, 125, 121, 121, 121, 121, + 121, 126, 125, 125, 121, 121, 121, 125, 125, 125, + 125, 125, 121, 121, 121, 121, 125, 121, 121, 125, + 121, 121, 121, 125, 125, 125, 125, 121, 121, 125, + 121, 125, 121, 121, 125, 121, 121, 125, 125, 121, + + 125, 121, 121, 125, 125, 121, 125, 121, 121, 125, + 125, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 0, 121, 121, 121, 121, 121 + } ; + +static lp_yyconst short int lp_yy_nxt[305] = + { 0, + 8, 9, 10, 9, 8, 11, 12, 13, 12, 14, + 15, 16, 16, 17, 18, 19, 20, 21, 22, 22, + 22, 22, 22, 22, 22, 23, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 24, 28, 29, 82, 30, + 28, 29, 120, 30, 32, 33, 32, 33, 34, 34, + 34, 38, 38, 35, 47, 35, 48, 39, 25, 34, + 34, 34, 40, 26, 35, 41, 35, 42, 42, 47, + 119, 48, 38, 38, 49, 118, 47, 43, 48, 117, + 50, 51, 34, 34, 116, 47, 35, 48, 35, 54, + 34, 34, 34, 55, 47, 35, 48, 35, 115, 56, + + 36, 36, 36, 113, 52, 36, 47, 36, 48, 53, + 38, 38, 41, 112, 42, 42, 59, 47, 59, 48, + 43, 60, 60, 66, 43, 63, 47, 109, 48, 67, + 64, 47, 47, 48, 48, 60, 60, 60, 60, 69, + 75, 78, 108, 68, 70, 106, 103, 72, 47, 71, + 48, 102, 76, 79, 48, 48, 84, 47, 47, 48, + 48, 94, 75, 77, 80, 47, 47, 48, 48, 85, + 87, 47, 78, 48, 89, 47, 114, 48, 90, 100, + 47, 86, 48, 47, 91, 48, 96, 92, 114, 97, + 95, 47, 94, 48, 87, 47, 93, 48, 47, 47, + + 48, 48, 47, 47, 48, 48, 47, 69, 48, 98, + 99, 101, 47, 47, 48, 48, 61, 47, 104, 48, + 110, 75, 105, 104, 47, 111, 48, 105, 107, 78, + 61, 47, 88, 76, 83, 48, 82, 81, 74, 73, + 65, 79, 61, 48, 27, 27, 27, 27, 27, 31, + 31, 31, 31, 31, 36, 58, 37, 36, 46, 46, + 46, 62, 57, 62, 45, 44, 37, 121, 7, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + + 121, 121, 121, 121 + } ; + +static lp_yyconst short int lp_yy_chk[305] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 3, 3, 120, 3, + 4, 4, 119, 4, 5, 5, 6, 6, 9, 9, + 9, 14, 14, 9, 22, 9, 22, 15, 2, 10, + 10, 10, 15, 2, 10, 16, 10, 16, 16, 23, + 118, 23, 41, 41, 23, 117, 25, 16, 25, 116, + 23, 24, 24, 24, 115, 26, 24, 26, 24, 25, + 34, 34, 34, 26, 46, 34, 46, 34, 113, 26, + + 36, 36, 36, 108, 24, 36, 49, 36, 49, 24, + 38, 38, 42, 106, 42, 42, 43, 50, 43, 50, + 38, 43, 43, 53, 42, 49, 54, 103, 54, 53, + 50, 56, 55, 56, 55, 59, 59, 60, 60, 55, + 63, 64, 102, 54, 55, 100, 97, 56, 68, 55, + 68, 96, 63, 64, 63, 64, 68, 69, 70, 69, + 70, 94, 75, 63, 64, 71, 77, 71, 77, 70, + 72, 72, 78, 72, 75, 80, 109, 80, 77, 93, + 84, 71, 84, 86, 78, 86, 85, 80, 109, 88, + 84, 85, 83, 85, 87, 87, 81, 87, 90, 92, + + 90, 92, 98, 95, 98, 95, 99, 85, 99, 90, + 92, 95, 104, 101, 104, 101, 79, 105, 98, 105, + 104, 110, 99, 98, 107, 105, 107, 99, 101, 111, + 76, 74, 73, 110, 67, 110, 66, 65, 62, 58, + 52, 111, 47, 111, 122, 122, 122, 122, 122, 123, + 123, 123, 123, 123, 124, 37, 35, 124, 125, 125, + 125, 126, 30, 126, 21, 19, 12, 7, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + + 121, 121, 121, 121 + } ; + +static lp_yy_state_type lp_yy_state_buf[YY_BUF_SIZE + 2], *lp_yy_state_ptr; +static char *lp_yy_full_match; +static int lp_yy_lp; +#define REJECT \ +{ \ +*lp_yy_cp = lp_yy_hold_char; /* undo effects of setting up lp_yytext */ \ +lp_yy_cp = lp_yy_full_match; /* restore poss. backed-over text */ \ +++lp_yy_lp; \ +goto find_rule; \ +} +static int lp_yy_more_offset = 0; +static int lp_yy_prev_more_offset = 0; +#define lp_yymore() (lp_yy_more_offset = lp_yy_flex_strlen( lp_yytext )) +#define YY_NEED_STRLEN +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET \ + { \ + lp_yy_more_offset = lp_yy_prev_more_offset; \ + lp_yyleng -= lp_yy_more_offset; \ + } +#ifndef YYLMAX +#define YYLMAX 8192 +#endif + +char lp_yytext[YYLMAX]; +char *lp_yytext_ptr; +#define INITIAL 0 +#define COMMENT 1 + +#define LINECOMMENT 2 + + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int lp_yywrap YY_PROTO(( void )); +#else +extern int lp_yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void lp_yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef lp_yytext_ptr +static void lp_yy_flex_strncpy YY_PROTO(( char *, lp_yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int lp_yy_flex_strlen YY_PROTO(( lp_yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int lp_yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int lp_yy_start_stack_ptr = 0; +static int lp_yy_start_stack_depth = 0; +static int *lp_yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void lp_yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void lp_yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int lp_yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( lp_yytext, lp_yyleng, 1, lp_yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( lp_yy_current_buffer->lp_yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( lp_yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( lp_yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, lp_yyin )) == 0) \ + && ferror( lp_yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "lp_yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef lp_yyterminate +#define lp_yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) lp_yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int lp_yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after lp_yytext and lp_yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + if ( lp_yyleng > 0 ) \ + lp_yy_current_buffer->lp_yy_at_bol = \ + (lp_yytext[lp_yyleng - 1] == '\n'); \ + YY_USER_ACTION + +YY_DECL + { + register lp_yy_state_type lp_yy_current_state; + register char *lp_yy_cp, *lp_yy_bp; + register int lp_yy_act; + + + + if ( lp_yy_init ) + { + lp_yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! lp_yy_start ) + lp_yy_start = 1; /* first start state */ + + if ( ! lp_yyin ) + lp_yyin = stdin; + + if ( ! lp_yyout ) + lp_yyout = stdout; + + if ( ! lp_yy_current_buffer ) + lp_yy_current_buffer = + lp_yy_create_buffer( lp_yyin, YY_BUF_SIZE ); + + lp_yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + lp_yy_cp = lp_yy_c_buf_p; + + /* Support of lp_yytext. */ + *lp_yy_cp = lp_yy_hold_char; + + /* lp_yy_bp points to the position in lp_yy_ch_buf of the start of + * the current run. + */ + lp_yy_bp = lp_yy_cp; + + lp_yy_current_state = lp_yy_start; + lp_yy_current_state += YY_AT_BOL(); + lp_yy_state_ptr = lp_yy_state_buf; + *lp_yy_state_ptr++ = lp_yy_current_state; +lp_yy_match: + do + { + register YY_CHAR lp_yy_c = lp_yy_ec[YY_SC_TO_UI(*lp_yy_cp)]; + while ( lp_yy_chk[lp_yy_base[lp_yy_current_state] + lp_yy_c] != lp_yy_current_state ) + { + lp_yy_current_state = (int) lp_yy_def[lp_yy_current_state]; + if ( lp_yy_current_state >= 122 ) + lp_yy_c = lp_yy_meta[(unsigned int) lp_yy_c]; + } + lp_yy_current_state = lp_yy_nxt[lp_yy_base[lp_yy_current_state] + (unsigned int) lp_yy_c]; + *lp_yy_state_ptr++ = lp_yy_current_state; + ++lp_yy_cp; + } + while ( lp_yy_base[lp_yy_current_state] != 269 ); + +lp_yy_find_action: + lp_yy_current_state = *--lp_yy_state_ptr; + lp_yy_lp = lp_yy_accept[lp_yy_current_state]; +find_rule: /* we branch to this label when backing up */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( lp_yy_lp && lp_yy_lp < lp_yy_accept[lp_yy_current_state + 1] ) + { + lp_yy_act = lp_yy_acclist[lp_yy_lp]; + { + lp_yy_full_match = lp_yy_cp; + break; + } + } + --lp_yy_cp; + lp_yy_current_state = *--lp_yy_state_ptr; + lp_yy_lp = lp_yy_accept[lp_yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + if ( lp_yy_act != YY_END_OF_BUFFER ) + { + int lp_yyl; + for ( lp_yyl = 0; lp_yyl < lp_yyleng; ++lp_yyl ) + if ( lp_yytext[lp_yyl] == '\n' ) + ++lp_yylineno; + } + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( lp_yy_act ) + { /* beginning of action switch */ +case 1: +YY_RULE_SETUP +{ + BEGIN COMMENT; +} /* begin skip comment */ + YY_BREAK +case 2: +YY_RULE_SETUP +{ + BEGIN INITIAL; +} /* end skip comment */ + YY_BREAK +case 3: +YY_RULE_SETUP +{ +} + YY_BREAK +case 4: +YY_RULE_SETUP +{ +} + YY_BREAK +case 5: +YY_RULE_SETUP +{ +} + YY_BREAK +case 6: +YY_RULE_SETUP +{ + BEGIN LINECOMMENT; +} /* begin skip LINECOMMENT */ + YY_BREAK +case 7: +YY_RULE_SETUP +{ + BEGIN INITIAL; +} /* end skip LINECOMMENT */ + YY_BREAK +case 8: +YY_RULE_SETUP +{ + BEGIN INITIAL; +} /* end skip LINECOMMENT */ + YY_BREAK +case 9: +YY_RULE_SETUP +{ +} + YY_BREAK +case 10: +YY_RULE_SETUP +{ +} + YY_BREAK +case 11: +YY_RULE_SETUP +{ + return(COMMA); +} + YY_BREAK +case 12: +YY_RULE_SETUP +{ + return(MINIMISE); +} + YY_BREAK +case 13: +YY_RULE_SETUP +{ + return(MAXIMISE); +} + YY_BREAK +case 14: +YY_RULE_SETUP +{ + f = atof((char *)lp_yytext); + return(INTCONS); +} /* f contains the last float */ + YY_BREAK +case 15: +YY_RULE_SETUP +{ + f = atof((char *)lp_yytext); + return(CONS); +} /* f contains the last float */ + YY_BREAK +case 16: +YY_RULE_SETUP +{ + char *ptr; + + f = DEF_INFINITE; + Sign = 0; + ptr = (char *)lp_yytext; + while (isspace(*ptr)) ptr++; + if(*ptr == '-') + Sign = 1; + return(INF); +} /* f contains the last float */ + YY_BREAK +case 17: +YY_RULE_SETUP +{ + Sign = 0; + for(x = 0; x < lp_yyleng; x++) + if(lp_yytext[x] == '-' || lp_yytext[x] == '+') + Sign = (Sign == (lp_yytext[x] == '+')); + return (SIGN); + /* Sign is TRUE if the sign-string + represents a '-'. Otherwise Sign + is FALSE */ +} + YY_BREAK +case 18: +YY_RULE_SETUP +{ + if((!Within_int_decl) && (!Within_sec_decl) && (!Within_sos_decl)) { + Within_int_decl = TRUE; + Within_sos_decl1 = FALSE; + } + return(SEC_INT); +} + YY_BREAK +case 19: +YY_RULE_SETUP +{ + if((!Within_int_decl) && (!Within_sec_decl) && (!Within_sos_decl)) { + Within_sec_decl = TRUE; + Within_sos_decl1 = FALSE; + } + return(SEC_SEC); +} + YY_BREAK +case 20: +YY_RULE_SETUP +{ + if(!Within_sos_decl) + SOStype0 = (short)atoi(((char *)lp_yytext) + 3); + if((!Within_int_decl) && (!Within_sec_decl) && (!Within_sos_decl)) + Within_sos_decl = TRUE; + return(SEC_SOS); +} + YY_BREAK +case 21: +YY_RULE_SETUP +{ + strcpy(Last_var, (char *)lp_yytext); + Last_var[strlen(Last_var) - 2] = 0; + return(SOSDESCR); +} + YY_BREAK +case 22: +YY_RULE_SETUP +{ + strcpy(Last_var, (char *)lp_yytext); + Last_var[strlen(Last_var) - 1] = 0; + return(VARIABLECOLON); +} + YY_BREAK +case 23: +YY_RULE_SETUP +{ + strcpy(Last_var, (char *)lp_yytext); + return(VAR); +} + YY_BREAK +case 24: +YY_RULE_SETUP +{ + return (COLON); +} + YY_BREAK +case 25: +YY_RULE_SETUP +{ + return(AR_M_OP); +} + YY_BREAK +case 26: +YY_RULE_SETUP +{ + return(RE_OPLE); +} + YY_BREAK +case 27: +YY_RULE_SETUP +{ + return(RE_OPGE); +} + YY_BREAK +case 28: +YY_RULE_SETUP +{ + Within_int_decl = Within_sec_decl = Within_sos_decl = FALSE; + check_int_sec_sos_decl(Within_int_decl, Within_sec_decl, Within_sos_decl); + return(END_C); +} + YY_BREAK +case 29: +YY_RULE_SETUP +{ + report(NULL, CRITICAL, "LEX ERROR : %s lineno %d\n", lp_yytext, lp_yylineno); + return(UNDEFINED); +} + YY_BREAK +case 30: +YY_RULE_SETUP +ECHO; + YY_BREAK + case YY_STATE_EOF(INITIAL): + case YY_STATE_EOF(COMMENT): + case YY_STATE_EOF(LINECOMMENT): + lp_yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int lp_yy_amount_of_matched_text = (int) (lp_yy_cp - lp_yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *lp_yy_cp = lp_yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( lp_yy_current_buffer->lp_yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed lp_yyin at a new source and called + * lp_yylex(). If so, then we have to assure + * consistency between lp_yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + lp_yy_n_chars = lp_yy_current_buffer->lp_yy_n_chars; + lp_yy_current_buffer->lp_yy_input_file = lp_yyin; + lp_yy_current_buffer->lp_yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for lp_yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since lp_yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( lp_yy_c_buf_p <= &lp_yy_current_buffer->lp_yy_ch_buf[lp_yy_n_chars] ) + { /* This was really a NUL. */ + lp_yy_state_type lp_yy_next_state; + + lp_yy_c_buf_p = lp_yytext_ptr + lp_yy_amount_of_matched_text; + + lp_yy_current_state = lp_yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * lp_yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + lp_yy_next_state = lp_yy_try_NUL_trans( lp_yy_current_state ); + + lp_yy_bp = lp_yytext_ptr + YY_MORE_ADJ; + + if ( lp_yy_next_state ) + { + /* Consume the NUL. */ + lp_yy_cp = ++lp_yy_c_buf_p; + lp_yy_current_state = lp_yy_next_state; + goto lp_yy_match; + } + + else + { + lp_yy_cp = lp_yy_c_buf_p; + goto lp_yy_find_action; + } + } + + else switch ( lp_yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + lp_yy_did_buffer_switch_on_eof = 0; + + if ( lp_yywrap() ) + { + /* Note: because we've taken care in + * lp_yy_get_next_buffer() to have set up + * lp_yytext, we can now set up + * lp_yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + lp_yy_c_buf_p = lp_yytext_ptr + YY_MORE_ADJ; + + lp_yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! lp_yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + lp_yy_c_buf_p = + lp_yytext_ptr + lp_yy_amount_of_matched_text; + + lp_yy_current_state = lp_yy_get_previous_state(); + + lp_yy_cp = lp_yy_c_buf_p; + lp_yy_bp = lp_yytext_ptr + YY_MORE_ADJ; + goto lp_yy_match; + + case EOB_ACT_LAST_MATCH: + lp_yy_c_buf_p = + &lp_yy_current_buffer->lp_yy_ch_buf[lp_yy_n_chars]; + + lp_yy_current_state = lp_yy_get_previous_state(); + + lp_yy_cp = lp_yy_c_buf_p; + lp_yy_bp = lp_yytext_ptr + YY_MORE_ADJ; + goto lp_yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of lp_yylex */ + + +/* lp_yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int lp_yy_get_next_buffer() + { + register char *dest = lp_yy_current_buffer->lp_yy_ch_buf; + register char *source = lp_yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( lp_yy_c_buf_p > &lp_yy_current_buffer->lp_yy_ch_buf[lp_yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( lp_yy_current_buffer->lp_yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( lp_yy_c_buf_p - lp_yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (lp_yy_c_buf_p - lp_yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( lp_yy_current_buffer->lp_yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + lp_yy_current_buffer->lp_yy_n_chars = lp_yy_n_chars = 0; + + else + { + int num_to_read = + lp_yy_current_buffer->lp_yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = lp_yy_current_buffer; + + int lp_yy_c_buf_p_offset = + (int) (lp_yy_c_buf_p - b->lp_yy_ch_buf); + + if ( b->lp_yy_is_our_buffer ) + { + int new_size = b->lp_yy_buf_size * 2; + + if ( new_size <= 0 ) + b->lp_yy_buf_size += b->lp_yy_buf_size / 8; + else + b->lp_yy_buf_size *= 2; + + b->lp_yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + lp_yy_flex_realloc( (void *) b->lp_yy_ch_buf, + b->lp_yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->lp_yy_ch_buf = 0; + + if ( ! b->lp_yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + lp_yy_c_buf_p = &b->lp_yy_ch_buf[lp_yy_c_buf_p_offset]; + + num_to_read = lp_yy_current_buffer->lp_yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&lp_yy_current_buffer->lp_yy_ch_buf[number_to_move]), + lp_yy_n_chars, num_to_read ); + + lp_yy_current_buffer->lp_yy_n_chars = lp_yy_n_chars; + } + + if ( lp_yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + lp_yyrestart( lp_yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + lp_yy_current_buffer->lp_yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + lp_yy_n_chars += number_to_move; + lp_yy_current_buffer->lp_yy_ch_buf[lp_yy_n_chars] = YY_END_OF_BUFFER_CHAR; + lp_yy_current_buffer->lp_yy_ch_buf[lp_yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + lp_yytext_ptr = &lp_yy_current_buffer->lp_yy_ch_buf[0]; + + return ret_val; + } + + +/* lp_yy_get_previous_state - get the state just before the EOB char was reached */ + +static lp_yy_state_type lp_yy_get_previous_state() + { + register lp_yy_state_type lp_yy_current_state; + register char *lp_yy_cp; + + lp_yy_current_state = lp_yy_start; + lp_yy_current_state += YY_AT_BOL(); + lp_yy_state_ptr = lp_yy_state_buf; + *lp_yy_state_ptr++ = lp_yy_current_state; + + for ( lp_yy_cp = lp_yytext_ptr + YY_MORE_ADJ; lp_yy_cp < lp_yy_c_buf_p; ++lp_yy_cp ) + { + register YY_CHAR lp_yy_c = (*lp_yy_cp ? lp_yy_ec[YY_SC_TO_UI(*lp_yy_cp)] : 1); + while ( lp_yy_chk[lp_yy_base[lp_yy_current_state] + lp_yy_c] != lp_yy_current_state ) + { + lp_yy_current_state = (int) lp_yy_def[lp_yy_current_state]; + if ( lp_yy_current_state >= 122 ) + lp_yy_c = lp_yy_meta[(unsigned int) lp_yy_c]; + } + lp_yy_current_state = lp_yy_nxt[lp_yy_base[lp_yy_current_state] + (unsigned int) lp_yy_c]; + *lp_yy_state_ptr++ = lp_yy_current_state; + } + + return lp_yy_current_state; + } + + +/* lp_yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = lp_yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static lp_yy_state_type lp_yy_try_NUL_trans( lp_yy_state_type lp_yy_current_state ) +#else +static lp_yy_state_type lp_yy_try_NUL_trans( lp_yy_current_state ) +lp_yy_state_type lp_yy_current_state; +#endif + { + register int lp_yy_is_jam; + + register YY_CHAR lp_yy_c = 1; + while ( lp_yy_chk[lp_yy_base[lp_yy_current_state] + lp_yy_c] != lp_yy_current_state ) + { + lp_yy_current_state = (int) lp_yy_def[lp_yy_current_state]; + if ( lp_yy_current_state >= 122 ) + lp_yy_c = lp_yy_meta[(unsigned int) lp_yy_c]; + } + lp_yy_current_state = lp_yy_nxt[lp_yy_base[lp_yy_current_state] + (unsigned int) lp_yy_c]; + lp_yy_is_jam = (lp_yy_current_state == 121); + if ( ! lp_yy_is_jam ) + *lp_yy_state_ptr++ = lp_yy_current_state; + + return lp_yy_is_jam ? 0 : lp_yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void lp_yyunput( int c, register char *lp_yy_bp ) +#else +static void lp_yyunput( c, lp_yy_bp ) +int c; +register char *lp_yy_bp; +#endif + { + register char *lp_yy_cp = lp_yy_c_buf_p; + + /* undo effects of setting up lp_yytext */ + *lp_yy_cp = lp_yy_hold_char; + + if ( lp_yy_cp < lp_yy_current_buffer->lp_yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = lp_yy_n_chars + 2; + register char *dest = &lp_yy_current_buffer->lp_yy_ch_buf[ + lp_yy_current_buffer->lp_yy_buf_size + 2]; + register char *source = + &lp_yy_current_buffer->lp_yy_ch_buf[number_to_move]; + + while ( source > lp_yy_current_buffer->lp_yy_ch_buf ) + *--dest = *--source; + + lp_yy_cp += (int) (dest - source); + lp_yy_bp += (int) (dest - source); + lp_yy_current_buffer->lp_yy_n_chars = + lp_yy_n_chars = lp_yy_current_buffer->lp_yy_buf_size; + + if ( lp_yy_cp < lp_yy_current_buffer->lp_yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--lp_yy_cp = (char) c; + + if ( c == '\n' ) + --lp_yylineno; + + lp_yytext_ptr = lp_yy_bp; + lp_yy_hold_char = *lp_yy_cp; + lp_yy_c_buf_p = lp_yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int lp_yyinput() +#else +static int input() +#endif + { + int c; + + *lp_yy_c_buf_p = lp_yy_hold_char; + + if ( *lp_yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* lp_yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( lp_yy_c_buf_p < &lp_yy_current_buffer->lp_yy_ch_buf[lp_yy_n_chars] ) + /* This was really a NUL. */ + *lp_yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = lp_yy_c_buf_p - lp_yytext_ptr; + ++lp_yy_c_buf_p; + + switch ( lp_yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because lp_yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + lp_yyrestart( lp_yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( lp_yywrap() ) + return EOF; + + if ( ! lp_yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return lp_yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + lp_yy_c_buf_p = lp_yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) lp_yy_c_buf_p; /* cast for 8-bit char's */ + *lp_yy_c_buf_p = '\0'; /* preserve lp_yytext */ + lp_yy_hold_char = *++lp_yy_c_buf_p; + + lp_yy_current_buffer->lp_yy_at_bol = (c == '\n'); + if ( lp_yy_current_buffer->lp_yy_at_bol ) + ++lp_yylineno; + + return c; + } + + +#ifdef YY_USE_PROTOS +void lp_yyrestart( FILE *input_file ) +#else +void lp_yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! lp_yy_current_buffer ) + lp_yy_current_buffer = lp_yy_create_buffer( lp_yyin, YY_BUF_SIZE ); + + lp_yy_init_buffer( lp_yy_current_buffer, input_file ); + lp_yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void lp_yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void lp_yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( lp_yy_current_buffer == new_buffer ) + return; + + if ( lp_yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *lp_yy_c_buf_p = lp_yy_hold_char; + lp_yy_current_buffer->lp_yy_buf_pos = lp_yy_c_buf_p; + lp_yy_current_buffer->lp_yy_n_chars = lp_yy_n_chars; + } + + lp_yy_current_buffer = new_buffer; + lp_yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (lp_yywrap()) processing, but the only time this flag + * is looked at is after lp_yywrap() is called, so it's safe + * to go ahead and always set it. + */ + lp_yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void lp_yy_load_buffer_state( void ) +#else +void lp_yy_load_buffer_state() +#endif + { + lp_yy_n_chars = lp_yy_current_buffer->lp_yy_n_chars; + lp_yytext_ptr = lp_yy_c_buf_p = lp_yy_current_buffer->lp_yy_buf_pos; + lp_yyin = lp_yy_current_buffer->lp_yy_input_file; + lp_yy_hold_char = *lp_yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE lp_yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE lp_yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) lp_yy_flex_alloc( sizeof( struct lp_yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in lp_yy_create_buffer()" ); + + b->lp_yy_buf_size = size; + + /* lp_yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->lp_yy_ch_buf = (char *) lp_yy_flex_alloc( b->lp_yy_buf_size + 2 ); + if ( ! b->lp_yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in lp_yy_create_buffer()" ); + + b->lp_yy_is_our_buffer = 1; + + lp_yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void lp_yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void lp_yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == lp_yy_current_buffer ) + lp_yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->lp_yy_is_our_buffer ) + lp_yy_flex_free( (void *) b->lp_yy_ch_buf ); + + lp_yy_flex_free( (void *) b ); + } + + +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif + +#ifdef YY_USE_PROTOS +void lp_yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void lp_yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + lp_yy_flush_buffer( b ); + + b->lp_yy_input_file = file; + b->lp_yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->lp_yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->lp_yy_is_interactive = 0; +#else + b->lp_yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void lp_yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void lp_yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->lp_yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->lp_yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->lp_yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->lp_yy_buf_pos = &b->lp_yy_ch_buf[0]; + + b->lp_yy_at_bol = 1; + b->lp_yy_buffer_status = YY_BUFFER_NEW; + + if ( b == lp_yy_current_buffer ) + lp_yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE lp_yy_scan_buffer( char *base, lp_yy_size_t size ) +#else +YY_BUFFER_STATE lp_yy_scan_buffer( base, size ) +char *base; +lp_yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) lp_yy_flex_alloc( sizeof( struct lp_yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in lp_yy_scan_buffer()" ); + + b->lp_yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->lp_yy_buf_pos = b->lp_yy_ch_buf = base; + b->lp_yy_is_our_buffer = 0; + b->lp_yy_input_file = 0; + b->lp_yy_n_chars = b->lp_yy_buf_size; + b->lp_yy_is_interactive = 0; + b->lp_yy_at_bol = 1; + b->lp_yy_fill_buffer = 0; + b->lp_yy_buffer_status = YY_BUFFER_NEW; + + lp_yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE lp_yy_scan_string( lp_yyconst char *lp_yy_str ) +#else +YY_BUFFER_STATE lp_yy_scan_string( lp_yy_str ) +lp_yyconst char *lp_yy_str; +#endif + { + int len; + for ( len = 0; lp_yy_str[len]; ++len ) + ; + + return lp_yy_scan_bytes( lp_yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE lp_yy_scan_bytes( lp_yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE lp_yy_scan_bytes( bytes, len ) +lp_yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + lp_yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) lp_yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in lp_yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = lp_yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in lp_yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->lp_yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void lp_yy_push_state( int new_state ) +#else +static void lp_yy_push_state( new_state ) +int new_state; +#endif + { + if ( lp_yy_start_stack_ptr >= lp_yy_start_stack_depth ) + { + lp_yy_size_t new_size; + + lp_yy_start_stack_depth += YY_START_STACK_INCR; + new_size = lp_yy_start_stack_depth * sizeof( int ); + + if ( ! lp_yy_start_stack ) + lp_yy_start_stack = (int *) lp_yy_flex_alloc( new_size ); + + else + lp_yy_start_stack = (int *) lp_yy_flex_realloc( + (void *) lp_yy_start_stack, new_size ); + + if ( ! lp_yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + lp_yy_start_stack[lp_yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void lp_yy_pop_state() + { + if ( --lp_yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(lp_yy_start_stack[lp_yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int lp_yy_top_state() + { + return lp_yy_start_stack[lp_yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void lp_yy_fatal_error( lp_yyconst char msg[] ) +#else +static void lp_yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine lp_yyless() so it works in section 3 code. */ + +#undef lp_yyless +#define lp_yyless(n) \ + do \ + { \ + /* Undo effects of setting up lp_yytext. */ \ + lp_yytext[lp_yyleng] = lp_yy_hold_char; \ + lp_yy_c_buf_p = lp_yytext + n; \ + lp_yy_hold_char = *lp_yy_c_buf_p; \ + *lp_yy_c_buf_p = '\0'; \ + lp_yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef lp_yytext_ptr +#ifdef YY_USE_PROTOS +static void lp_yy_flex_strncpy( char *s1, lp_yyconst char *s2, int n ) +#else +static void lp_yy_flex_strncpy( s1, s2, n ) +char *s1; +lp_yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int lp_yy_flex_strlen( lp_yyconst char *s ) +#else +static int lp_yy_flex_strlen( s ) +lp_yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *lp_yy_flex_alloc( lp_yy_size_t size ) +#else +static void *lp_yy_flex_alloc( size ) +lp_yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *lp_yy_flex_realloc( void *ptr, lp_yy_size_t size ) +#else +static void *lp_yy_flex_realloc( ptr, size ) +void *ptr; +lp_yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void lp_yy_flex_free( void *ptr ) +#else +static void lp_yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + lp_yylex(); + return 0; + } +#endif + Index: src/tools/solver/lp_solve/lp_rlpt.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_rlpt.c diff -N src/tools/solver/lp_solve/lp_rlpt.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_rlpt.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1393 @@ + +/* A Bison parser, made from lp_rlpt.y + by GNU Bison version 1.28 */ + +#define YYBISON 1 /* Identify Bison output. */ + +#define VAR 257 +#define CONS 258 +#define INTCONS 259 +#define VARIABLECOLON 260 +#define INF 261 +#define FR 262 +#define SEC_INT 263 +#define SEC_SEC 264 +#define SEC_SOS 265 +#define SOSTYPE 266 +#define SIGN 267 +#define RE_OPLE 268 +#define RE_OPGE 269 +#define MINIMISE 270 +#define MAXIMISE 271 +#define SUBJECTTO 272 +#define BOUNDS 273 +#define END 274 +#define UNDEFINED 275 + + +#include +#include + +#include "lpkit.h" +#include "yacc_read.h" + +static char Last_var[NAMELEN], Last_var0[NAMELEN]; +static REAL f, f0; +static int x; +static int Sign; +static int isign, isign0; /* internal_sign variable to make sure nothing goes wrong */ + /* with lookahead */ +static int make_neg; /* is true after the relational operator is seen in order */ + /* to remember if lin_term stands before or after re_op */ +static int Within_gen_decl = FALSE; /* TRUE when we are within an gen declaration */ +static int Within_bin_decl = FALSE; /* TRUE when we are within an bin declaration */ +static int Within_sec_decl = FALSE; /* TRUE when we are within an sec declaration */ +static int Within_sos_decl = FALSE; /* TRUE when we are within an sos declaration */ +static short SOStype; /* SOS type */ +static int SOSNr; +static int weight; /* SOS weight */ +static int SOSweight = 0; /* SOS weight */ + +static int HadConstraint; +static int HadVar; +static int Had_lineair_sum; + +#define YY_FATAL_ERROR lex_fatal_error + +/* let's please C++ users */ +#ifdef __cplusplus +extern "C" { +#endif + +static int wrap(void) +{ + return(1); +} + +#ifdef __cplusplus +}; +#endif + +#define lpt_yywrap wrap +#define lpt_yyerror read_error + +#include "lp_rlpt.h" + +#ifndef YYSTYPE +#define YYSTYPE int +#endif +#include + +#ifndef __cplusplus +#ifndef __STDC__ +#define const +#endif +#endif + + + +#define YYFINAL 129 +#define YYFLAG -32768 +#define YYNTBASE 22 + +#define YYTRANSLATE(x) ((unsigned)(x) <= 275 ? lpt_yytranslate[x] : 83) + +static const char lpt_yytranslate[] = { 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21 +}; + +#if YYDEBUG != 0 +static const short lpt_yyprhs[] = { 0, + 0, 1, 2, 9, 12, 15, 17, 18, 22, 24, + 25, 29, 31, 34, 36, 37, 41, 42, 48, 50, + 53, 54, 59, 64, 66, 68, 71, 73, 75, 78, + 80, 83, 84, 89, 90, 91, 92, 93, 94, 106, + 107, 112, 113, 114, 120, 122, 123, 128, 130, 132, + 134, 136, 138, 140, 144, 146, 149, 151, 154, 156, + 158, 159, 163, 165, 167, 169, 172, 173, 177, 179, + 181, 183, 186, 187, 191, 193, 195, 197, 200, 203, + 205, 207, 209, 212, 213, 217, 219, 221, 223 +}; + +static const short lpt_yyrhs[] = { -1, + 0, 24, 25, 29, 41, 60, 82, 0, 17, 26, + 0, 16, 26, 0, 28, 0, 0, 6, 27, 28, + 0, 36, 0, 0, 18, 30, 31, 0, 32, 0, + 31, 32, 0, 34, 0, 0, 6, 33, 34, 0, + 0, 36, 39, 35, 40, 57, 0, 37, 0, 36, + 37, 0, 0, 58, 81, 38, 59, 0, 58, 56, + 81, 59, 0, 14, 0, 15, 0, 58, 56, 0, + 7, 0, 22, 0, 19, 42, 0, 43, 0, 42, + 43, 0, 0, 81, 44, 59, 50, 0, 0, 0, + 0, 0, 0, 40, 45, 39, 46, 81, 47, 59, + 48, 57, 49, 54, 0, 0, 39, 51, 40, 57, + 0, 0, 0, 8, 52, 57, 53, 57, 0, 22, + 0, 0, 39, 55, 40, 57, 0, 5, 0, 4, + 0, 22, 0, 22, 0, 13, 0, 22, 0, 66, + 70, 74, 0, 63, 0, 61, 63, 0, 64, 0, + 62, 64, 0, 81, 0, 81, 0, 0, 6, 65, + 5, 0, 22, 0, 67, 0, 68, 0, 67, 68, + 0, 0, 9, 69, 61, 0, 22, 0, 71, 0, + 72, 0, 71, 72, 0, 0, 10, 73, 61, 0, + 22, 0, 75, 0, 76, 0, 75, 76, 0, 11, + 77, 0, 22, 0, 78, 0, 79, 0, 78, 79, + 0, 0, 12, 80, 62, 0, 3, 0, 8, 0, + 22, 0, 20, 0 +}; + +#endif + +#if YYDEBUG != 0 +static const short lpt_yyrline[] = { 0, + 65, 68, 77, 93, 99, 106, 107, 114, 116, 149, + 154, 159, 160, 164, 165, 172, 174, 182, 194, 195, + 199, 205, 205, 211, 211, 214, 216, 240, 241, 245, + 246, 250, 256, 257, 263, 269, 274, 279, 284, 293, + 300, 310, 319, 329, 341, 342, 350, 361, 361, 364, + 375, 379, 385, 403, 408, 409, 413, 414, 418, 424, + 431, 436, 442, 444, 447, 448, 452, 457, 459, 461, + 464, 465, 469, 474, 477, 479, 482, 483, 487, 491, + 493, 496, 498, 502, 518, 526, 526, 531, 532 +}; +#endif + + +#if YYDEBUG != 0 || defined (YYERROR_VERBOSE) + +static const char * const lpt_yytname[] = { "$","error","$undefined.","VAR","CONS", +"INTCONS","VARIABLECOLON","INF","FR","SEC_INT","SEC_SEC","SEC_SOS","SOSTYPE", +"SIGN","RE_OPLE","RE_OPGE","MINIMISE","MAXIMISE","SUBJECTTO","BOUNDS","END", +"UNDEFINED","EMPTY","inputfile","@1","objective_function","of","@2","real_of", +"x_constraints","@3","constraints","constraint","@4","real_constraint","@5", +"lineair_sum","lineair_term","@6","RE_OP","cons_term","bounds","x_bounds","bound", +"@7","@8","@9","@10","@11","@12","bound2","@13","@14","@15","optionalbound", +"@16","REALCONS","RHS_STORE","x_SIGN","VAR_STORE","int_sec_sos_declarations", +"VARIABLES","SOSVARIABLES","ONEVARIABLE","ONESOSVARIABLE","@17","x_int_declarations", +"int_declarations","int_declaration","@18","x_sec_declarations","sec_declarations", +"sec_declaration","@19","x_sos_declarations","sos_declarations","sos_declaration", +"x_single_sos_declarations","single_sos_declarations","single_sos_declaration", +"@20","VARIABLE","end", NULL +}; +#endif + +static const short lpt_yyr1[] = { 0, + 22, 24, 23, 25, 25, 26, 27, 26, 28, 30, + 29, 31, 31, 32, 33, 32, 35, 34, 36, 36, + 38, 37, 37, 39, 39, 40, 40, 41, 41, 42, + 42, 44, 43, 45, 46, 47, 48, 49, 43, 51, + 50, 52, 53, 50, 54, 55, 54, 56, 56, 57, + 58, 58, 59, 60, 61, 61, 62, 62, 63, 64, + 65, 64, 66, 66, 67, 67, 69, 68, 70, 70, + 71, 71, 73, 72, 74, 74, 75, 75, 76, 77, + 77, 78, 78, 80, 79, 81, 81, 82, 82 +}; + +static const short lpt_yyr2[] = { 0, + 0, 0, 6, 2, 2, 1, 0, 3, 1, 0, + 3, 1, 2, 1, 0, 3, 0, 5, 1, 2, + 0, 4, 4, 1, 1, 2, 1, 1, 2, 1, + 2, 0, 4, 0, 0, 0, 0, 0, 11, 0, + 4, 0, 0, 5, 1, 0, 4, 1, 1, 1, + 1, 1, 1, 3, 1, 2, 1, 2, 1, 1, + 0, 3, 1, 1, 1, 2, 0, 3, 1, 1, + 1, 2, 0, 3, 1, 1, 1, 2, 2, 1, + 1, 1, 2, 0, 3, 1, 1, 1, 1 +}; + +static const short lpt_yydefact[] = { 2, + 0, 1, 1, 0, 7, 52, 51, 5, 6, 1, + 19, 0, 4, 10, 1, 1, 20, 86, 49, 48, + 87, 0, 21, 1, 1, 28, 1, 8, 1, 1, + 15, 11, 12, 14, 1, 27, 34, 29, 30, 0, + 32, 67, 63, 1, 1, 64, 65, 53, 23, 22, + 1, 13, 24, 25, 17, 0, 31, 26, 1, 0, + 89, 88, 3, 73, 69, 1, 70, 71, 66, 16, + 1, 35, 0, 68, 55, 59, 0, 1, 75, 54, + 76, 77, 72, 1, 0, 42, 40, 33, 56, 74, + 84, 80, 79, 81, 82, 78, 50, 18, 36, 1, + 1, 0, 83, 1, 43, 1, 61, 85, 57, 60, + 37, 1, 41, 0, 58, 1, 44, 62, 38, 1, + 45, 46, 39, 1, 1, 47, 0, 0, 0 +}; + +static const short lpt_yydefgoto[] = { 7, + 127, 1, 4, 8, 16, 9, 15, 24, 32, 33, + 51, 34, 71, 10, 11, 30, 55, 37, 27, 38, + 39, 59, 56, 85, 104, 116, 120, 88, 101, 100, + 112, 123, 124, 22, 98, 12, 49, 44, 74, 108, + 75, 109, 114, 45, 46, 47, 60, 66, 67, 68, + 77, 80, 81, 82, 93, 94, 95, 102, 76, 63 +}; + +static const short lpt_yypact[] = {-32768, + 83, 4, 4, -14,-32768,-32768,-32768,-32768,-32768, 53, +-32768, 56,-32768,-32768, -12, 9,-32768,-32768,-32768,-32768, +-32768, 64,-32768, 37, 34,-32768, 15,-32768,-32768,-32768, +-32768, 49,-32768,-32768, 80,-32768,-32768, 70,-32768, 102, +-32768,-32768,-32768, 26, 38, 15,-32768,-32768,-32768,-32768, + 9,-32768,-32768,-32768,-32768, 94,-32768,-32768,-32768, 64, +-32768,-32768,-32768,-32768,-32768, 45, 38,-32768,-32768,-32768, + 12,-32768, 25, 64,-32768,-32768, 64, 68,-32768,-32768, + 45,-32768,-32768,-32768, 64,-32768,-32768,-32768,-32768, 64, +-32768,-32768,-32768, 68,-32768,-32768,-32768,-32768,-32768,-32768, + 12, 84,-32768,-32768,-32768,-32768,-32768, 84,-32768,-32768, +-32768,-32768,-32768, 60,-32768,-32768,-32768,-32768,-32768, 94, +-32768,-32768,-32768, 12,-32768,-32768, 82, 86,-32768 +}; + +static const short lpt_yypgoto[] = { -15, +-32768,-32768,-32768, 65,-32768, 72,-32768,-32768,-32768, 52, +-32768, 61,-32768, -23, 3,-32768, -50, -66,-32768,-32768, + 58,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768, +-32768,-32768,-32768, 63, -80, -22, -28,-32768, 36,-32768, + -63, 6,-32768,-32768,-32768, 69,-32768,-32768,-32768, 44, +-32768,-32768,-32768, 35,-32768,-32768, 23,-32768, -4,-32768 +}; + + +#define YYLAST 117 + + +static const short lpt_yytable[] = { 26, + 35, 50, 40, 14, 84, 72, 25, 23, 35, 5, + 89, 43, 17, 48, 48, 40, 6, 29, 36, 105, + 41, 6, 87, 42, 6, 113, 89, 35, 62, 65, + 73, 117, 86, 41, 106, 119, 18, 17, 53, 54, + 36, 21, 31, 48, 126, 61, 6, 64, 40, 6, + 79, -1, -1, -1, 31, 78, -1, 125, 18, 19, + 20, 6, 92, 21, 118, 6, 18, 13, 97, 122, + -9, 21, 18, -1, -1, 111, 36, 21, 40, 91, + 99, 128, 6, 52, 97, 129, 18, 28, 48, 107, + 97, 21, 6, 53, 54, 57, 97, 110, 2, 3, + 97, 40, 58, 110, 121, 19, 20, 53, 54, 97, + 83, 70, 90, 115, 69, 96, 103 +}; + +static const short lpt_yycheck[] = { 15, + 24, 30, 25, 18, 71, 56, 19, 12, 32, 6, + 74, 27, 10, 29, 30, 38, 13, 22, 7, 100, + 25, 13, 73, 9, 13, 106, 90, 51, 44, 45, + 59, 112, 8, 38, 101, 116, 3, 35, 14, 15, + 7, 8, 6, 59, 125, 20, 13, 10, 71, 13, + 66, 3, 4, 5, 6, 11, 8, 124, 3, 4, + 5, 13, 78, 8, 5, 13, 3, 3, 84, 120, + 18, 8, 3, 4, 5, 104, 7, 8, 101, 12, + 85, 0, 13, 32, 100, 0, 3, 16, 104, 6, + 106, 8, 13, 14, 15, 38, 112, 102, 16, 17, + 116, 124, 40, 108, 120, 4, 5, 14, 15, 125, + 67, 51, 77, 108, 46, 81, 94 +}; +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ + +/* This file comes from bison-1.28. */ + +/* Skeleton output parser for bison, + Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* This is the parser code that is written into each bison parser + when the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +#ifndef YYSTACK_USE_ALLOCA +#ifdef alloca +#define YYSTACK_USE_ALLOCA +#else /* alloca not defined */ +#ifdef __GNUC__ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#else /* not GNU C. */ +#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386)) +#define YYSTACK_USE_ALLOCA +#include +#else /* not sparc */ +/* We think this test detects Watcom and Microsoft C. */ +/* This used to test MSDOS, but that is a bad idea + since that symbol is in the user namespace. */ +#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__) +#if 0 /* No need for malloc.h, which pollutes the namespace; + instead, just don't use alloca. */ +#include +#endif +#else /* not MSDOS, or __TURBOC__ */ +#if defined(_AIX) +/* I don't know what this was needed for, but it pollutes the namespace. + So I turned it off. rms, 2 May 1997. */ +/* #include */ + #pragma alloca +#define YYSTACK_USE_ALLOCA +#else /* not MSDOS, or __TURBOC__, or _AIX */ +#if 0 +#ifdef __hpux /* haible@ilog.fr says this works for HPUX 9.05 and up, + and on HPUX 10. Eventually we can turn this on. */ +#define YYSTACK_USE_ALLOCA +#define alloca __builtin_alloca +#endif /* __hpux */ +#endif +#endif /* not _AIX */ +#endif /* not MSDOS, or __TURBOC__ */ +#endif /* not sparc */ +#endif /* not GNU C */ +#endif /* alloca not defined */ +#endif /* YYSTACK_USE_ALLOCA not defined */ + +#ifdef YYSTACK_USE_ALLOCA +#define YYSTACK_ALLOC alloca +#else +#define YYSTACK_ALLOC malloc +#endif + +/* Note: there must be only one dollar sign in this file. + It is replaced by the list of actions, each action + as one case of the switch. */ + +#define lpt_yyerrok (lpt_yyerrstatus = 0) +#define lpt_yyclearin (lpt_yychar = YYEMPTY) +#define YYEMPTY -2 +#define YYEOF 0 +#define YYACCEPT goto lpt_yyacceptlab +#define YYABORT goto lpt_yyabortlab +#define YYERROR goto lpt_yyerrlab1 +/* Like YYERROR except do call lpt_yyerror. + This remains here temporarily to ease the + transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define YYFAIL goto lpt_yyerrlab +#define YYRECOVERING() (!!lpt_yyerrstatus) +#define YYBACKUP(token, value) \ +do \ + if (lpt_yychar == YYEMPTY && lpt_yylen == 1) \ + { lpt_yychar = (token), lpt_yylval = (value); \ + lpt_yychar1 = YYTRANSLATE (lpt_yychar); \ + YYPOPSTACK; \ + goto lpt_yybackup; \ + } \ + else \ + { lpt_yyerror ("syntax error: cannot back up"); YYERROR; } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +#ifndef YYPURE +#define YYLEX lpt_yylex() +#endif + +#ifdef YYPURE +#ifdef YYLSP_NEEDED +#ifdef YYLEX_PARAM +#define YYLEX lpt_yylex(&lpt_yylval, &lpt_yylloc, YYLEX_PARAM) +#else +#define YYLEX lpt_yylex(&lpt_yylval, &lpt_yylloc) +#endif +#else /* not YYLSP_NEEDED */ +#ifdef YYLEX_PARAM +#define YYLEX lpt_yylex(&lpt_yylval, YYLEX_PARAM) +#else +#define YYLEX lpt_yylex(&lpt_yylval) +#endif +#endif /* not YYLSP_NEEDED */ +#endif + +/* If nonreentrant, generate the variables here */ + +#ifndef YYPURE + +int lpt_yychar; /* the lookahead symbol */ +YYSTYPE lpt_yylval; /* the semantic value of the */ + /* lookahead symbol */ + +#ifdef YYLSP_NEEDED +YYLTYPE lpt_yylloc; /* location data for the lookahead */ + /* symbol */ +#endif + +int lpt_yynerrs; /* number of parse errors so far */ +#endif /* not YYPURE */ + +#if YYDEBUG != 0 +int lpt_yydebug; /* nonzero means print parse trace */ +/* Since this is uninitialized, it does not stop multiple parsers + from coexisting. */ +#endif + +/* YYINITDEPTH indicates the initial size of the parser's stacks */ + +#ifndef YYINITDEPTH +#define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH is the maximum size the stacks can grow to + (effective only if the built-in stack extension method is used). */ + +#if YYMAXDEPTH == 0 +#undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 10000 +#endif + +/* Define __lpt_yy_memcpy. Note that the size argument + should be passed with type unsigned int, because that is what the non-GCC + definitions require. With GCC, __builtin_memcpy takes an arg + of type size_t, but it can handle unsigned int. */ + +#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */ +#define __lpt_yy_memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT) +#else /* not GNU C or C++ */ +#ifndef __cplusplus + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__lpt_yy_memcpy (to, from, count) + char *to; + char *from; + unsigned int count; +{ + register char *f = from; + register char *t = to; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#else /* __cplusplus */ + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__lpt_yy_memcpy (char *to, char *from, unsigned int count) +{ + register char *t = to; + register char *f = from; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#endif +#endif + + + +/* The user can define YYPARSE_PARAM as the name of an argument to be passed + into lpt_yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef YYPARSE_PARAM +#ifdef __cplusplus +#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM +#define YYPARSE_PARAM_DECL +#else /* not __cplusplus */ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM +#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; +#endif /* not __cplusplus */ +#else /* not YYPARSE_PARAM */ +#define YYPARSE_PARAM_ARG +#define YYPARSE_PARAM_DECL +#endif /* not YYPARSE_PARAM */ + +/* Prevent warning if -Wstrict-prototypes. */ +#ifdef __GNUC__ +#ifdef YYPARSE_PARAM +int lpt_yyparse (void *); +#else +int lpt_yyparse (void); +#endif +#endif + +int +lpt_yyparse(YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL +{ + register int lpt_yystate; + register int lpt_yyn; + register short *lpt_yyssp; + register YYSTYPE *lpt_yyvsp; + int lpt_yyerrstatus; /* number of tokens to shift before error messages enabled */ + int lpt_yychar1 = 0; /* lookahead token as an internal (translated) token number */ + + short lpt_yyssa[YYINITDEPTH]; /* the state stack */ + YYSTYPE lpt_yyvsa[YYINITDEPTH]; /* the semantic value stack */ + + short *lpt_yyss = lpt_yyssa; /* refer to the stacks thru separate pointers */ + YYSTYPE *lpt_yyvs = lpt_yyvsa; /* to allow lpt_yyoverflow to reallocate them elsewhere */ + +#ifdef YYLSP_NEEDED + YYLTYPE lpt_yylsa[YYINITDEPTH]; /* the location stack */ + YYLTYPE *lpt_yyls = lpt_yylsa; + YYLTYPE *lpt_yylsp; + +#define YYPOPSTACK (lpt_yyvsp--, lpt_yyssp--, lpt_yylsp--) +#else +#define YYPOPSTACK (lpt_yyvsp--, lpt_yyssp--) +#endif + + int lpt_yystacksize = YYINITDEPTH; + int lpt_yyfree_stacks = 0; + +#ifdef YYPURE + int lpt_yychar; + YYSTYPE lpt_yylval; + int lpt_yynerrs; +#ifdef YYLSP_NEEDED + YYLTYPE lpt_yylloc; +#endif +#endif + + YYSTYPE lpt_yyval = 0; /* the variable used to return */ + /* semantic values from the action */ + /* routines */ + + int lpt_yylen; + +#if YYDEBUG != 0 + if (lpt_yydebug) + fprintf(stderr, "Starting parse\n"); +#endif + + lpt_yystate = 0; + lpt_yyerrstatus = 0; + lpt_yynerrs = 0; + lpt_yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + lpt_yyssp = lpt_yyss - 1; + lpt_yyvsp = lpt_yyvs; +#ifdef YYLSP_NEEDED + lpt_yylsp = lpt_yyls; +#endif + +/* Push a new state, which is found in lpt_yystate . */ +/* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. */ +lpt_yynewstate: + + *++lpt_yyssp = lpt_yystate; + + if (lpt_yyssp >= lpt_yyss + lpt_yystacksize - 1) + { + /* Give user a chance to reallocate the stack */ + /* Use copies of these so that the &'s don't force the real ones into memory. */ + YYSTYPE *lpt_yyvs1 = lpt_yyvs; + short *lpt_yyss1 = lpt_yyss; +#ifdef YYLSP_NEEDED + YYLTYPE *lpt_yyls1 = lpt_yyls; +#endif + + /* Get the current used size of the three stacks, in elements. */ + int size = lpt_yyssp - lpt_yyss + 1; + +#ifdef lpt_yyoverflow + /* Each stack pointer address is followed by the size of + the data in use in that stack, in bytes. */ +#ifdef YYLSP_NEEDED + /* This used to be a conditional around just the two extra args, + but that might be undefined if lpt_yyoverflow is a macro. */ + lpt_yyoverflow("parser stack overflow", + &lpt_yyss1, size * sizeof (*lpt_yyssp), + &lpt_yyvs1, size * sizeof (*lpt_yyvsp), + &lpt_yyls1, size * sizeof (*lpt_yylsp), + &lpt_yystacksize); +#else + lpt_yyoverflow("parser stack overflow", + &lpt_yyss1, size * sizeof (*lpt_yyssp), + &lpt_yyvs1, size * sizeof (*lpt_yyvsp), + &lpt_yystacksize); +#endif + + lpt_yyss = lpt_yyss1; lpt_yyvs = lpt_yyvs1; +#ifdef YYLSP_NEEDED + lpt_yyls = lpt_yyls1; +#endif +#else /* no lpt_yyoverflow */ + /* Extend the stack our own way. */ + if (lpt_yystacksize >= YYMAXDEPTH) + { + lpt_yyerror("parser stack overflow"); + if (lpt_yyfree_stacks) + { + free (lpt_yyss); + free (lpt_yyvs); +#ifdef YYLSP_NEEDED + free (lpt_yyls); +#endif + } + return 2; + } + lpt_yystacksize *= 2; + if (lpt_yystacksize > YYMAXDEPTH) + lpt_yystacksize = YYMAXDEPTH; +#ifndef YYSTACK_USE_ALLOCA + lpt_yyfree_stacks = 1; +#endif + lpt_yyss = (short *) YYSTACK_ALLOC (lpt_yystacksize * sizeof (*lpt_yyssp)); + __lpt_yy_memcpy ((char *)lpt_yyss, (char *)lpt_yyss1, + size * (unsigned int) sizeof (*lpt_yyssp)); + lpt_yyvs = (YYSTYPE *) YYSTACK_ALLOC (lpt_yystacksize * sizeof (*lpt_yyvsp)); + __lpt_yy_memcpy ((char *)lpt_yyvs, (char *)lpt_yyvs1, + size * (unsigned int) sizeof (*lpt_yyvsp)); +#ifdef YYLSP_NEEDED + lpt_yyls = (YYLTYPE *) YYSTACK_ALLOC (lpt_yystacksize * sizeof (*lpt_yylsp)); + __lpt_yy_memcpy ((char *)lpt_yyls, (char *)lpt_yyls1, + size * (unsigned int) sizeof (*lpt_yylsp)); +#endif +#endif /* no lpt_yyoverflow */ + + lpt_yyssp = lpt_yyss + size - 1; + lpt_yyvsp = lpt_yyvs + size - 1; +#ifdef YYLSP_NEEDED + lpt_yylsp = lpt_yyls + size - 1; +#endif + +#if YYDEBUG != 0 + if (lpt_yydebug) + fprintf(stderr, "Stack size increased to %d\n", lpt_yystacksize); +#endif + + if (lpt_yyssp >= lpt_yyss + lpt_yystacksize - 1) + YYABORT; + } + +#if YYDEBUG != 0 + if (lpt_yydebug) + fprintf(stderr, "Entering state %d\n", lpt_yystate); +#endif + + goto lpt_yybackup; + lpt_yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* lpt_yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + lpt_yyn = lpt_yypact[lpt_yystate]; + if (lpt_yyn == YYFLAG) + goto lpt_yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* lpt_yychar is either YYEMPTY or YYEOF + or a valid token in external form. */ + + if (lpt_yychar == YYEMPTY) + { +#if YYDEBUG != 0 + if (lpt_yydebug) + fprintf(stderr, "Reading a token: "); +#endif + lpt_yychar = YYLEX; + } + + /* Convert token to internal form (in lpt_yychar1) for indexing tables with */ + + if (lpt_yychar <= 0) /* This means end of input. */ + { + lpt_yychar1 = 0; + lpt_yychar = YYEOF; /* Don't call YYLEX any more */ + +#if YYDEBUG != 0 + if (lpt_yydebug) + fprintf(stderr, "Now at end of input.\n"); +#endif + } + else + { + lpt_yychar1 = YYTRANSLATE(lpt_yychar); + +#if YYDEBUG != 0 + if (lpt_yydebug) + { + fprintf (stderr, "Next token is %d (%s", lpt_yychar, lpt_yytname[lpt_yychar1]); + /* Give the individual parser a way to print the precise meaning + of a token, for further debugging info. */ +#ifdef YYPRINT + YYPRINT (stderr, lpt_yychar, lpt_yylval); +#endif + fprintf (stderr, ")\n"); + } +#endif + } + + lpt_yyn += lpt_yychar1; + if (lpt_yyn < 0 || lpt_yyn > YYLAST || lpt_yycheck[lpt_yyn] != lpt_yychar1) + goto lpt_yydefault; + + lpt_yyn = lpt_yytable[lpt_yyn]; + + /* lpt_yyn is what to do for this token type in this state. + Negative => reduce, -lpt_yyn is rule number. + Positive => shift, lpt_yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ + + if (lpt_yyn < 0) + { + if (lpt_yyn == YYFLAG) + goto lpt_yyerrlab; + lpt_yyn = -lpt_yyn; + goto lpt_yyreduce; + } + else if (lpt_yyn == 0) + goto lpt_yyerrlab; + + if (lpt_yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + +#if YYDEBUG != 0 + if (lpt_yydebug) + fprintf(stderr, "Shifting token %d (%s), ", lpt_yychar, lpt_yytname[lpt_yychar1]); +#endif + + /* Discard the token being shifted unless it is eof. */ + if (lpt_yychar != YYEOF) + lpt_yychar = YYEMPTY; + + *++lpt_yyvsp = lpt_yylval; +#ifdef YYLSP_NEEDED + *++lpt_yylsp = lpt_yylloc; +#endif + + /* count tokens shifted since error; after three, turn off error status. */ + if (lpt_yyerrstatus) lpt_yyerrstatus--; + + lpt_yystate = lpt_yyn; + goto lpt_yynewstate; + +/* Do the default action for the current state. */ +lpt_yydefault: + + lpt_yyn = lpt_yydefact[lpt_yystate]; + if (lpt_yyn == 0) + goto lpt_yyerrlab; + +/* Do a reduction. lpt_yyn is the number of a rule to reduce with. */ +lpt_yyreduce: + lpt_yylen = lpt_yyr2[lpt_yyn]; + if (lpt_yylen > 0) + lpt_yyval = lpt_yyvsp[1-lpt_yylen]; /* implement default value of the action */ + +#if YYDEBUG != 0 + if (lpt_yydebug) + { + int i; + + fprintf (stderr, "Reducing via rule %d (line %d), ", + lpt_yyn, lpt_yyrline[lpt_yyn]); + + /* Print the symbols being reduced, and their result. */ + for (i = lpt_yyprhs[lpt_yyn]; lpt_yyrhs[i] > 0; i++) + fprintf (stderr, "%s ", lpt_yytname[lpt_yyrhs[i]]); + fprintf (stderr, " -> %s\n", lpt_yytname[lpt_yyr1[lpt_yyn]]); + } +#endif + + + switch (lpt_yyn) { + +case 2: +{ + isign = 0; + make_neg = 0; + Sign = 0; + HadConstraint = FALSE; + HadVar = FALSE; +; + break;} +case 4: +{ + set_obj_dir(TRUE); +; + break;} +case 5: +{ + set_obj_dir(FALSE); +; + break;} +case 7: +{ + if(!add_constraint_name(Last_var)) + YYABORT; + /* HadConstraint = TRUE; */ +; + break;} +case 9: +{ + add_row(); + /* HadConstraint = FALSE; */ + HadVar = FALSE; + isign = 0; + make_neg = 0; +; + break;} +case 10: +{ + HadConstraint = TRUE; +; + break;} +case 11: +{ + HadConstraint = FALSE; +; + break;} +case 15: +{ + if(!add_constraint_name(Last_var)) + YYABORT; + /* HadConstraint = TRUE; */ +; + break;} +case 17: +{ + if(!store_re_op((char *) lpt_yytext, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + make_neg = 1; +; + break;} +case 18: +{ + Had_lineair_sum = TRUE; + add_row(); + /* HadConstraint = FALSE; */ + HadVar = FALSE; + isign = 0; + make_neg = 0; + null_tmp_store(TRUE); +; + break;} +case 21: +{ + f = 1.0; +; + break;} +case 27: +{ + isign = Sign; +; + break;} +case 32: +{ + f = 1.0; + isign = 0; +; + break;} +case 34: +{ + f0 = f; + isign0 = isign; +; + break;} +case 35: +{ + if(!store_re_op((char *) lpt_yytext, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + make_neg = 0; +; + break;} +case 36: +{ + isign = 0; + f = -1.0; +; + break;} +case 37: +{ + isign = isign0; + f = f0; +; + break;} +case 38: +{ + if(!store_bounds(TRUE)) + YYABORT; +; + break;} +case 39: +{ + /* HadConstraint = FALSE; */ + HadVar = FALSE; + isign = 0; + make_neg = 0; + null_tmp_store(TRUE); +; + break;} +case 40: +{ + if(!store_re_op((char *) lpt_yytext, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + make_neg = 1; +; + break;} +case 41: +{ + if(!store_bounds(TRUE)) + YYABORT; + /* HadConstraint = FALSE; */ + HadVar = FALSE; + isign = 0; + make_neg = 0; + null_tmp_store(TRUE); +; + break;} +case 42: +{ + if(!store_re_op(">", HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + make_neg = 1; + isign = 0; + f = -DEF_INFINITE; +; + break;} +case 43: +{ + if(!store_bounds(FALSE)) + YYABORT; + + if(!store_re_op("<", HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + f = DEF_INFINITE; + isign = 0; +; + break;} +case 44: +{ + if(!store_bounds(FALSE)) + YYABORT; + /* HadConstraint = FALSE; */ + HadVar = FALSE; + isign = 0; + make_neg = 0; + null_tmp_store(TRUE); +; + break;} +case 46: +{ + if(!store_re_op((*lpt_yytext == '<') ? ">" : (*lpt_yytext == '>') ? "<" : (char *) lpt_yytext, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + make_neg = 0; + isign = 0; +; + break;} +case 47: +{ + if(!store_bounds(TRUE)) + YYABORT; +; + break;} +case 50: +{ + if ( (isign || !make_neg) + && !(isign && !make_neg)) /* but not both! */ + f = -f; + if(!rhs_store(f, HadConstraint, HadVar, Had_lineair_sum)) + YYABORT; + isign = 0; +; + break;} +case 51: +{ + isign = 0; +; + break;} +case 52: +{ + isign = Sign; +; + break;} +case 53: +{ + if ( (isign || make_neg) + && !(isign && make_neg)) /* but not both! */ + f = -f; + if(!var_store(Last_var, f, HadConstraint, HadVar, Had_lineair_sum)) { + lpt_yyerror("var_store failed"); + YYABORT; + } + /* HadConstraint |= HadVar; */ + HadVar = TRUE; + isign = 0; +; + break;} +case 59: +{ + storevarandweight(Last_var); +; + break;} +case 60: +{ + SOSNr++; + weight = SOSNr; + storevarandweight(Last_var); + set_sos_weight(weight, 2); +; + break;} +case 61: +{ + storevarandweight(Last_var); +; + break;} +case 62: +{ + weight = (int) (f + .1); + set_sos_weight(weight, 2); +; + break;} +case 67: +{ + check_int_sec_sos_decl(Within_gen_decl ? 1 : Within_bin_decl ? 2 : 0, 0, 0); +; + break;} +case 73: +{ + check_int_sec_sos_decl(0, 1, 0); +; + break;} +case 84: +{ + char buf[16]; + + check_int_sec_sos_decl(0, 0, 1); + SOSweight++; + sprintf(buf, "SOS%d", SOSweight); + storevarandweight(buf); + SOStype = Last_var[1] - '0'; + set_sos_type(SOStype); + check_int_sec_sos_decl(0, 0, 2); + weight = 0; + SOSNr = 0; +; + break;} +case 85: +{ + set_sos_weight(SOSweight, 1); +; + break;} +} + /* the action file gets copied in in place of this dollarsign */ + + + lpt_yyvsp -= lpt_yylen; + lpt_yyssp -= lpt_yylen; +#ifdef YYLSP_NEEDED + lpt_yylsp -= lpt_yylen; +#endif + +#if YYDEBUG != 0 + if (lpt_yydebug) + { + short *ssp1 = lpt_yyss - 1; + fprintf (stderr, "state stack now"); + while (ssp1 != lpt_yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + + *++lpt_yyvsp = lpt_yyval; + +#ifdef YYLSP_NEEDED + lpt_yylsp++; + if (lpt_yylen == 0) + { + lpt_yylsp->first_line = lpt_yylloc.first_line; + lpt_yylsp->first_column = lpt_yylloc.first_column; + lpt_yylsp->last_line = (lpt_yylsp-1)->last_line; + lpt_yylsp->last_column = (lpt_yylsp-1)->last_column; + lpt_yylsp->text = 0; + } + else + { + lpt_yylsp->last_line = (lpt_yylsp+lpt_yylen-1)->last_line; + lpt_yylsp->last_column = (lpt_yylsp+lpt_yylen-1)->last_column; + } +#endif + + /* Now "shift" the result of the reduction. + Determine what state that goes to, + based on the state we popped back to + and the rule number reduced by. */ + + lpt_yyn = lpt_yyr1[lpt_yyn]; + + lpt_yystate = lpt_yypgoto[lpt_yyn - YYNTBASE] + *lpt_yyssp; + if (lpt_yystate >= 0 && lpt_yystate <= YYLAST && lpt_yycheck[lpt_yystate] == *lpt_yyssp) + lpt_yystate = lpt_yytable[lpt_yystate]; + else + lpt_yystate = lpt_yydefgoto[lpt_yyn - YYNTBASE]; + + goto lpt_yynewstate; + +lpt_yyerrlab: /* here on detecting error */ + + if (! lpt_yyerrstatus) + /* If not already recovering from an error, report this error. */ + { + ++lpt_yynerrs; + +#ifdef YYERROR_VERBOSE + lpt_yyn = lpt_yypact[lpt_yystate]; + + if (lpt_yyn > YYFLAG && lpt_yyn < YYLAST) + { + int size = 0; + char *msg; + int x, count; + + count = 0; + /* Start X at -lpt_yyn if nec to avoid negative indexes in lpt_yycheck. */ + for (x = (lpt_yyn < 0 ? -lpt_yyn : 0); + x < (sizeof(lpt_yytname) / sizeof(char *)); x++) + if (lpt_yycheck[x + lpt_yyn] == x) + size += strlen(lpt_yytname[x]) + 15, count++; + msg = (char *) malloc(size + 15); + if (msg != 0) + { + strcpy(msg, "parse error"); + + if (count < 5) + { + count = 0; + for (x = (lpt_yyn < 0 ? -lpt_yyn : 0); + x < (sizeof(lpt_yytname) / sizeof(char *)); x++) + if (lpt_yycheck[x + lpt_yyn] == x) + { + strcat(msg, count == 0 ? ", expecting `" : " or `"); + strcat(msg, lpt_yytname[x]); + strcat(msg, "'"); + count++; + } + } + lpt_yyerror(msg); + free(msg); + } + else + lpt_yyerror ("parse error; also virtual memory exceeded"); + } + else +#endif /* YYERROR_VERBOSE */ + lpt_yyerror("parse error"); + } + + goto lpt_yyerrlab1; +lpt_yyerrlab1: /* here on error raised explicitly by an action */ + + if (lpt_yyerrstatus == 3) + { + /* if just tried and failed to reuse lookahead token after an error, discard it. */ + + /* return failure if at end of input */ + if (lpt_yychar == YYEOF) + YYABORT; + +#if YYDEBUG != 0 + if (lpt_yydebug) + fprintf(stderr, "Discarding token %d (%s).\n", lpt_yychar, lpt_yytname[lpt_yychar1]); +#endif + + lpt_yychar = YYEMPTY; + } + + /* Else will try to reuse lookahead token + after shifting the error token. */ + + lpt_yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto lpt_yyerrhandle; + +lpt_yyerrdefault: /* current state does not do anything special for the error token. */ + +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + lpt_yyn = lpt_yydefact[lpt_yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/ + if (lpt_yyn) goto lpt_yydefault; +#endif + +lpt_yyerrpop: /* pop the current state because it cannot handle the error token */ + + if (lpt_yyssp == lpt_yyss) YYABORT; + lpt_yyvsp--; + lpt_yystate = *--lpt_yyssp; +#ifdef YYLSP_NEEDED + lpt_yylsp--; +#endif + +#if YYDEBUG != 0 + if (lpt_yydebug) + { + short *ssp1 = lpt_yyss - 1; + fprintf (stderr, "Error: state stack now"); + while (ssp1 != lpt_yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + +lpt_yyerrhandle: + + lpt_yyn = lpt_yypact[lpt_yystate]; + if (lpt_yyn == YYFLAG) + goto lpt_yyerrdefault; + + lpt_yyn += YYTERROR; + if (lpt_yyn < 0 || lpt_yyn > YYLAST || lpt_yycheck[lpt_yyn] != YYTERROR) + goto lpt_yyerrdefault; + + lpt_yyn = lpt_yytable[lpt_yyn]; + if (lpt_yyn < 0) + { + if (lpt_yyn == YYFLAG) + goto lpt_yyerrpop; + lpt_yyn = -lpt_yyn; + goto lpt_yyreduce; + } + else if (lpt_yyn == 0) + goto lpt_yyerrpop; + + if (lpt_yyn == YYFINAL) + YYACCEPT; + +#if YYDEBUG != 0 + if (lpt_yydebug) + fprintf(stderr, "Shifting error token, "); +#endif + + *++lpt_yyvsp = lpt_yylval; +#ifdef YYLSP_NEEDED + *++lpt_yylsp = lpt_yylloc; +#endif + + lpt_yystate = lpt_yyn; + goto lpt_yynewstate; + + lpt_yyacceptlab: + /* YYACCEPT comes here. */ + if (lpt_yyfree_stacks) + { + free (lpt_yyss); + free (lpt_yyvs); +#ifdef YYLSP_NEEDED + free (lpt_yyls); +#endif + } + return 0; + + lpt_yyabortlab: + /* YYABORT comes here. */ + if (lpt_yyfree_stacks) + { + free (lpt_yyss); + free (lpt_yyvs); +#ifdef YYLSP_NEEDED + free (lpt_yyls); +#endif + } + return 1; +} + + +static void lpt_yy_delete_allocated_memory(void) +{ + /* free memory allocated by flex. Otherwise some memory is not freed. + This is a bit tricky. There is not much documentation about this, but a lot of + reports of memory that keeps allocated */ + + /* If you get errors on this function call, just comment it. This will only result + in some memory that is not being freed. */ + +# if defined YY_CURRENT_BUFFER + /* flex defines the macro YY_CURRENT_BUFFER, so you should only get here if lp_rlpt.h is + generated by flex */ + /* lex doesn't define this macro and thus should not come here, but lex doesn't has + this memory leak also ...*/ + + lpt_yy_delete_buffer(YY_CURRENT_BUFFER); /* comment this line if you have problems with it */ + lpt_yy_init = 1; /* make sure that the next time memory is allocated again */ + lpt_yy_start = 0; +# endif +} + +static int parse(void) +{ + return(lpt_yyparse()); +} + +lprec * __WINAPI read_lpt(FILE *filename, int verbose, char *lp_name) +{ + lpt_yyin = filename; + return(yacc_read(verbose, lp_name, &lpt_yylineno, parse, lpt_yy_delete_allocated_memory)); +} + +lprec * __WINAPI read_LPT(char *filename, int verbose, char *lp_name) +{ + FILE *fpin; + lprec *lp = NULL; + + if((fpin = fopen(filename, "r")) != NULL) { + lp = read_lpt(fpin, verbose, lp_name); + fclose(fpin); + } + return(lp); +} Index: src/tools/solver/lp_solve/lp_rlpt.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_rlpt.h diff -N src/tools/solver/lp_solve/lp_rlpt.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_rlpt.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1839 @@ +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header: /var/lib/cvs/oe-netbook/gnumeric/gnumeric-1.3.1/1.3.1-20040809.patch,v 1.1 2004/08/10 23:40:01 wookey Exp $ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include +#include +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define lpt_yyconst const +#else +#define lpt_yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN lpt_yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((lpt_yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE lpt_yyrestart( lpt_yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct lpt_yy_buffer_state *YY_BUFFER_STATE; + +extern int lpt_yyleng; +extern FILE *lpt_yyin, *lpt_yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * lpt_yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the lpt_yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define lpt_yyless(n) \ + do \ + { \ + /* Undo effects of setting up lpt_yytext. */ \ + *lpt_yy_cp = lpt_yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + lpt_yy_c_buf_p = lpt_yy_cp = lpt_yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up lpt_yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) lpt_yyunput( c, lpt_yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int lpt_yy_size_t; + + +struct lpt_yy_buffer_state + { + FILE *lpt_yy_input_file; + + char *lpt_yy_ch_buf; /* input buffer */ + char *lpt_yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + lpt_yy_size_t lpt_yy_buf_size; + + /* Number of characters read into lpt_yy_ch_buf, not including EOB + * characters. + */ + int lpt_yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int lpt_yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int lpt_yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int lpt_yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int lpt_yy_fill_buffer; + + int lpt_yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via lpt_yyrestart()), so that the user can continue scanning by + * just pointing lpt_yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE lpt_yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER lpt_yy_current_buffer + + +/* lpt_yy_hold_char holds the character lost when lpt_yytext is formed. */ +static char lpt_yy_hold_char; + +static int lpt_yy_n_chars; /* number of characters read into lpt_yy_ch_buf */ + + +int lpt_yyleng; + +/* Points to current character in buffer. */ +static char *lpt_yy_c_buf_p = (char *) 0; +static int lpt_yy_init = 1; /* whether we need to initialize */ +static int lpt_yy_start = 0; /* start state number */ + +/* Flag which is used to allow lpt_yywrap()'s to do buffer switches + * instead of setting up a fresh lpt_yyin. A bit of a hack ... + */ +static int lpt_yy_did_buffer_switch_on_eof; + +void lpt_yyrestart YY_PROTO(( FILE *input_file )); + +void lpt_yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void lpt_yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE lpt_yy_create_buffer YY_PROTO(( FILE *file, int size )); +void lpt_yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void lpt_yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void lpt_yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER lpt_yy_flush_buffer( lpt_yy_current_buffer ) + +YY_BUFFER_STATE lpt_yy_scan_buffer YY_PROTO(( char *base, lpt_yy_size_t size )); +YY_BUFFER_STATE lpt_yy_scan_string YY_PROTO(( lpt_yyconst char *lpt_yy_str )); +YY_BUFFER_STATE lpt_yy_scan_bytes YY_PROTO(( lpt_yyconst char *bytes, int len )); + +static void *lpt_yy_flex_alloc YY_PROTO(( lpt_yy_size_t )); +static void *lpt_yy_flex_realloc YY_PROTO(( void *, lpt_yy_size_t )); +static void lpt_yy_flex_free YY_PROTO(( void * )); + +#define lpt_yy_new_buffer lpt_yy_create_buffer + +#define lpt_yy_set_interactive(is_interactive) \ + { \ + if ( ! lpt_yy_current_buffer ) \ + lpt_yy_current_buffer = lpt_yy_create_buffer( lpt_yyin, YY_BUF_SIZE ); \ + lpt_yy_current_buffer->lpt_yy_is_interactive = is_interactive; \ + } + +#define lpt_yy_set_bol(at_bol) \ + { \ + if ( ! lpt_yy_current_buffer ) \ + lpt_yy_current_buffer = lpt_yy_create_buffer( lpt_yyin, YY_BUF_SIZE ); \ + lpt_yy_current_buffer->lpt_yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (lpt_yy_current_buffer->lpt_yy_at_bol) + + +#define YY_USES_REJECT +typedef unsigned char YY_CHAR; +FILE *lpt_yyin = (FILE *) 0, *lpt_yyout = (FILE *) 0; +typedef int lpt_yy_state_type; +#define YY_FLEX_LEX_COMPAT +extern int lpt_yylineno; +int lpt_yylineno = 1; +extern char lpt_yytext[]; + + +static lpt_yy_state_type lpt_yy_get_previous_state YY_PROTO(( void )); +static lpt_yy_state_type lpt_yy_try_NUL_trans YY_PROTO(( lpt_yy_state_type current_state )); +static int lpt_yy_get_next_buffer YY_PROTO(( void )); +static void lpt_yy_fatal_error YY_PROTO(( lpt_yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up lpt_yytext. + */ +#define YY_DO_BEFORE_ACTION \ + lpt_yytext_ptr = lpt_yy_bp; \ + lpt_yyleng = (int) (lpt_yy_cp - lpt_yy_bp); \ + lpt_yy_hold_char = *lpt_yy_cp; \ + *lpt_yy_cp = '\0'; \ + if ( lpt_yyleng + lpt_yy_more_offset >= YYLMAX ) \ + YY_FATAL_ERROR( "token too large, exceeds YYLMAX" ); \ + lpt_yy_flex_strncpy( &lpt_yytext[lpt_yy_more_offset], lpt_yytext_ptr, lpt_yyleng + 1 ); \ + lpt_yyleng += lpt_yy_more_offset; \ + lpt_yy_prev_more_offset = lpt_yy_more_offset; \ + lpt_yy_more_offset = 0; \ + lpt_yy_c_buf_p = lpt_yy_cp; + +#define YY_NUM_RULES 25 +#define YY_END_OF_BUFFER 26 +static lpt_yyconst short int lpt_yy_acclist[173] = + { 0, + 22, 22, 26, 24, 25, 5, 24, 25, 5, 25, + 21, 24, 25, 15, 24, 25, 24, 25, 11, 12, + 24, 25, 22, 24, 25, 22, 24, 25, 23, 24, + 25, 21, 24, 25, 21, 24, 25, 1, 24, 25, + 21, 24, 25, 21, 24, 25, 21, 24, 25, 21, + 24, 25, 21, 24, 25, 21, 24, 25, 4, 25, + 2, 25, 3, 4, 25, 5, 21, 20, 15, 12, + 11, 12, 22, 23, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 8, 21, 21, 12, + 21, 20, 16, 21, 21, 10, 21, 16, 21, 16, + + 21, 7, 21, 6, 21, 21, 21, 18, 21, 8, + 21, 21, 21, 13, 14, 21, 19, 21, 21, 21, + 21, 21, 21, 17, 21, 21, 21, 21, 9, 21, + 21, 21, 21, 21, 17, 21, 21, 21, 16, 21, + 9, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 16, 21, 16, 21, 21, 7, 21, 21, 6, 21, + 21, 16, 21, 16, 21, 7, 21, 6, 21, 13, + 8, 17 + } ; + +static lpt_yyconst short int lpt_yy_accept[139] = + { 0, + 1, 2, 3, 3, 3, 4, 6, 9, 11, 14, + 17, 19, 23, 26, 29, 32, 35, 38, 41, 44, + 47, 50, 53, 56, 59, 61, 63, 66, 67, 67, + 68, 69, 70, 70, 71, 71, 73, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 89, 90, 90, 90, 91, 92, 93, 95, + 96, 98, 100, 102, 104, 106, 107, 108, 110, 112, + 113, 114, 115, 117, 118, 119, 120, 121, 122, 123, + 124, 126, 127, 128, 128, 129, 131, 132, 133, 134, + 135, 135, 137, 138, 138, 138, 139, 141, 143, 144, + + 145, 146, 147, 148, 149, 149, 150, 150, 150, 151, + 153, 155, 156, 158, 159, 161, 161, 162, 162, 162, + 164, 166, 168, 170, 170, 170, 170, 171, 171, 171, + 172, 172, 172, 172, 172, 172, 173, 173 + } ; + +static lpt_yyconst int lpt_yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 1, 7, 6, 8, 9, 6, 10, 11, 11, + 10, 10, 10, 10, 10, 10, 10, 12, 6, 13, + 14, 15, 6, 6, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 6, 26, 27, 28, 29, 6, + 6, 30, 31, 32, 33, 6, 6, 34, 35, 36, + 1, 37, 1, 1, 6, 6, 16, 17, 18, 19, + + 20, 21, 22, 23, 24, 25, 6, 26, 27, 28, + 29, 6, 6, 30, 31, 32, 33, 6, 6, 34, + 35, 36, 6, 6, 6, 6, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static lpt_yyconst int lpt_yy_meta[38] = + { 0, + 1, 2, 2, 2, 2, 3, 2, 2, 3, 3, + 3, 3, 1, 1, 1, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 1 + } ; + +static lpt_yyconst short int lpt_yy_base[141] = + { 0, + 0, 21, 36, 43, 265, 266, 42, 46, 252, 48, + 47, 50, 249, 266, 248, 32, 52, 266, 53, 39, + 54, 56, 57, 76, 266, 266, 266, 0, 237, 248, + 266, 68, 231, 69, 80, 83, 90, 266, 266, 66, + 246, 71, 83, 92, 90, 94, 95, 100, 98, 105, + 102, 110, 103, 236, 113, 124, 119, 244, 124, 113, + 243, 125, 126, 130, 131, 135, 136, 242, 241, 137, + 138, 228, 239, 266, 139, 140, 141, 144, 145, 146, + 155, 156, 165, 222, 163, 152, 162, 168, 167, 169, + 231, 236, 172, 215, 222, 177, 233, 232, 101, 173, + + 170, 180, 182, 183, 214, 184, 219, 209, 189, 192, + 193, 197, 228, 199, 227, 210, 203, 221, 179, 224, + 223, 222, 221, 200, 199, 198, 266, 205, 199, 266, + 194, 188, 184, 179, 134, 266, 266, 224, 69, 39 + } ; + +static lpt_yyconst short int lpt_yy_def[141] = + { 0, + 137, 1, 138, 138, 137, 137, 139, 139, 140, 137, + 137, 137, 137, 137, 137, 140, 140, 137, 140, 140, + 140, 140, 140, 140, 137, 137, 137, 8, 137, 140, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 137, 137, 137, 140, 137, 140, 140, + 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, + 140, 137, 140, 137, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 137, 140, 140, 140, 140, 140, 140, + 137, 140, 140, 137, 137, 140, 140, 140, 140, 140, + + 140, 140, 140, 140, 137, 140, 137, 137, 140, 140, + 140, 140, 140, 140, 140, 137, 140, 137, 137, 140, + 140, 140, 140, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 0, 137, 137, 137 + } ; + +static lpt_yyconst short int lpt_yy_nxt[304] = + { 0, + 6, 7, 8, 7, 7, 9, 10, 10, 11, 12, + 12, 6, 13, 14, 15, 9, 9, 9, 9, 9, + 16, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 17, 9, 9, 9, 9, 9, 18, 19, 26, 27, + 20, 30, 21, 31, 22, 26, 27, 23, 29, 29, + 31, 24, 29, 29, 32, 32, 34, 34, 35, 36, + 36, 40, 41, 31, 31, 31, 44, 31, 31, 37, + 28, 33, 47, 45, 32, 32, 42, 31, 34, 34, + 48, 43, 31, 46, 49, 57, 41, 31, 37, 34, + 34, 35, 36, 36, 31, 50, 55, 55, 59, 56, + + 56, 31, 37, 31, 51, 31, 31, 52, 53, 31, + 61, 31, 31, 31, 31, 60, 31, 62, 69, 70, + 71, 31, 56, 56, 31, 63, 110, 65, 64, 66, + 31, 67, 68, 56, 56, 31, 31, 31, 73, 75, + 76, 31, 31, 69, 77, 78, 31, 31, 31, 31, + 31, 31, 31, 79, 80, 31, 31, 31, 86, 81, + 83, 82, 91, 31, 136, 88, 31, 31, 85, 94, + 87, 89, 90, 31, 31, 93, 31, 99, 31, 31, + 31, 31, 98, 31, 31, 92, 96, 100, 31, 106, + 101, 31, 103, 31, 31, 31, 109, 97, 127, 102, + + 31, 104, 111, 31, 31, 112, 113, 125, 31, 115, + 31, 135, 134, 127, 31, 117, 122, 114, 123, 97, + 133, 132, 120, 121, 25, 25, 25, 130, 131, 130, + 129, 128, 31, 31, 31, 31, 126, 124, 31, 31, + 119, 118, 116, 31, 31, 108, 107, 31, 105, 95, + 31, 84, 31, 31, 31, 74, 72, 58, 54, 31, + 33, 39, 38, 31, 137, 5, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + + 137, 137, 137 + } ; + +static lpt_yyconst short int lpt_yy_chk[304] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, + 2, 140, 2, 16, 2, 4, 4, 2, 7, 7, + 20, 2, 8, 8, 10, 10, 11, 11, 12, 12, + 12, 16, 17, 17, 19, 21, 20, 22, 23, 12, + 139, 10, 23, 21, 32, 32, 19, 40, 34, 34, + 23, 19, 42, 22, 24, 40, 24, 24, 34, 35, + 35, 36, 36, 36, 43, 24, 37, 37, 42, 37, + + 37, 45, 36, 44, 24, 46, 47, 24, 24, 49, + 44, 48, 99, 51, 53, 43, 50, 45, 52, 53, + 53, 52, 55, 55, 60, 46, 99, 48, 47, 49, + 57, 50, 51, 56, 56, 59, 62, 63, 57, 59, + 60, 64, 65, 66, 62, 63, 66, 67, 70, 71, + 75, 76, 77, 64, 65, 78, 79, 80, 76, 67, + 71, 70, 81, 86, 135, 78, 81, 82, 75, 83, + 77, 79, 80, 87, 85, 82, 83, 87, 89, 88, + 90, 101, 86, 93, 100, 81, 85, 88, 96, 93, + 89, 102, 90, 103, 104, 106, 96, 85, 119, 89, + + 109, 90, 100, 110, 111, 101, 102, 117, 112, 104, + 114, 134, 133, 119, 117, 106, 112, 103, 114, 109, + 132, 131, 110, 111, 138, 138, 138, 129, 128, 126, + 125, 124, 123, 122, 121, 120, 118, 116, 115, 113, + 108, 107, 105, 98, 97, 95, 94, 92, 91, 84, + 73, 72, 69, 68, 61, 58, 54, 41, 33, 30, + 29, 15, 13, 9, 5, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + + 137, 137, 137 + } ; + +static lpt_yy_state_type lpt_yy_state_buf[YY_BUF_SIZE + 2], *lpt_yy_state_ptr; +static char *lpt_yy_full_match; +static int lpt_yy_lp; +#define REJECT \ +{ \ +*lpt_yy_cp = lpt_yy_hold_char; /* undo effects of setting up lpt_yytext */ \ +lpt_yy_cp = lpt_yy_full_match; /* restore poss. backed-over text */ \ +++lpt_yy_lp; \ +goto find_rule; \ +} +static int lpt_yy_more_offset = 0; +static int lpt_yy_prev_more_offset = 0; +#define lpt_yymore() (lpt_yy_more_offset = lpt_yy_flex_strlen( lpt_yytext )) +#define YY_NEED_STRLEN +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET \ + { \ + lpt_yy_more_offset = lpt_yy_prev_more_offset; \ + lpt_yyleng -= lpt_yy_more_offset; \ + } +#ifndef YYLMAX +#define YYLMAX 8192 +#endif + +char lpt_yytext[YYLMAX]; +char *lpt_yytext_ptr; +#define INITIAL 0 +#define LINECOMMENT 1 + + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int lpt_yywrap YY_PROTO(( void )); +#else +extern int lpt_yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void lpt_yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef lpt_yytext_ptr +static void lpt_yy_flex_strncpy YY_PROTO(( char *, lpt_yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int lpt_yy_flex_strlen YY_PROTO(( lpt_yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int lpt_yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int lpt_yy_start_stack_ptr = 0; +static int lpt_yy_start_stack_depth = 0; +static int *lpt_yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void lpt_yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void lpt_yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int lpt_yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( lpt_yytext, lpt_yyleng, 1, lpt_yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( lpt_yy_current_buffer->lpt_yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( lpt_yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( lpt_yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, lpt_yyin )) == 0) \ + && ferror( lpt_yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "lpt_yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef lpt_yyterminate +#define lpt_yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) lpt_yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int lpt_yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after lpt_yytext and lpt_yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + if ( lpt_yyleng > 0 ) \ + lpt_yy_current_buffer->lpt_yy_at_bol = \ + (lpt_yytext[lpt_yyleng - 1] == '\n'); \ + YY_USER_ACTION + +YY_DECL + { + register lpt_yy_state_type lpt_yy_current_state; + register char *lpt_yy_cp, *lpt_yy_bp; + register int lpt_yy_act; + + + + + if ( lpt_yy_init ) + { + lpt_yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! lpt_yy_start ) + lpt_yy_start = 1; /* first start state */ + + if ( ! lpt_yyin ) + lpt_yyin = stdin; + + if ( ! lpt_yyout ) + lpt_yyout = stdout; + + if ( ! lpt_yy_current_buffer ) + lpt_yy_current_buffer = + lpt_yy_create_buffer( lpt_yyin, YY_BUF_SIZE ); + + lpt_yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + lpt_yy_cp = lpt_yy_c_buf_p; + + /* Support of lpt_yytext. */ + *lpt_yy_cp = lpt_yy_hold_char; + + /* lpt_yy_bp points to the position in lpt_yy_ch_buf of the start of + * the current run. + */ + lpt_yy_bp = lpt_yy_cp; + + lpt_yy_current_state = lpt_yy_start; + lpt_yy_current_state += YY_AT_BOL(); + lpt_yy_state_ptr = lpt_yy_state_buf; + *lpt_yy_state_ptr++ = lpt_yy_current_state; +lpt_yy_match: + do + { + register YY_CHAR lpt_yy_c = lpt_yy_ec[YY_SC_TO_UI(*lpt_yy_cp)]; + while ( lpt_yy_chk[lpt_yy_base[lpt_yy_current_state] + lpt_yy_c] != lpt_yy_current_state ) + { + lpt_yy_current_state = (int) lpt_yy_def[lpt_yy_current_state]; + if ( lpt_yy_current_state >= 138 ) + lpt_yy_c = lpt_yy_meta[(unsigned int) lpt_yy_c]; + } + lpt_yy_current_state = lpt_yy_nxt[lpt_yy_base[lpt_yy_current_state] + (unsigned int) lpt_yy_c]; + *lpt_yy_state_ptr++ = lpt_yy_current_state; + ++lpt_yy_cp; + } + while ( lpt_yy_base[lpt_yy_current_state] != 266 ); + +lpt_yy_find_action: + lpt_yy_current_state = *--lpt_yy_state_ptr; + lpt_yy_lp = lpt_yy_accept[lpt_yy_current_state]; +find_rule: /* we branch to this label when backing up */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( lpt_yy_lp && lpt_yy_lp < lpt_yy_accept[lpt_yy_current_state + 1] ) + { + lpt_yy_act = lpt_yy_acclist[lpt_yy_lp]; + { + lpt_yy_full_match = lpt_yy_cp; + break; + } + } + --lpt_yy_cp; + lpt_yy_current_state = *--lpt_yy_state_ptr; + lpt_yy_lp = lpt_yy_accept[lpt_yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + if ( lpt_yy_act != YY_END_OF_BUFFER ) + { + int lpt_yyl; + for ( lpt_yyl = 0; lpt_yyl < lpt_yyleng; ++lpt_yyl ) + if ( lpt_yytext[lpt_yyl] == '\n' ) + ++lpt_yylineno; + } + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( lpt_yy_act ) + { /* beginning of action switch */ +case 1: +YY_RULE_SETUP +{ + BEGIN LINECOMMENT; +} /* begin skip LINECOMMENT */ + YY_BREAK +case 2: +YY_RULE_SETUP +{ + BEGIN INITIAL; +} /* end skip LINECOMMENT */ + YY_BREAK +case 3: +YY_RULE_SETUP +{ + BEGIN INITIAL; +} /* end skip LINECOMMENT */ + YY_BREAK +case 4: +YY_RULE_SETUP +{ +} + YY_BREAK +case 5: +YY_RULE_SETUP +{ +} + YY_BREAK +case 6: +YY_RULE_SETUP +{ + /* strcpy(Last_var, (char *)lpt_yytext); */ + return(MINIMISE); +} + YY_BREAK +case 7: +YY_RULE_SETUP +{ + /* strcpy(Last_var, (char *)lpt_yytext); */ + return(MAXIMISE); +} + YY_BREAK +case 8: +YY_RULE_SETUP +{ + /* strcpy(Last_var, (char *)lpt_yytext); */ + return(SUBJECTTO); +} + YY_BREAK +case 9: +YY_RULE_SETUP +{ + /* strcpy(Last_var, (char *)lpt_yytext); */ + return(BOUNDS); +} + YY_BREAK +case 10: +YY_RULE_SETUP +{ + /* strcpy(Last_var, (char *)lpt_yytext); */ + return(END); +} + YY_BREAK +case 11: +YY_RULE_SETUP +{ + f = atof((char *)lpt_yytext); + return(INTCONS); +} /* f contains the last float */ + YY_BREAK +case 12: +YY_RULE_SETUP +{ + f = atof((char *)lpt_yytext); + return(CONS); +} /* f contains the last float */ + YY_BREAK +case 13: +YY_RULE_SETUP +{ + char *ptr; + + f = DEF_INFINITE; + Sign = 0; + ptr = (char *)lpt_yytext; + while (isspace(*ptr)) ptr++; + if(*ptr == '-') + Sign = 1; + return(INF); +} /* f contains the last float */ + YY_BREAK +case 14: +YY_RULE_SETUP +{ + strcpy(Last_var0, Last_var); + strcpy(Last_var, (char *)lpt_yytext); + return(FR); +} + YY_BREAK +case 15: +YY_RULE_SETUP +{ + Sign = 0; + for(x = 0; x < lpt_yyleng; x++) + if(lpt_yytext[x] == '-' || lpt_yytext[x] == '+') + Sign = (Sign == (lpt_yytext[x] == '+')); + return (SIGN); + /* Sign is TRUE if the sign-string + represents a '-'. Otherwise Sign + is FALSE */ +} + YY_BREAK +case 16: +YY_RULE_SETUP +{ + Within_gen_decl = Within_bin_decl = Within_sec_decl = Within_sos_decl = FALSE; + if((toupper(*lpt_yytext) == 'G') || (toupper(*lpt_yytext) == 'I')) + Within_gen_decl = TRUE; + else + Within_bin_decl = TRUE; + return(SEC_INT); +} + YY_BREAK +case 17: +YY_RULE_SETUP +{ + Within_gen_decl = Within_bin_decl = Within_sec_decl = Within_sos_decl = FALSE; + Within_sec_decl = TRUE; + return(SEC_SEC); +} + YY_BREAK +case 18: +YY_RULE_SETUP +{ + Within_gen_decl = Within_bin_decl = Within_sec_decl = Within_sos_decl = FALSE; + Within_sos_decl = TRUE; + return(SEC_SOS); +} + YY_BREAK +case 19: +YY_RULE_SETUP +{ + strcpy(Last_var, (char *)lpt_yytext); + return(SOSTYPE); +} + YY_BREAK +case 20: +YY_RULE_SETUP +{ + strcpy(Last_var, (char *)lpt_yytext); + Last_var[strlen(Last_var) - 1] = 0; + return(VARIABLECOLON); +} + YY_BREAK +case 21: +YY_RULE_SETUP +{ + strcpy(Last_var, (char *)lpt_yytext); + return(VAR); +} + YY_BREAK +case 22: +YY_RULE_SETUP +{ + return(RE_OPLE); +} + YY_BREAK +case 23: +YY_RULE_SETUP +{ + return(RE_OPGE); +} + YY_BREAK +case 24: +YY_RULE_SETUP +{ + report(NULL, CRITICAL, "LEX ERROR : %s lineno %d\n", lpt_yytext, lpt_yylineno); + return(UNDEFINED); +} + YY_BREAK +case 25: +YY_RULE_SETUP +ECHO; + YY_BREAK + case YY_STATE_EOF(INITIAL): + case YY_STATE_EOF(LINECOMMENT): + lpt_yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int lpt_yy_amount_of_matched_text = (int) (lpt_yy_cp - lpt_yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *lpt_yy_cp = lpt_yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( lpt_yy_current_buffer->lpt_yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed lpt_yyin at a new source and called + * lpt_yylex(). If so, then we have to assure + * consistency between lpt_yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + lpt_yy_n_chars = lpt_yy_current_buffer->lpt_yy_n_chars; + lpt_yy_current_buffer->lpt_yy_input_file = lpt_yyin; + lpt_yy_current_buffer->lpt_yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for lpt_yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since lpt_yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( lpt_yy_c_buf_p <= &lpt_yy_current_buffer->lpt_yy_ch_buf[lpt_yy_n_chars] ) + { /* This was really a NUL. */ + lpt_yy_state_type lpt_yy_next_state; + + lpt_yy_c_buf_p = lpt_yytext_ptr + lpt_yy_amount_of_matched_text; + + lpt_yy_current_state = lpt_yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * lpt_yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + lpt_yy_next_state = lpt_yy_try_NUL_trans( lpt_yy_current_state ); + + lpt_yy_bp = lpt_yytext_ptr + YY_MORE_ADJ; + + if ( lpt_yy_next_state ) + { + /* Consume the NUL. */ + lpt_yy_cp = ++lpt_yy_c_buf_p; + lpt_yy_current_state = lpt_yy_next_state; + goto lpt_yy_match; + } + + else + { + lpt_yy_cp = lpt_yy_c_buf_p; + goto lpt_yy_find_action; + } + } + + else switch ( lpt_yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + lpt_yy_did_buffer_switch_on_eof = 0; + + if ( lpt_yywrap() ) + { + /* Note: because we've taken care in + * lpt_yy_get_next_buffer() to have set up + * lpt_yytext, we can now set up + * lpt_yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + lpt_yy_c_buf_p = lpt_yytext_ptr + YY_MORE_ADJ; + + lpt_yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! lpt_yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + lpt_yy_c_buf_p = + lpt_yytext_ptr + lpt_yy_amount_of_matched_text; + + lpt_yy_current_state = lpt_yy_get_previous_state(); + + lpt_yy_cp = lpt_yy_c_buf_p; + lpt_yy_bp = lpt_yytext_ptr + YY_MORE_ADJ; + goto lpt_yy_match; + + case EOB_ACT_LAST_MATCH: + lpt_yy_c_buf_p = + &lpt_yy_current_buffer->lpt_yy_ch_buf[lpt_yy_n_chars]; + + lpt_yy_current_state = lpt_yy_get_previous_state(); + + lpt_yy_cp = lpt_yy_c_buf_p; + lpt_yy_bp = lpt_yytext_ptr + YY_MORE_ADJ; + goto lpt_yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of lpt_yylex */ + + +/* lpt_yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int lpt_yy_get_next_buffer() + { + register char *dest = lpt_yy_current_buffer->lpt_yy_ch_buf; + register char *source = lpt_yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( lpt_yy_c_buf_p > &lpt_yy_current_buffer->lpt_yy_ch_buf[lpt_yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( lpt_yy_current_buffer->lpt_yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( lpt_yy_c_buf_p - lpt_yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (lpt_yy_c_buf_p - lpt_yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( lpt_yy_current_buffer->lpt_yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + lpt_yy_current_buffer->lpt_yy_n_chars = lpt_yy_n_chars = 0; + + else + { + int num_to_read = + lpt_yy_current_buffer->lpt_yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = lpt_yy_current_buffer; + + int lpt_yy_c_buf_p_offset = + (int) (lpt_yy_c_buf_p - b->lpt_yy_ch_buf); + + if ( b->lpt_yy_is_our_buffer ) + { + int new_size = b->lpt_yy_buf_size * 2; + + if ( new_size <= 0 ) + b->lpt_yy_buf_size += b->lpt_yy_buf_size / 8; + else + b->lpt_yy_buf_size *= 2; + + b->lpt_yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + lpt_yy_flex_realloc( (void *) b->lpt_yy_ch_buf, + b->lpt_yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->lpt_yy_ch_buf = 0; + + if ( ! b->lpt_yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + lpt_yy_c_buf_p = &b->lpt_yy_ch_buf[lpt_yy_c_buf_p_offset]; + + num_to_read = lpt_yy_current_buffer->lpt_yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&lpt_yy_current_buffer->lpt_yy_ch_buf[number_to_move]), + lpt_yy_n_chars, num_to_read ); + + lpt_yy_current_buffer->lpt_yy_n_chars = lpt_yy_n_chars; + } + + if ( lpt_yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + lpt_yyrestart( lpt_yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + lpt_yy_current_buffer->lpt_yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + lpt_yy_n_chars += number_to_move; + lpt_yy_current_buffer->lpt_yy_ch_buf[lpt_yy_n_chars] = YY_END_OF_BUFFER_CHAR; + lpt_yy_current_buffer->lpt_yy_ch_buf[lpt_yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + lpt_yytext_ptr = &lpt_yy_current_buffer->lpt_yy_ch_buf[0]; + + return ret_val; + } + + +/* lpt_yy_get_previous_state - get the state just before the EOB char was reached */ + +static lpt_yy_state_type lpt_yy_get_previous_state() + { + register lpt_yy_state_type lpt_yy_current_state; + register char *lpt_yy_cp; + + lpt_yy_current_state = lpt_yy_start; + lpt_yy_current_state += YY_AT_BOL(); + lpt_yy_state_ptr = lpt_yy_state_buf; + *lpt_yy_state_ptr++ = lpt_yy_current_state; + + for ( lpt_yy_cp = lpt_yytext_ptr + YY_MORE_ADJ; lpt_yy_cp < lpt_yy_c_buf_p; ++lpt_yy_cp ) + { + register YY_CHAR lpt_yy_c = (*lpt_yy_cp ? lpt_yy_ec[YY_SC_TO_UI(*lpt_yy_cp)] : 1); + while ( lpt_yy_chk[lpt_yy_base[lpt_yy_current_state] + lpt_yy_c] != lpt_yy_current_state ) + { + lpt_yy_current_state = (int) lpt_yy_def[lpt_yy_current_state]; + if ( lpt_yy_current_state >= 138 ) + lpt_yy_c = lpt_yy_meta[(unsigned int) lpt_yy_c]; + } + lpt_yy_current_state = lpt_yy_nxt[lpt_yy_base[lpt_yy_current_state] + (unsigned int) lpt_yy_c]; + *lpt_yy_state_ptr++ = lpt_yy_current_state; + } + + return lpt_yy_current_state; + } + + +/* lpt_yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = lpt_yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static lpt_yy_state_type lpt_yy_try_NUL_trans( lpt_yy_state_type lpt_yy_current_state ) +#else +static lpt_yy_state_type lpt_yy_try_NUL_trans( lpt_yy_current_state ) +lpt_yy_state_type lpt_yy_current_state; +#endif + { + register int lpt_yy_is_jam; + + register YY_CHAR lpt_yy_c = 1; + while ( lpt_yy_chk[lpt_yy_base[lpt_yy_current_state] + lpt_yy_c] != lpt_yy_current_state ) + { + lpt_yy_current_state = (int) lpt_yy_def[lpt_yy_current_state]; + if ( lpt_yy_current_state >= 138 ) + lpt_yy_c = lpt_yy_meta[(unsigned int) lpt_yy_c]; + } + lpt_yy_current_state = lpt_yy_nxt[lpt_yy_base[lpt_yy_current_state] + (unsigned int) lpt_yy_c]; + lpt_yy_is_jam = (lpt_yy_current_state == 137); + if ( ! lpt_yy_is_jam ) + *lpt_yy_state_ptr++ = lpt_yy_current_state; + + return lpt_yy_is_jam ? 0 : lpt_yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void lpt_yyunput( int c, register char *lpt_yy_bp ) +#else +static void lpt_yyunput( c, lpt_yy_bp ) +int c; +register char *lpt_yy_bp; +#endif + { + register char *lpt_yy_cp = lpt_yy_c_buf_p; + + /* undo effects of setting up lpt_yytext */ + *lpt_yy_cp = lpt_yy_hold_char; + + if ( lpt_yy_cp < lpt_yy_current_buffer->lpt_yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = lpt_yy_n_chars + 2; + register char *dest = &lpt_yy_current_buffer->lpt_yy_ch_buf[ + lpt_yy_current_buffer->lpt_yy_buf_size + 2]; + register char *source = + &lpt_yy_current_buffer->lpt_yy_ch_buf[number_to_move]; + + while ( source > lpt_yy_current_buffer->lpt_yy_ch_buf ) + *--dest = *--source; + + lpt_yy_cp += (int) (dest - source); + lpt_yy_bp += (int) (dest - source); + lpt_yy_current_buffer->lpt_yy_n_chars = + lpt_yy_n_chars = lpt_yy_current_buffer->lpt_yy_buf_size; + + if ( lpt_yy_cp < lpt_yy_current_buffer->lpt_yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--lpt_yy_cp = (char) c; + + if ( c == '\n' ) + --lpt_yylineno; + + lpt_yytext_ptr = lpt_yy_bp; + lpt_yy_hold_char = *lpt_yy_cp; + lpt_yy_c_buf_p = lpt_yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int lpt_yyinput() +#else +static int input() +#endif + { + int c; + + *lpt_yy_c_buf_p = lpt_yy_hold_char; + + if ( *lpt_yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* lpt_yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( lpt_yy_c_buf_p < &lpt_yy_current_buffer->lpt_yy_ch_buf[lpt_yy_n_chars] ) + /* This was really a NUL. */ + *lpt_yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = lpt_yy_c_buf_p - lpt_yytext_ptr; + ++lpt_yy_c_buf_p; + + switch ( lpt_yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because lpt_yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + lpt_yyrestart( lpt_yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( lpt_yywrap() ) + return EOF; + + if ( ! lpt_yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return lpt_yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + lpt_yy_c_buf_p = lpt_yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) lpt_yy_c_buf_p; /* cast for 8-bit char's */ + *lpt_yy_c_buf_p = '\0'; /* preserve lpt_yytext */ + lpt_yy_hold_char = *++lpt_yy_c_buf_p; + + lpt_yy_current_buffer->lpt_yy_at_bol = (c == '\n'); + if ( lpt_yy_current_buffer->lpt_yy_at_bol ) + ++lpt_yylineno; + + return c; + } + + +#ifdef YY_USE_PROTOS +void lpt_yyrestart( FILE *input_file ) +#else +void lpt_yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! lpt_yy_current_buffer ) + lpt_yy_current_buffer = lpt_yy_create_buffer( lpt_yyin, YY_BUF_SIZE ); + + lpt_yy_init_buffer( lpt_yy_current_buffer, input_file ); + lpt_yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void lpt_yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void lpt_yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( lpt_yy_current_buffer == new_buffer ) + return; + + if ( lpt_yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *lpt_yy_c_buf_p = lpt_yy_hold_char; + lpt_yy_current_buffer->lpt_yy_buf_pos = lpt_yy_c_buf_p; + lpt_yy_current_buffer->lpt_yy_n_chars = lpt_yy_n_chars; + } + + lpt_yy_current_buffer = new_buffer; + lpt_yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (lpt_yywrap()) processing, but the only time this flag + * is looked at is after lpt_yywrap() is called, so it's safe + * to go ahead and always set it. + */ + lpt_yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void lpt_yy_load_buffer_state( void ) +#else +void lpt_yy_load_buffer_state() +#endif + { + lpt_yy_n_chars = lpt_yy_current_buffer->lpt_yy_n_chars; + lpt_yytext_ptr = lpt_yy_c_buf_p = lpt_yy_current_buffer->lpt_yy_buf_pos; + lpt_yyin = lpt_yy_current_buffer->lpt_yy_input_file; + lpt_yy_hold_char = *lpt_yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE lpt_yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE lpt_yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) lpt_yy_flex_alloc( sizeof( struct lpt_yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in lpt_yy_create_buffer()" ); + + b->lpt_yy_buf_size = size; + + /* lpt_yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->lpt_yy_ch_buf = (char *) lpt_yy_flex_alloc( b->lpt_yy_buf_size + 2 ); + if ( ! b->lpt_yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in lpt_yy_create_buffer()" ); + + b->lpt_yy_is_our_buffer = 1; + + lpt_yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void lpt_yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void lpt_yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == lpt_yy_current_buffer ) + lpt_yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->lpt_yy_is_our_buffer ) + lpt_yy_flex_free( (void *) b->lpt_yy_ch_buf ); + + lpt_yy_flex_free( (void *) b ); + } + + +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif + +#ifdef YY_USE_PROTOS +void lpt_yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void lpt_yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + lpt_yy_flush_buffer( b ); + + b->lpt_yy_input_file = file; + b->lpt_yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->lpt_yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->lpt_yy_is_interactive = 0; +#else + b->lpt_yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void lpt_yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void lpt_yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->lpt_yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->lpt_yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->lpt_yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->lpt_yy_buf_pos = &b->lpt_yy_ch_buf[0]; + + b->lpt_yy_at_bol = 1; + b->lpt_yy_buffer_status = YY_BUFFER_NEW; + + if ( b == lpt_yy_current_buffer ) + lpt_yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE lpt_yy_scan_buffer( char *base, lpt_yy_size_t size ) +#else +YY_BUFFER_STATE lpt_yy_scan_buffer( base, size ) +char *base; +lpt_yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) lpt_yy_flex_alloc( sizeof( struct lpt_yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in lpt_yy_scan_buffer()" ); + + b->lpt_yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->lpt_yy_buf_pos = b->lpt_yy_ch_buf = base; + b->lpt_yy_is_our_buffer = 0; + b->lpt_yy_input_file = 0; + b->lpt_yy_n_chars = b->lpt_yy_buf_size; + b->lpt_yy_is_interactive = 0; + b->lpt_yy_at_bol = 1; + b->lpt_yy_fill_buffer = 0; + b->lpt_yy_buffer_status = YY_BUFFER_NEW; + + lpt_yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE lpt_yy_scan_string( lpt_yyconst char *lpt_yy_str ) +#else +YY_BUFFER_STATE lpt_yy_scan_string( lpt_yy_str ) +lpt_yyconst char *lpt_yy_str; +#endif + { + int len; + for ( len = 0; lpt_yy_str[len]; ++len ) + ; + + return lpt_yy_scan_bytes( lpt_yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE lpt_yy_scan_bytes( lpt_yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE lpt_yy_scan_bytes( bytes, len ) +lpt_yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + lpt_yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) lpt_yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in lpt_yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = lpt_yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in lpt_yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->lpt_yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void lpt_yy_push_state( int new_state ) +#else +static void lpt_yy_push_state( new_state ) +int new_state; +#endif + { + if ( lpt_yy_start_stack_ptr >= lpt_yy_start_stack_depth ) + { + lpt_yy_size_t new_size; + + lpt_yy_start_stack_depth += YY_START_STACK_INCR; + new_size = lpt_yy_start_stack_depth * sizeof( int ); + + if ( ! lpt_yy_start_stack ) + lpt_yy_start_stack = (int *) lpt_yy_flex_alloc( new_size ); + + else + lpt_yy_start_stack = (int *) lpt_yy_flex_realloc( + (void *) lpt_yy_start_stack, new_size ); + + if ( ! lpt_yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + lpt_yy_start_stack[lpt_yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void lpt_yy_pop_state() + { + if ( --lpt_yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(lpt_yy_start_stack[lpt_yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int lpt_yy_top_state() + { + return lpt_yy_start_stack[lpt_yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void lpt_yy_fatal_error( lpt_yyconst char msg[] ) +#else +static void lpt_yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine lpt_yyless() so it works in section 3 code. */ + +#undef lpt_yyless +#define lpt_yyless(n) \ + do \ + { \ + /* Undo effects of setting up lpt_yytext. */ \ + lpt_yytext[lpt_yyleng] = lpt_yy_hold_char; \ + lpt_yy_c_buf_p = lpt_yytext + n; \ + lpt_yy_hold_char = *lpt_yy_c_buf_p; \ + *lpt_yy_c_buf_p = '\0'; \ + lpt_yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef lpt_yytext_ptr +#ifdef YY_USE_PROTOS +static void lpt_yy_flex_strncpy( char *s1, lpt_yyconst char *s2, int n ) +#else +static void lpt_yy_flex_strncpy( s1, s2, n ) +char *s1; +lpt_yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int lpt_yy_flex_strlen( lpt_yyconst char *s ) +#else +static int lpt_yy_flex_strlen( s ) +lpt_yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *lpt_yy_flex_alloc( lpt_yy_size_t size ) +#else +static void *lpt_yy_flex_alloc( size ) +lpt_yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *lpt_yy_flex_realloc( void *ptr, lpt_yy_size_t size ) +#else +static void *lpt_yy_flex_realloc( ptr, size ) +void *ptr; +lpt_yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void lpt_yy_flex_free( void *ptr ) +#else +static void lpt_yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + lpt_yylex(); + return 0; + } +#endif + Index: src/tools/solver/lp_solve/lp_scale.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_scale.c diff -N src/tools/solver/lp_solve/lp_scale.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_scale.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,900 @@ + +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_scale.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* + Scaling routines for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: LGPL. + + Requires: lp_lib.h, lp_scale.h + + Release notes: + v5.0.0 1 January 2004 Significantly expanded and repackaged scaling + routines. + v5.0.1 20 February 2004 Modified rounding behaviour in several areas. + + ---------------------------------------------------------------------------------- +*/ + +/* First define scaling and unscaling primitives */ + +REAL scaled_value(lprec *lp, REAL value, int index) +{ + if(fabs(value) < lp->infinite) { + if(lp->scaling_used) + if(index > lp->rows) + value /= lp->scalars[index]; + else + value *= lp->scalars[index]; + } + else + value = my_sign(value)*lp->infinite; + return(value); +} + +REAL unscaled_value(lprec *lp, REAL value, int index) +{ + if(fabs(value) < lp->infinite) { + if(lp->scaling_used) + if(index > lp->rows) + value *= lp->scalars[index]; + else + value /= lp->scalars[index]; + } + else + value = my_sign(value)*lp->infinite; + return(value); +} + +STATIC REAL scaled_mat(lprec *lp, REAL value, int row, int col) +{ + if(lp->scaling_used) + value *= lp->scalars[row] * lp->scalars[lp->rows + col]; + return( value ); +} + +STATIC REAL unscaled_mat(lprec *lp, REAL value, int row, int col) +{ + if(lp->scaling_used) + value /= lp->scalars[row] * lp->scalars[lp->rows + col]; + return( value ); +} + +/* Compute the scale factor by the formulae: + FALSE: SUM (log |Aij|) ^ 2 + TRUE: SUM (log |Aij| - RowScale[i] - ColScale[j]) ^ 2 */ +REAL CurtisReidMeasure(lprec *lp, MYBOOL _Advanced, REAL *FRowScale, REAL *FColScale) +{ + int row, col, ent, colMax; + REAL value, logvalue, Result; + + Result=0; + colMax = lp->columns; + for(col=1; col<=colMax; col++) { + for(ent=lp->matA->col_end[col-1]; entmatA->col_end[col]; ent++) { + row=lp->matA->col_mat[ent].row_nr; + value=fabs(lp->matA->col_mat[ent].value); + if(value>0) { + logvalue=log(value); + if(_Advanced) + logvalue-= FRowScale[row] + FColScale[col]; + Result+=logvalue*logvalue; + } + } + } + return(Result); +} + +/* Implementation of the Curtis-Reid scaling based on the paper + "On the Automatic Scaling of Matrices for Gaussian + Elimination," Journal of the Institute of Mathematics and + Its Applications (1972) 10, 118-124. + + Solve the system | M E | (r) (sigma) + | | ( ) = ( ) + | E^T N | (c) ( tau ) + + by the conjugate gradient method (clever recurrences). + + E is the matrix A with all elements = 1 + + M is diagonal matrix of row counts (RowCount) + N is diagonal matrix of column counts (ColCount) + + sigma is the vector of row logarithm sums (RowSum) + tau is the vector of column logarithm sums (ColSum) + + r, c are resulting row and column scalings (RowScale, ColScale) */ + +int CurtisReidScales(lprec *lp, MYBOOL _Advanced, REAL *FRowScale, REAL *FColScale) +{ + int rowbase, row, col, ent; + REAL *RowScalem2, *ColScalem2, + *RowSum, *ColSum, + *residual_even, *residual_odd; + REAL sk, qk, ek, + skm1, qkm1, ekm1, + qkm2, qkqkm1, ekm2, ekekm1, + value, logvalue, + StopTolerance; + int *RowCount, *ColCount, colMax; + int Result; + MATrec *mat = lp->matA; + + if(CurtisReidMeasure(lp, _Advanced, FRowScale, FColScale)<0.1*get_nonzeros(lp)) + return(0); + + /* Allocate temporary memory and find RowSum and ColSum measures */ + + colMax = lp->columns; + + allocREAL(lp, &RowSum, lp->rows+1, TRUE); + allocINT(lp, &RowCount, lp->rows+1, TRUE); + allocREAL(lp, &residual_odd, lp->rows+1, TRUE); + + allocREAL(lp, &ColSum, colMax+1, TRUE); + allocINT(lp, &ColCount, colMax+1, TRUE); + allocREAL(lp, &residual_even, colMax+1, TRUE); + + allocREAL(lp, &RowScalem2, lp->rows+1, FALSE); + allocREAL(lp, &ColScalem2, colMax+1, FALSE); + + /* Set origin for row scaling (1=constraints only, 0=include OF) */ + rowbase = 0; + for(row=0; row < rowbase; row++) + FRowScale[row] = 1; + + for(col=1; col<=colMax; col++) { + for(ent=mat->col_end[col-1]; entcol_end[col]; ent++) { + row=mat->col_mat[ent].row_nr; + if(rowcol_mat[ent].value); + if(value>0) { + logvalue=log(value); + ColSum[col]+=logvalue; + RowSum[row]+=logvalue; + ColCount[col]++; + RowCount[row]++; + } + } + } + + /* Make sure we dont't have division by zero errors */ + for(row=rowbase; row<=lp->rows; row++) + if(RowCount[row]==0) + RowCount[row]=1; + for(col=1; col<=colMax; col++) + if(ColCount[col]==0) + ColCount[col]=1; + + /* Initialize to RowScale = RowCount-1 RowSum + ColScale = 0.0 + residual = ColSum - ET RowCount-1 RowSum */ + + StopTolerance= my_max(lp->scalelimit-floor(lp->scalelimit), DEF_SCALINGEPS); + StopTolerance*= (REAL) get_nonzeros(lp); + for(row=rowbase; row<=lp->rows; row++) { + FRowScale[row]=RowSum[row] / (REAL) RowCount[row]; + RowScalem2[row]=FRowScale[row]; + } + + /* Compute initial residual */ + for(col=1; col<=colMax; col++) { + FColScale[col]=0; + ColScalem2[col]=0; + residual_even[col]=ColSum[col]; + for(ent=mat->col_end[col-1]; entcol_end[col]; ent++) { + row=mat->col_mat[ent].row_nr; + if(rowStopTolerance) { + /* Given the values of residual and sk, construct + ColScale (when pass is even) + RowScale (when pass is odd) */ + + qkqkm1 = qk * qkm1; + ekekm1 = ek * ekm1; + if((Result % 2) == 0) { /* pass is even; construct RowScale[pass+1] */ + if(Result != 0) { + for(row=rowbase; row<=lp->rows; row++) + RowScalem2[row]=FRowScale[row]; + if(qkqkm1 != 0) { + for(row=rowbase; row<=lp->rows; row++) + FRowScale[row]*=(1 + ekekm1 / qkqkm1); + for(row=rowbase; row<=lp->rows; row++) + FRowScale[row]+=(residual_odd[row] / (qkqkm1 * (REAL) RowCount[row]) - + RowScalem2[row] * ekekm1 / qkqkm1); + } + } + } + else { /* pass is odd; construct ColScale[pass+1] */ + for(col=1; col<=colMax; col++) + ColScalem2[col]=FColScale[col]; + if(qkqkm1 != 0) { + for(col=1; col<=colMax; col++) + FColScale[col]*=(1 + ekekm1 / qkqkm1); + for(col=1; col<=colMax; col++) + FColScale[col]+=(residual_even[col] / ((REAL) ColCount[col] * qkqkm1) - + ColScalem2[col] * ekekm1 / qkqkm1); + } + } + + /* update residual and sk (pass + 1) */ + if((Result % 2) == 0) { /* even */ + /* residual */ + for(row=rowbase; row<=lp->rows; row++) + residual_odd[row]*=ek; + for(col=1; col<=colMax; col++) + for(ent=mat->col_end[col-1]; entcol_end[col]; ent++) { + row=mat->col_mat[ent].row_nr; + if(rowrows; row++) + residual_odd[row]*=(-1 / qk); + + /* sk */ + skm1=sk; + sk=0; + for(row=rowbase; row<=lp->rows; row++) + sk+=(residual_odd[row]*residual_odd[row]) / (REAL) RowCount[row]; + } + else { /* odd */ + /* residual */ + for(col=1; col<=colMax; col++) + residual_even[col]*=ek; + for(col=1; col<=colMax; col++) + for(ent=mat->col_end[col-1]; entcol_end[col]; ent++) { + row=mat->col_mat[ent].row_nr; + if(rowrows; row++) + FRowScale[row]*=(1.0 + ekekm1 / qkm1); + for(row=rowbase; row<=lp->rows; row++) + FRowScale[row]+=(residual_odd[row] / (qkm1 * (REAL) RowCount[row]) - + RowScalem2[row] * ekekm1 / qkm1); + } + else { /* pass is odd, compute ColScale */ + for(col=1; col<=colMax; col++) + FColScale[col]*=(1 + ekekm1 / qkm1); + for(col=1; col<=colMax; col++) + FColScale[col]+=(residual_even[col] / ((REAL) ColCount[col] * qkm1) - + ColScalem2[col] * ekekm1 / qkm1); + } + + /* Do validation, if indicated */ + if(FALSE && mat_validate(mat)){ + double check, error; + + /* CHECK: M RowScale + E ColScale = RowSum */ + error = 0; + for(row = rowbase; row <= lp->rows; row++) { + check = (REAL) RowCount[row] * FRowScale[row]; + if(rowbase == 0) + ent = 0; + else + ent = mat->row_end[row-1]; + for(; ent < mat->row_end[row]; ent++) { + col = ROW_MAT_COL(mat->row_mat[ent]); + check += FColScale[col]; + } + check -= RowSum[row]; + error += check*check; + } + + /* CHECK: E^T RowScale + N ColScale = ColSum */ + error = 0; + for(col = 1; col <= colMax; col++) { + check=(REAL) ColCount[col] * FColScale[col]; + for(ent = mat->col_end[col-1]; ent < mat->col_end[col]; ent++) { + row = mat->col_mat[ent].row_nr; + if(row < rowbase) continue; + check += FRowScale[row]; + } + check -= ColSum[col]; + error += check*check; + } + } + + /* Convert to scaling factors (rounding to nearest power + of 2 can optionally be done as a separate step later) */ + for(col=1; col<=colMax; col++) { + value=exp(-FColScale[col]); + if(valueMAX_SCALAR) value=MAX_SCALAR; + if(!is_int(lp,col) || is_integerscaling(lp)) + FColScale[col]=value; + else + FColScale[col]=1; + } + for(row=rowbase; row<=lp->rows; row++) { + value=exp(-FRowScale[row]); + if(valueMAX_SCALAR) value=MAX_SCALAR; + FRowScale[row]=value; + } + + /* free temporary memory */ + FREE(RowSum); + FREE(ColSum); + FREE(RowCount); + FREE(ColCount); + FREE(residual_even); + FREE(residual_odd); + FREE(RowScalem2); + FREE(ColScalem2); + + return(Result); + +} + +STATIC MYBOOL scaleCR(lprec *lp) +{ + REAL *scalechange; + int Result; + + if(!lp->scaling_used) { + allocREAL(lp, &lp->scalars, lp->sum_alloc + 1, FALSE); + for(Result = 0; Result <= lp->sum; Result++) + lp->scalars[Result] = 1; + lp->scaling_used = TRUE; + } + + allocREAL(lp, &scalechange, lp->sum + 1, FALSE); + + Result=CurtisReidScales(lp, FALSE, scalechange, &scalechange[lp->rows]); + if(Result>0) { + + /* Do the scaling*/ + if(scale_updaterows(lp, scalechange, TRUE) || + scale_updatecolumns(lp, &scalechange[lp->rows], TRUE)) + lp->scalemode |= SCALE_CURTISREID; + + lp->doInvert = TRUE; + lp->doRebase = TRUE; + lp->doRecompute = TRUE; + } + + FREE(scalechange); + + return((MYBOOL) (Result > 0)); +} + +STATIC MYBOOL transform_for_scale(lprec *lp, REAL *value) +{ + MYBOOL Accept = TRUE; + *value = fabs(*value); +#ifdef Paranoia + if(*value < lp->epsmachine) { + Accept = FALSE; + report(lp, SEVERE, "transform_for_scale: A zero-valued entry was passed\n"); + } + else +#endif + if(is_scalemode(lp, SCALE_LOGARITHMIC)) + *value = log(*value); + else if(is_scalemode(lp, SCALE_QUADRATIC)) + (*value) *= (*value); + return( Accept ); +} + +STATIC void accumulate_for_scale(lprec *lp, REAL *min, REAL *max, REAL value) +{ + if(transform_for_scale(lp, &value)) + if(is_scaletype(lp, SCALE_MEAN)) { + *max += value; + *min += 1; + } + else { + *max = my_max(*max, value); + *min = my_min(*min, value); + } +} + +STATIC REAL minmax_to_scale(lprec *lp, REAL min, REAL max) +{ + REAL scale; + + /* Initialize according to transformation / weighting model */ + if(is_scalemode(lp, SCALE_LOGARITHMIC)) + scale = 0; + else + scale = 1; + + /* Compute base scalar according to chosen scaling type */ + if(is_scaletype(lp, SCALE_MEAN)) { + if(min > 0) + scale = max / min; + } + else if(is_scaletype(lp, SCALE_RANGE)) + scale = (max + min) / 2; + else if(is_scaletype(lp, SCALE_GEOMETRIC)) + scale = sqrt(min*max); + else if(is_scaletype(lp, SCALE_EXTREME)) + scale = max; + + /* Compute final scalar according to transformation / weighting model */ + if(is_scalemode(lp, SCALE_LOGARITHMIC)) + scale = exp(-scale); + else if(is_scalemode(lp, SCALE_QUADRATIC)) { + if(scale == 0) + scale = 1; + else + scale = 1 / sqrt(scale); + } + else { + if(scale == 0) + scale = 1; + else + scale = 1 / scale; + } + + /* Make sure we are within acceptable scaling ranges */ + scale = my_max(scale, MIN_SCALAR); + scale = my_min(scale, MAX_SCALAR); + + return(scale); +} + +STATIC REAL roundPower2(REAL scale) +/* Purpose is to round a number to it nearest power of 2; in a system + with binary number representation, this avoids rounding errors when + scale is used to normalize another value */ +{ + long int power2; + MYBOOL isSmall = FALSE; + + if(scale == 1) + return( scale ); + + /* Obtain the fractional power of 2 */ + if(scale < 2) { + scale = 2 / scale; + isSmall = TRUE; + } + else + scale /= 2; + scale = log(scale)/log(2.0); + + /* Find the desired nearest power of two and compute the associated scalar */ + power2 = (long) ceil(scale-0.5); + scale = 1 << power2; + if(isSmall) + scale = 1.0 / scale; + + return( scale ); + +} + +STATIC MYBOOL scale_updatecolumns(lprec *lp, REAL *scalechange, MYBOOL updateonly) +{ + int i, j; + + /* Verify that the scale change is significant (different from the unit) */ + for(i = lp->columns; i > 0; i--) + if(fabs(scalechange[i]-1) > lp->epsprimal) + break; + if(i <= 0) + return( FALSE ); + + /* Update the pre-existing column scalar */ + if(updateonly) + for(i = 1, j = lp->rows + 1; j <= lp->sum; i++, j++) + lp->scalars[j] *= scalechange[i]; + else + for(i = 1, j = lp->rows + 1; j <= lp->sum; i++, j++) + lp->scalars[j] = scalechange[i]; + + return( TRUE ); +} + +STATIC MYBOOL scale_updaterows(lprec *lp, REAL *scalechange, MYBOOL updateonly) +{ + int i; + + /* Verify that the scale change is significant (different from the unit) */ + for(i = lp->rows; i >= 0; i--) { + if(fabs(scalechange[i]-1) > lp->epsprimal) + break; + } + if(i < 0) + return( FALSE ); + + /* Update the pre-existing row scalar */ + if(updateonly) + for(i = 0; i <= lp->rows; i++) + lp->scalars[i] *= scalechange[i]; + else + for(i = 0; i <= lp->rows; i++) + lp->scalars[i] = scalechange[i]; + + return( TRUE ); +} + +STATIC MYBOOL scale_columns(lprec *lp) +{ + int i,j, colMax, nz; + MATitem *matitem; + REAL *scalechange = &lp->scalars[lp->rows]; + + colMax = lp->columns; + + /* Scale matrix entries (including any Lagrangean constraints) */ + mat_validate(lp->matA); + nz = get_nonzeros(lp); + for(i = 0, matitem = lp->matA->col_mat; i < nz; i++, matitem++) + (*matitem).value *= scalechange[(*matitem).col_nr]; + + /* Scale variable bounds as well */ + for(i = 1, j = lp->rows + 1; j <= lp->sum; i++, j++) { /* was <; changed by PN */ + if(lp->orig_lowbo[j] > -lp->infinite) + lp->orig_lowbo[j] /= scalechange[i]; + if(lp->orig_upbo[j] < lp->infinite) + lp->orig_upbo[j] /= scalechange[i]; + if(lp->var_is_sc[i] != 0) + lp->var_is_sc[i] /= scalechange[i]; + } + + lp->columns_scaled = TRUE; + lp->doInvert = TRUE; + lp->doRebase = TRUE; + lp->doRecompute = TRUE; + + return( TRUE ); +} + +STATIC MYBOOL scale_rows(lprec *lp) +{ + int i, nz, colMax; + MATitem *matitem; + REAL *scalechange = lp->scalars; + + colMax = lp->columns; + + /* First row-scale the matrix (including the objective function) */ + nz = get_nonzeros(lp); + for(i = 0, matitem = lp->matA->col_mat; i < nz; i++, matitem++) + (*matitem).value *= scalechange[(*matitem).row_nr]; + + /* ...and scale the rhs and the row bounds (RANGES in MPS!!) */ + for(i = 0; i <= lp->rows; i++) { + if(fabs(lp->orig_rh[i]) < lp->infinite) + lp->orig_rh[i] *= scalechange[i]; + + if(lp->orig_upbo[i] < lp->infinite) /* This is the range */ + lp->orig_upbo[i] *= scalechange[i]; + + if((lp->orig_lowbo[i] != 0) && (fabs(lp->orig_lowbo[i]) < lp->infinite)) + lp->orig_lowbo[i] *= scalechange[i]; + } + + lp->doInvert = TRUE; + lp->doRebase = TRUE; + lp->doRecompute = TRUE; + + return( TRUE ); +} + +STATIC REAL scale(lprec *lp) +{ + int i, j, row_nr, row_count, colMax; + REAL *row_max, *row_min, *scalechange, absval; + REAL col_max, col_min; + MYBOOL rowscaled, colscaled; + MATrec *mat = lp->matA; + + colMax = lp->columns; + + if(is_scaletype(lp, SCALE_NONE)) + return(0.0); + + if(!lp->scaling_used) { + allocREAL(lp, &lp->scalars, lp->sum_alloc + 1, FALSE); + for(i = 0; i <= lp->sum; i++) + lp->scalars[i] = 1; + lp->scaling_used = TRUE; + } + allocREAL(lp, &scalechange, lp->sum + 1, FALSE); + + /* Must initialize due to computation of scaling statistic - KE */ + for(i = 0; i <= lp->sum; i++) + scalechange[i] = 1; + + row_count = lp->rows; + allocREAL(lp, &row_max, row_count + 1, TRUE); + allocREAL(lp, &row_min, row_count + 1, FALSE); + + /* Initialise min and max values of rows */ + for(i = 0; i <= row_count; i++) { + if(is_scaletype(lp, SCALE_MEAN)) + row_min[i] = 0; /* Carries the count of elements */ + else + row_min[i] = lp->infinite; /* Carries the minimum element */ + } + + /* Calculate row scaling data */ + for(j = 1; j <= colMax; j++) { + for(i = mat->col_end[j - 1]; i < mat->col_end[j]; i++) { + row_nr = mat->col_mat[i].row_nr; +#ifdef NoRowScaleOF + if(row_nr == 0) + continue; +#endif + absval = mat->col_mat[i].value; + absval = scaled_mat(lp, absval, row_nr, j); + accumulate_for_scale(lp, &row_min[row_nr], &row_max[row_nr], absval); + } + } + + /* calculate scale factors for rows */ + i = 0; +#ifdef NoRowScaleOF + i++; +#endif + for(; i <= lp->rows; i++) { + scalechange[i] = minmax_to_scale(lp, row_min[i], row_max[i]); + } + + FREE(row_max); + FREE(row_min); + + /* Row-scale the matrix (including the objective function and Lagrangean constraints) */ + rowscaled = scale_updaterows(lp, scalechange, TRUE); + + /* Calculate column scales */ + i = 1; + for(j = 1; j <= colMax; j++) { + if(is_int(lp,j) && !is_integerscaling(lp)) { /* do not scale integer columns */ + scalechange[lp->rows + j] = 1; + } + else { + col_max = 0; + if(is_scaletype(lp, SCALE_MEAN)) + col_min = 0; + else + col_min = lp->infinite; + + for(i = mat->col_end[j - 1]; i < mat->col_end[j]; i++) { + absval = mat->col_mat[i].value; + absval = scaled_mat(lp, absval, mat->col_mat[i].row_nr, j); + accumulate_for_scale(lp, &col_min, &col_max, absval); + } + scalechange[lp->rows + j] = minmax_to_scale(lp, col_min, col_max); + } + } + + /* ... and then column-scale the already row-scaled matrix */ + colscaled = scale_updatecolumns(lp, &scalechange[lp->rows], TRUE); + + /* Create a geometric mean-type measure of the extent of scaling performed; */ + /* ideally, upon successive calls to scale() the value should converge to 0 */ + if(rowscaled || colscaled) { + col_max = 0; + for(j = 1; j <= colMax; j++) + col_max += log(scalechange[lp->rows + j]); + col_max = exp(col_max/lp->columns); + + i = 0; +#ifdef NoRowScaleOF + i++; +#endif + col_min = 0; + for(; i <= lp->rows; i++) + col_min += log(scalechange[i]); + col_min = exp(col_min/row_count); + } + else { + col_max = 1; + col_min = 1; + } + + FREE(scalechange); + + return(1 - sqrt(col_max*col_min)); +} + +STATIC MYBOOL finalize_scaling(lprec *lp) +{ + int i; + + /* Check if we should equilibrate */ + if(is_scalemode(lp, SCALE_EQUILIBRATE) && !is_scaletype(lp, SCALE_CURTISREID)) { + int oldmode; + + oldmode = lp->scalemode; + lp->scalemode = SCALE_LINEAR + SCALE_EXTREME; + scale(lp); + lp->scalemode = oldmode; + } + + /* Check if we should prevent rounding errors */ + if(is_scalemode(lp, SCALE_POWER2)) { + + for(i = 0; i <= lp->sum; i++) + lp->scalars[i] = roundPower2(lp->scalars[i]); + } + + /* Then transfer the scalars to the model's data */ + return( scale_rows(lp) && scale_columns(lp) ); + +} + +STATIC REAL lp_solve_auto_scale(lprec *lp) +{ + REAL scalingmetric = 0; + int n = 1; + + if(lp->scaling_used) + return( scalingmetric); + + if(lp->scalemode != SCALE_NONE) { + if(is_scaletype(lp, SCALE_CURTISREID)) { + scalingmetric = scaleCR(lp); + } + else { + REAL scalinglimit, scalingdelta; + int count; + + /* Integer value of scalelimit holds the maximum number of iterations; default to 1 */ + count = (int) floor(lp->scalelimit); + scalinglimit = lp->scalelimit; + if((count == 0) || (scalinglimit == 0)) { + if(scalinglimit > 0) + count = DEF_SCALINGLIMIT; /* A non-zero convergence has been given, default to max 5 iterations */ + else + count = 1; + } + else + scalinglimit -= count; + + /* Scale to desired relative convergence or iteration limit */ + n = 0; + scalingdelta = 1.0; + scalingmetric = 1.0; + while((n < count) && (fabs(scalingdelta) > scalinglimit)) { + n++; + scalingdelta = scale(lp); + scalingmetric = scalingmetric*(1+scalingdelta); + } + scalingmetric -= 1; + } + } + + /* Check if we really have to do scaling */ + if(lp->scaling_used && (fabs(scalingmetric) >= lp->epsprimal)) + /* Ok, do it */ + finalize_scaling(lp); + + else { + + /* Otherwise reset scaling variables */ + if(lp->scalars != NULL) { + FREE(lp->scalars); + } + lp->scaling_used = FALSE; + lp->columns_scaled = FALSE; + } + + return(scalingmetric); +} + +STATIC void unscale_columns(lprec *lp) +{ + int i, j, colMax; + MATrec *mat = lp->matA; + + if(!lp->columns_scaled) + return; + + colMax = lp->columns; + + /* unscale mat */ + for(j = 1; j <= colMax; j++) { + for(i = mat->col_end[j - 1]; i < mat->col_end[j]; i++) + mat->col_mat[i].value = unscaled_mat(lp, mat->col_mat[i].value, mat->col_mat[i].row_nr, j); + } + + /* unscale bounds as well */ + for(i = lp->rows + 1, j = 1; i <= lp->sum; i++, j++) { + lp->orig_lowbo[i] = unscaled_value(lp, lp->orig_lowbo[i], i); + lp->orig_upbo[i] = unscaled_value(lp, lp->orig_upbo[i], i); + lp->var_is_sc[j] = unscaled_value(lp, lp->var_is_sc[j], i); + } + + for(i = lp->rows + 1; i<= lp->sum; i++) + lp->scalars[i] = 1; + + lp->columns_scaled = FALSE; + + lp->doInvert = TRUE; + lp->doRebase = TRUE; + lp->doRecompute = TRUE; +} + +void undoscale(lprec *lp) +{ + int i, j, colMax; + MATrec *mat = lp->matA; + + colMax = lp->columns; + + if(lp->scaling_used) { + /* unscale the matrix */ + for(j = 1; j <= colMax; j++) { + for(i = mat->col_end[j - 1]; i < mat->col_end[j]; i++) + mat->col_mat[i].value = unscaled_mat(lp, mat->col_mat[i].value, mat->col_mat[i].row_nr, j); + } + + /* unscale variable bounds */ + for(i = lp->rows + 1, j = 1; i <= lp->sum; i++, j++) { + lp->orig_lowbo[i] = unscaled_value(lp, lp->orig_lowbo[i], i); + lp->orig_upbo[i] = unscaled_value(lp, lp->orig_upbo[i], i); + lp->var_is_sc[j] = unscaled_value(lp, lp->var_is_sc[j], i); + } + + /* unscale the rhs, upper and lower bounds... */ + for(i = 0; i <= lp->rows; i++) { + lp->orig_rh[i] = unscaled_value(lp, lp->orig_rh[i], i); + lp->orig_lowbo[i] = unscaled_value(lp, lp->orig_lowbo[i], i); + lp->orig_upbo[i] = unscaled_value(lp, lp->orig_upbo[i], i); + } + + FREE(lp->scalars); + lp->scaling_used = FALSE; + lp->columns_scaled = FALSE; + + lp->doInvert = TRUE; + lp->doRebase = TRUE; + lp->doRecompute = TRUE; + } +} + Index: src/tools/solver/lp_solve/lp_scale.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_scale.h diff -N src/tools/solver/lp_solve/lp_scale.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_scale.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,26 @@ +#ifndef HEADER_lp_scale +#define HEADER_lp_scale + +#include "lp_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Put function headers here */ +STATIC MYBOOL scale_updatecolumns(lprec *lp, REAL *scalechange, MYBOOL updateonly); +STATIC MYBOOL scale_updaterows(lprec *lp, REAL *scalechange, MYBOOL updateonly); +STATIC MYBOOL scale_rows(lprec *lp); +STATIC MYBOOL scale_columns(lprec *lp); +STATIC void unscale_columns(lprec *lp); +STATIC REAL scale(lprec *lp); +STATIC MYBOOL scaleCR(lprec *lp); +STATIC MYBOOL finalize_scaling(lprec *lp); +STATIC REAL lp_solve_auto_scale(lprec *lp); +void undoscale(lprec *lp); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_scale */ Index: src/tools/solver/lp_solve/lp_simplex.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_simplex.c diff -N src/tools/solver/lp_solve/lp_simplex.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_simplex.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1509 @@ + +/* + Core optimization drivers for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Michel Berkelaar (to lp_solve v3.2), + Kjell Eikland + Contact: + License terms: LGPL. + + Requires: lp_lib.h, lp_simplex.h, lp_presolve.h, lp_pricerPSE.h + + Release notes: + v5.0.0 1 January 2004 New unit applying stacked basis and bounds storage. + v5.0.1 31 January 2004 Moved B&B routines to separate file and implemented + a new runsolver() general purpose call method. + v5.0.2 1 May 2004 Changed routine names to be more intuitive. + + ---------------------------------------------------------------------------------- +*/ + +#include +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_simplex.h" +#include "lp_crash.h" +#include "lp_presolve.h" +#include "lp_price.h" +#include "lp_pricePSE.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +STATIC int primloop(lprec *lp, MYBOOL feasible) +{ + int i, j, k, ok = TRUE; + LREAL theta = 0.0; + REAL *drow = NULL, *prow = NULL, *pcol = NULL, prevobj, epsvalue; + MYBOOL primal = TRUE, minit; + MYBOOL pivdynamic; + int oldpivrule, oldpivmode, pivrule, Blandswitches, + colnr, rownr, lastnr, minitcount = 0; + int Rcycle = 0, Ccycle = 0, Ncycle = 0, changedphase = FALSE; + int *nzdrow = NULL; + + /* Add sufficent number of artificial variables to make the problem feasible + through the first phase; delete when primal feasibility has been achieved */ + lp->Extrap = 0; +#ifdef EnablePrimalPhase1 + if(!feasible) { +#ifdef Paranoia + if(!verifyBasis(lp)) + report(lp, SEVERE, "primloop: No valid basis for artificial variables\n"); +#endif +#if 0 + /* First check if we can get away with a single artificial variable */ + if(lp->equalities == 0) { + i = (int) feasibilityOffset(lp, !primal); + add_artificial(lp, i); + } + else +#endif + /* Otherwise add as many as is necessary to force basic feasibility */ + for(i = 1; i <= lp->rows; i++) + add_artificial(lp, i); + } + if(lp->spx_trace) + report(lp, DETAILED, "Extrap count = %d\n", lp->Extrap); +#endif + + if(lp->spx_trace) + report(lp, DETAILED, "Entered primal simplex algorithm with feasibility %s\n", + my_boolstr(feasible)); + + /* Create work arrays */ +#ifdef UseSparseReducedCost + allocINT(lp, &nzdrow, lp->sum + 1, FALSE); +#endif + allocREAL(lp, &drow, lp->sum + 1, TRUE); + allocREAL(lp, &prow, lp->sum + 1, TRUE); + allocREAL(lp, &pcol, lp->rows + 1, TRUE); + + /* Refactorize the basis and set status variables */ + i = my_if(is_bb_action(lp, ACTION_REBASE), INITSOL_SHIFTZERO, INITSOL_USEZERO); + if((lp->spx_status == SWITCH_TO_PRIMAL) || (lp->Extrap != 0) || + is_bb_action(lp, ACTION_REINVERT)) { + simplexPricer(lp, (MYBOOL)!primal); + + /* Do basis crashing before refactorization, if specified */ + invert(lp, (MYBOOL) i); + } + else { + if(is_bb_action(lp, ACTION_RECOMPUTE)) + recompute_solution(lp, (MYBOOL) i); + restartPricer(lp, (MYBOOL)!primal); + } + lp->bb_action = ACTION_NONE; + + lp->spx_status = RUNNING; + lp->doIterate = FALSE; + minit = ITERATE_MAJORMAJOR; + prevobj = lp->rhs[0]; + oldpivmode = lp->piv_strategy; + oldpivrule = get_piv_rule(lp); + pivdynamic = ANTICYCLEBLAND && is_piv_mode(lp, PRICE_ADAPTIVE); + epsvalue = lp->epspivot; + Blandswitches = 0; + rownr = 0; + colnr = 0; + lastnr = 0; + lp->rejectpivot[0] = 0; + if(feasible) + lp->simplex_mode = SIMPLEX_Phase2_PRIMAL; + else + lp->simplex_mode = SIMPLEX_Phase1_PRIMAL; + + /* Iterate while we are successful; exit when the model is infeasible/unbounded, + or we must terminate due to numeric instability or user-determined reasons */ + while(lp->spx_status == RUNNING) { + + if(lp->spx_trace) + if(lastnr > 0) + report(lp, NORMAL, "primloop: Objective at iteration %7d is " MPSVALUEMASK " (%4d: %4d %s- %4d)\n", + get_total_iter(lp), lp->rhs[0], rownr, lastnr, + my_if(minit == ITERATE_MAJORMAJOR, "<","|"), colnr); + + pivrule = get_piv_rule(lp); + if(pivdynamic && ((pivrule != PRICER_FIRSTINDEX) || + (pivrule != oldpivrule)) +#if PrimalPivotStickiness==2 + && (lp->fixedvars == 0) +#elif PrimalPivotStickiness==1 + && feasible +#endif + ) { + /* Check if we have a stationary solution */ + if((minit == ITERATE_MAJORMAJOR) && !lp->justInverted && + (fabs(my_reldiff(lp->rhs[0], prevobj)) < epsvalue)) { + Ncycle++; + /* Start to monitor for variable cycling if this is the initial stationarity */ + if(Ncycle <= 1) { + Ccycle = colnr; + Rcycle = rownr; + } + /* Check if we should change pivoting strategy due to stationary variable cycling */ + else if((pivrule == oldpivrule) && (((MAX_STALLCOUNT > 1) && (Ncycle > MAX_STALLCOUNT)) || + (Ccycle == rownr) || (Rcycle == colnr))) { + /* First check if we should give up on Bland's rule and try perturbed bound + relaxation instead */ +#ifdef EnableStallAntiDegen + if((MAX_BLANDSWITCH >= 0) && (Blandswitches >= MAX_BLANDSWITCH)) { + lp->spx_status = DEGENERATE; + break; + } +#endif + Blandswitches++; + lp->piv_strategy = PRICER_FIRSTINDEX; /* There is no advanced normalization for Bland's rule, restart at end */ + Ccycle = 0; + Rcycle = 0; + Ncycle = 0; + if(lp->spx_trace) + report(lp, DETAILED, "primloop: Detected cycling at iteration %d; changed to FIRST INDEX rule!\n", + get_total_iter(lp)); + } + } +#if 0 + /* Handle cycling or stationary situations by switching to the dual simplex */ + else if((pivrule == oldpivrule) && feasible && (lp->simplex_strategy & SIMPLEX_DYNAMIC)) { + lp->spx_status = SWITCH_TO_DUAL; + if(lp->total_iter == 0) + report(lp, NORMAL, "Start dual simplex for finalization at iteration %7d\n", + get_total_iter(lp)); + break; + } +#endif + /* Change back to original selection strategy as soon as possible */ + else if((minit == ITERATE_MAJORMAJOR) && (pivrule != oldpivrule)) { + lp->piv_strategy = oldpivmode; + restartPricer(lp, AUTOMATIC); /* Pricer restart following Bland's rule */ + Ccycle = 0; + Rcycle = 0; + Ncycle = 0; + if(lp->spx_trace) + report(lp, DETAILED, "...returned to original pivot selection rule at iteration %d.\n", + get_total_iter(lp)); + } + } + + /* Store current LP value for reference at next iteration */ + prevobj = lp->rhs[0]; + + lp->doIterate = FALSE; + lp->doInvert = FALSE; + + /* Find best column to enter the basis */ +RetryCol: + if(!changedphase) { + i = 0; + do { + if(partial_countBlocks(lp, (MYBOOL)!primal) > 1) + partial_blockStep(lp, (MYBOOL)!primal); + colnr = colprim(lp, (MYBOOL) (minit == ITERATE_MINORRETRY), drow, nzdrow); + i++; + } while ((colnr == 0) && (i < partial_countBlocks(lp, (MYBOOL)!primal))); + +#ifdef FinalOptimalErrorLimitation + /* Do additional checking that we have a correct identification of optimality */ + if((colnr == 0) && !lp->justInverted) { + lp->doInvert = TRUE; + i = invert(lp, INITSOL_USEZERO); + colnr = colprim(lp, FALSE, drow, nzdrow, NULL); + } +#endif + } + + if(colnr > 0) { + changedphase = FALSE; + fsolve(lp, colnr, pcol, lp->workrowsINT, lp->epsmachine, 1.0, TRUE); /* Solve entering column for Pi */ +#ifdef UseRejectionList + if(is_anti_degen(lp, ANTIDEGEN_COLUMNCHECK) && !check_degeneracy(lp, pcol, NULL)) { + if(lp->rejectpivot[0] < DEF_MAXPIVOTRETRY/3) { + i = ++lp->rejectpivot[0]; + lp->rejectpivot[i] = colnr; + report(lp, DETAILED, "Entering column %d found to be non-improving due to degeneracy!\n", + colnr); + goto RetryCol; + } + else { + lp->rejectpivot[0] = 0; + report(lp, DETAILED, "Gave up trying to find a strictly improving entering column!\n"); + } + } +#endif + + /* Find the leaving variable that gives the most stringent bound on the entering variable */ + rownr = rowprim(lp, colnr, &theta, pcol); +#if 0 + report(lp, NORMAL, "Iteration %d: Enter %d, Leave %d\n", lp->current_iter, colnr, rownr); +#endif + + /* See if we can do a straight artificial<->slack replacement (when "colnr" is a slack) */ + if((lp->Extrap != 0) && (rownr == 0) && (colnr <= lp->rows)) + rownr = findAnti_artificial(lp, colnr); + + if(rownr > 0) { + lp->rejectpivot[0] = 0; + lp->bfp_prepareupdate(lp, rownr, colnr, pcol); + } + else if(lp->spx_status == UNBOUNDED) { + report(lp, DETAILED, "primloop: The model is primal unbounded.\n"); + break; + } +#ifdef UseRejectionList + else if(lp->rejectpivot[0] < DEF_MAXPIVOTRETRY) { + lp->spx_status = RUNNING; + if(lp->justInverted) { + lp->rejectpivot[0]++; + lp->rejectpivot[lp->rejectpivot[0]] = colnr; + report(lp, DETAILED, "...trying to recover via another pivot column!\n"); + } + else { + lp->doInvert = TRUE; + invert(lp, INITSOL_USEZERO); + } + goto RetryCol; + } +#endif + else { + + /* Assume failure if we are still unsuccessful and the model is not unbounded */ + if((rownr == 0) && (lp->spx_status == RUNNING)) { + report(lp, IMPORTANT, "primloop: Could not find a leaving variable for entering %d (iteration %d)\n", + colnr, get_total_iter(lp)); + lp->spx_status = NUMFAILURE; + } + } + } +#ifdef EnablePrimalPhase1 + else if(!feasible || isPhase1(lp)) { + + if(feasiblePhase1(lp, epsvalue)) { + lp->spx_status = RUNNING; + if(lp->bb_totalnodes == 0) { + report(lp, NORMAL, "Found feasibility by primal simplex at iteration %7d\n", + get_total_iter(lp)); + if((lp->usermessage != NULL) && (lp->msgmask & MSG_LPFEASIBLE)) + lp->usermessage(lp, lp->msghandle, MSG_LPFEASIBLE); + } + changedphase = FALSE; + feasible = TRUE; + lp->simplex_mode = SIMPLEX_Phase2_PRIMAL; + + /* We can do two things now; + 1) delete the rows belonging to those variables, since they are redundant, OR + 2) drive out the existing artificial variables via pivoting. */ + if(lp->Extrap > 0) { + +#ifdef Phase1EliminateRedundant + /* If it is not a MIP model we can try to delete redundant rows */ + if((lp->bb_totalnodes == 0) && (MIP_count(lp) == 0)) { + while(lp->Extrap > 0) { + i = lp->rows; + while((i > 0) && (lp->var_basic[i] <= lp->sum-lp->Extrap)) + i--; +#ifdef Paranoia + if(i <= 0) { + report(lp, SEVERE, "primloop: Could not find redundant artificial.\n"); + break; + } +#endif + /* Obtain column and row indeces */ + j = lp->var_basic[i]-lp->rows; + k = get_artificialRow(lp, j); + + /* Delete row before column due to basis "compensation logic" */ + if(lp->is_basic[k]) { + lp->is_basic[lp->rows+j] = FALSE; + del_constraint(lp, k); + } + else + setBasisVar(lp, i, k); + del_column(lp, j); + lp->Extrap--; + } + lp->basis_valid = TRUE; + } + /* Otherwise we drive out the artificials by elimination pivoting */ + else { + eliminate_artificials(lp, prow); + lp->doIterate = FALSE; + } +#else + lp->Extrap = my_flipsign(lp->Extrap); +#endif + } + lp->doInvert = TRUE; + prevobj = lp->infinite; + } + else { + lp->spx_status = INFEASIBLE; + minit = ITERATE_MAJORMAJOR; + if(lp->spx_trace) + report(lp, NORMAL, "Found infeasibility in primal simplex at iteration %7d\n", + get_total_iter(lp)); + } + } +#endif + + /* Pivot row/col and update the inverse */ + if(lp->doIterate) { + lastnr = lp->var_basic[rownr]; + + if(lp->justInverted) + minitcount = 0; + else if(minitcount > MAX_MINITUPDATES) + recompute_solution(lp, INITSOL_USEZERO); + minit = performiteration(lp, rownr, colnr, theta, primal, NULL, NULL); + if(minit == ITERATE_MINORRETRY) + minitcount++; + + if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) + break; +#ifdef UsePrimalReducedCostUpdate + /* Do a fast update of the reduced costs in preparation for the next iteration */ + if(minit == ITERATE_MAJORMAJOR) + update_reducedcosts(lp, primal, lastnr, colnr, pcol, drow); +#endif + +#ifdef EnablePrimalPhase1 + /* Detect if an auxiliary variable has left the basis and delete it; if + the non-basic variable only changed bound (a "minor iteration"), the + basic artificial variable did not leave and there is nothing to do */ + if((minit == ITERATE_MAJORMAJOR) && (lastnr > lp->sum - abs(lp->Extrap))) { +#ifdef Paranoia + if(lp->is_basic[lastnr] || !lp->is_basic[colnr]) + report(lp, SEVERE, "primloop: Invalid basis indicator for variable %d at iteration %d\n", + lastnr, get_total_iter(lp)); +#endif + del_column(lp, lastnr-lp->rows); + if(lp->Extrap > 0) + lp->Extrap--; + else + lp->Extrap++; + if(lp->Extrap == 0) { + colnr = 0; + prevobj = lp->infinite; + changedphase = TRUE; + } + } + +#endif + } + + if(lp->spx_status == SWITCH_TO_DUAL) + ; + else if(!changedphase && lp->bfp_mustrefactorize(lp)) { + i = invert(lp, INITSOL_USEZERO); +#ifdef ResetMinitOnReinvert + minit = ITERATE_MAJORMAJOR; +#endif + + if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) + break; + else if(!i) { + lp->spx_status = SINGULAR_BASIS; + break; + } + /* Check whether we are still feasible or not... */ + if(!isPrimalFeasible(lp, lp->epspivot)) { + lp->spx_status = LOSTFEAS; + } + } + userabort(lp, -1); + + } + if (lp->piv_strategy != oldpivmode) + lp->piv_strategy = oldpivmode; + +#ifdef EnablePrimalPhase1 + + /* Remove any remaining artificial variables (feasible or infeasible model) */ + lp->Extrap = abs(lp->Extrap); + if(lp->Extrap > 0) { + clear_artificials(lp); + if(lp->spx_status != OPTIMAL) + restore_basis(lp); + i = invert(lp, INITSOL_USEZERO); + } +#ifdef Paranoia + if(!verifyBasis(lp)) + report(lp, SEVERE, "primloop: Invalid basis detected due to internal error\n"); +#endif + + /* Switch to dual phase 1 simplex for MIP models during B&B phases */ + if((lp->bb_totalnodes == 0) && (MIP_count(lp) > 0) && + ((lp->simplex_strategy & SIMPLEX_Phase1_DUAL) == 0)) { + lp->simplex_strategy &= !SIMPLEX_Phase1_PRIMAL; + lp->simplex_strategy += SIMPLEX_Phase1_DUAL; + } + +#endif + +#ifdef UseSparseReducedCost + FREE(nzdrow); +#endif + FREE(drow); + FREE(prow); + FREE(pcol); + + return(ok); +} /* primloop */ + +STATIC int dualloop(lprec *lp, MYBOOL feasible) +{ + int i, ok = TRUE; + LREAL theta = 0.0; + REAL *drow = NULL, *prow = NULL, *pcol = NULL, prevobj, epsvalue; + MYBOOL primal = FALSE; + MYBOOL minit, pivdynamic, forceoutEQ = FALSE; + int oldpivrule, oldpivmode, pivrule, Blandswitches, + colnr, rownr, lastnr, minitcount = 0; + int Rcycle = 0, Ccycle = 0, Ncycle = 0, changedphase = TRUE; +#ifdef FixInaccurateDualMinit + int minitcolnr = 0; +#endif +#ifdef UseSparseReducedCost + int *nzprow = NULL; +#endif + + if(lp->spx_trace) + report(lp, DETAILED, "Entering dual simplex algorithm\n"); + + /* Set Extrad value to force dual feasibility; reset when + "local optimality" has been achieved or a dual non-feasibility + has been encountered (no candidate for a first leaving variable) */ + if(feasible) + lp->Extrad = 0; + else + lp->Extrad = feasibilityOffset(lp, (MYBOOL)!primal); + + if(lp->spx_trace) + report(lp, DETAILED, "Extrad = %g\n", (double)lp->Extrad); + + /* Allocate work arrays */ + allocREAL(lp, &drow, lp->sum + 1, TRUE); +#ifdef UseSparseReducedCost + allocINT(lp, &nzprow, lp->sum + 1, FALSE); +#endif + allocREAL(lp, &prow, lp->sum + 1, TRUE); + allocREAL(lp, &pcol, lp->rows + 1, TRUE); + + /* Refactorize the basis and set status variables */ + i = my_if(is_bb_action(lp, ACTION_REBASE), INITSOL_SHIFTZERO, INITSOL_USEZERO); + if((lp->spx_status == SWITCH_TO_DUAL) || (lp->Extrad != 0) || + is_bb_action(lp, ACTION_REINVERT)) { + simplexPricer(lp, (MYBOOL)!primal); + + /* Do basis crashing before refactorization, if specified */ + invert(lp, (MYBOOL) i); + } + else { + if(is_bb_action(lp, ACTION_RECOMPUTE)) + recompute_solution(lp, (MYBOOL) i); + restartPricer(lp, (MYBOOL)!primal); + } + lp->bb_action = ACTION_NONE; + + lp->spx_status = RUNNING; + lp->doIterate = FALSE; + minit = ITERATE_MAJORMAJOR; + prevobj = lp->rhs[0]; + oldpivmode = lp->piv_strategy; + oldpivrule = get_piv_rule(lp); + pivdynamic = ANTICYCLEBLAND && is_piv_mode(lp, PRICE_ADAPTIVE); + epsvalue = lp->epspivot; + Blandswitches = 0; + rownr = 0; + colnr = -1; /* Used to detect infeasibility at the beginning of the dual loop */ + lastnr = 0; + lp->rejectpivot[0] = 0; + if(feasible) + lp->simplex_mode = SIMPLEX_Phase2_DUAL; + else + lp->simplex_mode = SIMPLEX_Phase1_DUAL; + + /* Check if we have equality slacks in the basis and we should try to + drive them out in order to reduce chance of degeneracy in Phase 1 */ + if(!feasible && (lp->fixedvars > 0) && is_anti_degen(lp, ANTIDEGEN_FIXEDVARS)) + forceoutEQ = TRUE; + + while(lp->spx_status == RUNNING) { + + if(lp->spx_trace) + if(lastnr > 0) + report(lp, NORMAL, "dualloop: Objective at iteration %7d is " MPSVALUEMASK " (%4d: %4d %s- %4d)\n", + get_total_iter(lp), lp->rhs[0], rownr, lastnr, + my_if(minit == ITERATE_MAJORMAJOR, "<","|"), colnr); + + pivrule = get_piv_rule(lp); + if(pivdynamic && ((pivrule != PRICER_FIRSTINDEX) || + (pivrule != oldpivrule)) +/* lp_solve has problems with ffff8000.mps if one of these is not activated; problem source unknown */ +#if DualPivotStickiness==2 + /* Stays with pricing rule as long as possible (also preserves accuracy) */ + && (lp->fixedvars == 0) +#elif DualPivotStickiness==1 + /* Stays with pricing rule only if the model is infeasible */ + && feasible +#endif + ) { + /* Check if we have a stationary solution */ + if((minit == ITERATE_MAJORMAJOR) && !lp->justInverted && + (fabs(my_reldiff(lp->rhs[0], prevobj)) < epsvalue)) { + Ncycle++; + /* Start to monitor for variable cycling if this is the initial stationarity */ + if(Ncycle <= 1) { + Ccycle = colnr; + Rcycle = rownr; + } + /* Check if we should change pivoting strategy due to stationary variable cycling */ + else if((pivrule == oldpivrule) && (((MAX_STALLCOUNT > 1) && (Ncycle > MAX_STALLCOUNT)) || + (Ccycle == rownr) || (Rcycle == colnr))) { + /* First check if we should give up on Bland's rule and try perturbed bound + relaxation instead */ +#ifdef EnableStallAntiDegen + if((MAX_BLANDSWITCH >= 0) && (Blandswitches >= MAX_BLANDSWITCH)) { + lp->spx_status = DEGENERATE; + break; + } +#endif + Blandswitches++; + lp->piv_strategy = PRICER_FIRSTINDEX; /* There is no advanced normalization for Bland's rule, restart at end */ + Ccycle = 0; + Rcycle = 0; + Ncycle = 0; + if(lp->spx_trace) + report(lp, DETAILED, "dualloop: Detected cycling at iteration %d; changed to FIRST INDEX rule!\n", + get_total_iter(lp)); + } + } + /* Handle cycling or stationary situations by switching to the primal simplex */ + else if((pivrule == oldpivrule) && feasible && (lp->simplex_strategy & SIMPLEX_DYNAMIC)) { + lp->spx_status = SWITCH_TO_PRIMAL; + break; + } + /* Change back to original selection strategy as soon as possible */ + else if((minit == ITERATE_MAJORMAJOR) && (pivrule != oldpivrule)) { + lp->piv_strategy = oldpivmode; + restartPricer(lp, AUTOMATIC); /* Pricer restart following Bland's rule */ + Ccycle = 0; + Rcycle = 0; + Ncycle = 0; + if(lp->spx_trace) + report(lp, DETAILED, "...returned to original pivot selection rule at iteration %d.\n", + get_total_iter(lp)); + } + } + + /* Store current LP value for reference at next iteration */ + changedphase = FALSE; + prevobj = lp->rhs[0]; + lastnr = lp->var_basic[rownr]; + lp->doInvert = FALSE; + + /* Do minor iterations (non-basic variable bound switches) for as + long as possible since this is a cheap way of iterating */ +#ifdef Phase1DualPriceEqualities +RetryRow: +#endif + if(minit != ITERATE_MINORRETRY) { + /* forceoutEQ + FALSE : Only eliminate assured "good" violated equality constraint slacks + TRUE : Seek more aggressive elimination of equality constraint slacks + (but not as aggressive as the rule used in lp_solve v4.0 and earlier) */ + i = 0; + do { + if(partial_countBlocks(lp, (MYBOOL)!primal) > 1) + partial_blockStep(lp, (MYBOOL)!primal); + rownr = rowdual(lp, forceoutEQ); + i++; + } while ((rownr == 0) && (i < partial_countBlocks(lp, (MYBOOL)!primal))); + +#ifdef FinalOptimalErrorLimitation + /* Do additional checking that we have a correct identification of optimality */ + if((rownr == 0) && !lp->justInverted) { + lp->doInvert = TRUE; + i = invert(lp, INITSOL_USEZERO); + forceoutEQ = TRUE; + rownr = rowdual(lp, forceoutEQ); + } +#endif + lastnr = lp->var_basic[rownr]; + } + + if(rownr > 0) { +#ifdef UseRejectionList +RetryCol: +#endif + lp->doIterate = FALSE; + colnr = coldual(lp, rownr, (MYBOOL)(minit == ITERATE_MINORRETRY), + prow, nzprow, drow, NULL); + if(colnr > 0) { + lp->doIterate = TRUE; + fsolve(lp, colnr, pcol, lp->workrowsINT, lp->epsmachine, 1.0, TRUE); + +#ifdef FixInaccurateDualMinit + /* Prevent bound flip-flops during minor iterations; used to detect + infeasibility after triggering of minor iteration accuracy management */ + if(colnr != minitcolnr) + minitcolnr = 0; +#endif + + /* Getting division by zero here; catch it and try to recover */ + if(pcol[rownr] == 0) { + if(lp->spx_trace) + report(lp, DETAILED, "dualloop: Attempt to divide by zero (pcol[%d])\n", rownr); + lp->doIterate = FALSE; + if(!lp->justInverted) { + report(lp, DETAILED, "...trying to recover by reinverting!\n"); + lp->doInvert = TRUE; + } +#ifdef UseRejectionList + else if(lp->rejectpivot[0] < DEF_MAXPIVOTRETRY) { + lp->rejectpivot[0]++; + lp->rejectpivot[lp->rejectpivot[0]] = colnr; + if(lp->bb_totalnodes == 0) + report(lp, DETAILED, "...trying to recover via another pivot column!\n"); + goto RetryCol; + } +#endif + else { + if(lp->bb_totalnodes == 0) + report(lp, DETAILED, "...cannot recover by reinverting.\n"); + lp->spx_status = NUMFAILURE; + ok = FALSE; + } + } + else { + lp->rejectpivot[0] = 0; + theta = lp->bfp_prepareupdate(lp, rownr, colnr, pcol); + + + /* Verify numeric accuracy of the inverse and change to + the "theoretically" correct version of the theta */ + if((lp->improve & IMPROVE_INVERSE) && + (my_reldiff(fabs(theta),fabs(prow[colnr])) > lp->epspivot)) { + lp->doInvert = TRUE; +#ifdef IncreasePivotOnReducedAccuracy + if(lp->bfp_pivotcount(lp) < 2*DEF_MAXPIVOTRETRY) + lp->epspivot *= 3.0; +#endif + report(lp, DETAILED, "dualloop: Reinvert because of loss of accuracy at iteration %d\n", + get_total_iter(lp)); + } + theta = my_chsign(!lp->is_lower[colnr], prow[colnr]); + + compute_theta(lp, rownr, &theta, 0, primal); + } + } +#ifdef FixInaccurateDualMinit + /* Reinvert and try another row if we did not find a bound-violated leaving column */ + else if((minit != ITERATE_MAJORMAJOR) && (colnr != minitcolnr)) { + minitcolnr = colnr; + lp->doInvert = TRUE; + i = invert(lp, INITSOL_USEZERO); + if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) + break; + else if(!i) { + lp->spx_status = SINGULAR_BASIS; + break; + } + minit = ITERATE_MAJORMAJOR; + continue; + } +#endif + else { + if(lp->justInverted && (lp->simplex_mode == SIMPLEX_Phase2_DUAL)) + lp->spx_status = LOSTFEAS; +#if 1 + else if(!lp->justInverted && (lp->bb_level <= 1)) { +#else + else if(!lp->justInverted) { +#endif + lp->doIterate = FALSE; + lp->doInvert = TRUE; + } + else { + if((lp->spx_trace && (lp->bb_totalnodes == 0)) || + (lp->bb_trace && (lp->bb_totalnodes > 0))) + report(lp, DETAILED, "Model lost dual feasibility\n"); + lp->spx_status = INFEASIBLE; + ok = FALSE; + break; + } + } + } + else { + + /* New code to solve to optimality using the dual, but only if the user + has specified a preference for the dual simplex - KE added 20030731 */ + lp->doIterate = FALSE; + if((lp->Extrad != 0) && (colnr < 0) && !isPrimalFeasible(lp, lp->epsprimal)) { + if(feasible) { + if(lp->bb_totalnodes == 0) + report(lp, DETAILED, "Model is dual infeasible and primal feasible\n"); + lp->spx_status = SWITCH_TO_PRIMAL; + lp->doInvert = (MYBOOL) (lp->Extrad != 0); + lp->Extrad = 0; + } + else { + if(lp->bb_totalnodes == 0) + report(lp, NORMAL, "Model is primal and dual infeasible\n"); + lp->spx_status = INFEASIBLE; + ok = FALSE; + } + break; + } + else if(lp->Extrad == 0) { + + /* We are feasible (and possibly also optimal) */ + feasible = TRUE; + lp->simplex_mode = SIMPLEX_Phase2_DUAL; + + /* Check if we still have equality slacks stuck in the basis; drive them out? */ + if((lp->fixedvars > 0) && lp->bb_totalnodes == 0) +#ifdef Paranoia + report(lp, NORMAL, +#else + report(lp, DETAILED, +#endif + "Found dual solution with %d fixed slack variables left basic\n", + lp->fixedvars); + +#ifdef Phase1DualPriceEqualities + if(!forceoutEQ) { + forceoutEQ = TRUE; + goto RetryRow; + } + colnr = 0; +#else + +#if 1 + /* Problem: Check if we are dual degenerate and need to switch to the + primal simplex (there is a flaw in the dual simplex code) */ + colnr = colprim(lp, FALSE, drow, nzprow); +#else + colnr = 0; +#endif + +#endif + + if(colnr == 0) + lp->spx_status = OPTIMAL; + else { + lp->spx_status = SWITCH_TO_PRIMAL; + if(lp->total_iter == 0) + report(lp, NORMAL, "Use primal simplex for finalization at iteration %7d\n", + get_total_iter(lp)); + } + if((lp->total_iter == 0) && (lp->spx_status == OPTIMAL)) + report(lp, NORMAL, "Optimal solution with dual simplex at iteration %7d\n", + get_total_iter(lp)); + break; + } + else { + + /* We are feasible (and possibly also optimal) */ + feasible = TRUE; + lp->simplex_mode = SIMPLEX_Phase2_DUAL; + + /* Set default action; force an update of the rhs vector, adjusted for + the new Extrad=0 (set here so that usermessage() behaves properly) */ + lp->spx_status = RUNNING; + + if(lp->total_iter == 0) { + report(lp, NORMAL, "Found feasibility by dual simplex at iteration %7d\n", + get_total_iter(lp)); + + if((lp->usermessage != NULL) && (lp->msgmask & MSG_LPFEASIBLE)) + lp->usermessage(lp, lp->msghandle, MSG_LPFEASIBLE); + } + lp->Extrad = 0; + lp->doInvert = TRUE; + changedphase = TRUE; + + /* Override default action, if so selected by the user */ + if((lp->simplex_strategy & SIMPLEX_DUAL_PRIMAL) && (lp->fixedvars == 0)) + lp->spx_status = SWITCH_TO_PRIMAL; + } + } + + if(lp->doIterate) { + i = lp->var_basic[rownr]; + + if(lp->justInverted) + minitcount = 0; + else if(minitcount > MAX_MINITUPDATES) + recompute_solution(lp, INITSOL_USEZERO); + minit = performiteration(lp, rownr, colnr, theta, primal, prow, nzprow); + if(minit == ITERATE_MINORRETRY) + minitcount++; + +#ifdef UseDualReducedCostUpdate + /* Do a fast update of the reduced costs in preparation for the next iteration */ + if(minit == ITERATE_MAJORMAJOR) + update_reducedcosts(lp, primal, i, colnr, prow, drow); +#endif + if((minit == ITERATE_MAJORMAJOR) && is_fixedvar(lp, i)) + lp->fixedvars--; + else if(minit == ITERATE_MINORMAJOR) + ; + if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) + break; + } + + if(lp->spx_status == SWITCH_TO_PRIMAL) { + } + else if(lp->bfp_mustrefactorize(lp)) { + i = invert(lp, INITSOL_USEZERO); +#ifdef ResetMinitOnReinvert + minit = ITERATE_MAJORMAJOR; +#endif + if((lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT)) + break; + else if(!i) { + lp->spx_status = SINGULAR_BASIS; + break; + } + } + userabort(lp, -1); + } + + if (lp->piv_strategy != oldpivmode) + lp->piv_strategy = oldpivmode; + +#ifdef UseSparseReducedCost + FREE(nzprow); +#endif + FREE(drow); + FREE(prow); + FREE(pcol); + + return(ok); +} + +STATIC int spx_run(lprec *lp) +{ + int i, singular_count, lost_feas_count; + MYBOOL feasible, lost_feas, bb_skipinv; + + lp->current_iter = 0; + lp->current_bswap = 0; + if(lp->rowblocks != NULL) + lp->rowblocks->blocknow = 1; + if(lp->colblocks != NULL) + lp->colblocks->blocknow = 1; + lp->spx_status = RUNNING; + lp->bb_status = lp->spx_status; + lp->Extrap = 0; + lp->Extrad = 0; + singular_count = 0; + lost_feas_count = 0; + lost_feas = FALSE; + lp->simplex_mode = SIMPLEX_DYNAMIC; + + /* Reinvert for initialization, if necessary */ + bb_skipinv = is_bb_action(lp, ACTION_ACTIVE); + if(!bb_skipinv && lp->doInvert) { + i = my_if(lp->doRebase, INITSOL_SHIFTZERO, INITSOL_USEZERO); + invert(lp, (MYBOOL) i); + } + feasible = isPrimalFeasible(lp, lp->epspivot); + + /* Compute the number of fixed basic variables */ + lp->fixedvars = 0; + for(i = 1; i <= lp->sum; i++) { + if(lp->is_basic[i] && is_fixedvar(lp, i)) + lp->fixedvars++; + } + + /* Loop for as long as needed */ + while(lp->spx_status == RUNNING) { + + if(userabort(lp, -1)) + break; + + /* Check whether we are feasible or infeasible. */ + feasible &= !bb_skipinv; + bb_skipinv = FALSE; + if(lp->spx_trace) { + if(feasible) + report(lp, NORMAL, "Start at feasible basis\n"); + else if(lost_feas_count > 0) + report(lp, NORMAL, "Continuing at infeasible basis\n"); + else + report(lp, NORMAL, "Start at infeasible basis\n"); + } + + /* Now do the simplex magic */ +#ifdef EnablePrimalPhase1 + if(((lp->simplex_strategy & SIMPLEX_Phase1_DUAL) == 0) || + ((MIP_count(lp) > 0) && (lp->total_iter == 0) && + is_presolve(lp, PRESOLVE_REDUCEMIP))) { +#else + if(feasible && + ((lp->simplex_strategy & SIMPLEX_Phase1_DUAL) == 0)) { +#endif + if(!lost_feas && feasible && ((lp->simplex_strategy & SIMPLEX_Phase2_DUAL) > 0)) + lp->spx_status = SWITCH_TO_DUAL; + else + primloop(lp, feasible); + if(lp->spx_status == SWITCH_TO_DUAL) + dualloop(lp, TRUE); + } + else { + if(!lost_feas && feasible && ((lp->simplex_strategy & SIMPLEX_Phase2_PRIMAL) > 0)) + lp->spx_status = SWITCH_TO_PRIMAL; + else + dualloop(lp, feasible); + if(lp->spx_status == SWITCH_TO_PRIMAL) + primloop(lp, TRUE); + } + + /* Check for simplex outcomes that always involve breaking out of the loop; + this includes optimality, unboundedness, pure infeasibility (i.e. not + loss of feasibility), numerical failure and perturbation-based degeneracy + handling */ + i = lp->spx_status; + feasible = (MYBOOL) ((i == OPTIMAL) && isPrimalFeasible(lp, lp->epspivot)); + if(feasible || (i == UNBOUNDED)) + break; +#ifdef EnableAntiDegen + else if(((i == INFEASIBLE) && is_anti_degen(lp, ANTIDEGEN_INFEASIBLE)) || + ((i == LOSTFEAS) && is_anti_degen(lp, ANTIDEGEN_LOSTFEAS)) || + ((i == NUMFAILURE) && is_anti_degen(lp, ANTIDEGEN_NUMFAILURE)) || + ((i == DEGENERATE) && is_anti_degen(lp, ANTIDEGEN_STALLING))) { + if((lp->bb_level <= 1) || is_anti_degen(lp, ANTIDEGEN_DURINGBB)) + break; + } +#endif + + /* Check for outcomes that may involve trying another simplex loop */ + if(lp->spx_status == SINGULAR_BASIS) { + lost_feas = FALSE; + singular_count++; + if(singular_count >= DEF_MAXSINGULARITIES) { + report(lp, IMPORTANT, "spx_run: Failure due to too many singular bases.\n"); + lp->spx_status = NUMFAILURE; + break; + } + if(lp->spx_trace || (lp->verbose > DETAILED)) + report(lp, NORMAL, "spx_run: Singular basis; attempting to recover.\n"); + lp->spx_status = RUNNING; + /* Singular pivots are simply skipped by the inversion, leaving a row's + slack variable in the basis instead of the singular user variable. */ + } + else { + lost_feas = (MYBOOL) (lp->spx_status == LOSTFEAS); +#if 0 + /* Optionally handle loss of numerical accuracy as loss of feasibility, + but only attempt a single loop to try to recover from this. */ + lost_feas |= (MYBOOL) ((lp->spx_status == NUMFAILURE) && (lost_feas_count < 1)); +#endif + if(lost_feas) { + lost_feas_count++; + if(lost_feas_count < DEF_MAXSINGULARITIES) { + report(lp, DETAILED, "spx_run: Recovering from lost feasibility (iteration %d).\n", + get_total_iter(lp)); + lp->spx_status = RUNNING; + } + else { + report(lp, IMPORTANT, "Failure due to loss of feasibility %d times (iteration %d).\n", + lost_feas_count, get_total_iter(lp)); + lp->spx_status = NUMFAILURE; + } + } + } + } + + /* Update iteration tallies before returning */ + lp->total_iter += lp->current_iter; + lp->current_iter = 0; + lp->total_bswap += lp->current_bswap; + lp->current_bswap = 0; + + return(lp->spx_status); +} /* spx_run */ + +lprec *make_lag(lprec *lpserver) +{ + int i; + lprec *hlp; + MYBOOL ret; + REAL *duals; + + /* Create a Lagrangean solver instance */ + hlp = lp_solve_make_lp(0, lpserver->columns); + + if(hlp != NULL) { + + /* First create and core variable data */ + set_sense(hlp, is_maxim(lpserver)); + hlp->lag_bound = lpserver->bb_limitOF; + for(i = 1; i <= lpserver->columns; i++) { + lp_solve_set_mat(hlp, 0, i, get_mat(lpserver, 0, i)); + if(is_binary(lpserver, i)) + set_binary(hlp, i, TRUE); + else { + lp_solve_set_int(hlp, i, is_int(lpserver, i)); + set_bounds(hlp, i, get_lowbo(lpserver, i), get_upbo(lpserver, i)); + } + } + /* Then fill data for the Lagrangean constraints */ + hlp->matL = lpserver->matA; + inc_lag_space(hlp, lpserver->rows, TRUE); + ret = get_ptr_sensitivity_rhs(hlp, &duals, NULL, NULL); + for(i = 1; i <= lpserver->rows; i++) { + hlp->lag_con_type[i] = get_constr_type(lpserver, i); + hlp->lag_rhs[i] = lpserver->orig_rh[i]; + hlp->lambda[i] = (ret) ? duals[i - 1] : 0.0; + } + } + + return(hlp); +} + +STATIC int heuristics(lprec *lp, int mode) +/* Initialize / bound a MIP problem */ +{ + lprec *hlp; + int status = PROCFAIL; + + if(lp->bb_level > 1) + return( status ); + + status = RUNNING; + if(FALSE && (lp->int_count > 0)) { + + /* 1. Copy the problem into a new relaxed instance, extracting Lagrangean constraints */ + hlp = make_lag(lp); + + /* 2. Run the Lagrangean relaxation */ + status = lp_solve_solve(hlp); + + /* 3. Copy the key results (bound) into the original problem */ + lp->bb_heuristicOF = hlp->best_solution[0]; + + /* 4. Delete the helper heuristic */ + hlp->matL = NULL; + lp_solve_delete_lp(hlp); + } + return( status ); +} + +STATIC int lag_solve(lprec *lp, REAL start_bound, int num_iter) +{ + int i, j, citer, nochange, oldpresolve; + MYBOOL LagFeas, AnyFeas, Converged, same_basis; + REAL *OrigObj, *ModObj, *SubGrad, *BestFeasSol; + REAL Zub, Zlb, Znow, Zprev, Zbest, rhsmod, hold; + REAL Phi, StepSize = 0.0, SqrsumSubGrad; + double starttime; + + /* Make sure we have something to work with */ + if(lp->spx_status != OPTIMAL) { + lp->lag_status = NOTRUN; + return( lp->lag_status ); + } + lp->lag_status = RUNNING; + + /* Prepare for Lagrangean iterations using results from relaxed problem */ + starttime = lp->timestart; + oldpresolve = lp->do_presolve; + lp->do_presolve = PRESOLVE_NONE; + push_basis(lp, NULL, NULL, NULL); + + /* Allocate iteration arrays */ + allocREAL(lp, &OrigObj, lp->columns + 1, FALSE); + allocREAL(lp, &ModObj, lp->columns + 1, TRUE); + allocREAL(lp, &SubGrad, get_Lrows(lp) + 1, TRUE); + allocREAL(lp, &BestFeasSol, lp->sum + 1, TRUE); + + /* Initialize variables (assume minimization problem in overall structure) */ + Zlb = lp->best_solution[0]; + Zub = start_bound; + Zbest = Zub; + Znow = Zlb; + Zprev = lp->infinite; + rhsmod = 0; + + Phi = DEF_LAGCONTRACT; /* In the range 0-2.0 to guarantee convergence */ +/* Phi = 0.15; */ + LagFeas = FALSE; + Converged= FALSE; + AnyFeas = FALSE; + citer = 0; + nochange = 0; + + /* Initialize reference and solution vectors; don't bother about the + original OF offset since we are maintaining an offset locally. */ + get_row(lp, 0, OrigObj); +#ifdef DirectOverrideOF + set_OF_override(lp, ModObj); +#endif + OrigObj[0] = get_rh(lp, 0); + for(i = 1 ; i <= get_Lrows(lp); i++) + lp->lambda[i] = 0; + + /* Iterate to convergence, failure or user-specified termination */ + while((lp->lag_status == RUNNING) && (citer < num_iter)) { + + citer++; + + /* Compute constraint feasibility gaps and associated sum of squares, + and determine feasibility over the Lagrangean constraints; + SubGrad is the subgradient, which here is identical to the slack. */ + LagFeas = TRUE; + Converged = TRUE; + SqrsumSubGrad = 0; + for(i = 1; i <= get_Lrows(lp); i++) { + hold = lp->lag_rhs[i]; + for(j = 1; j <= lp->columns; j++) + hold -= mat_getitem(lp->matL, i, j) * lp->best_solution[lp->rows + j]; + if(LagFeas) { + if(lp->lag_con_type[i] == EQ) { + if(fabs(hold) > lp->epsprimal) + LagFeas = FALSE; + } + else if(hold < -lp->epsprimal) + LagFeas = FALSE; + } + /* Test for convergence and update */ + if(Converged && (fabs(my_reldiff(hold , SubGrad[i])) > lp->lag_accept)) + Converged = FALSE; + SubGrad[i] = hold; + SqrsumSubGrad += hold * hold; + } + SqrsumSubGrad = sqrt(SqrsumSubGrad); +#if 1 + Converged &= LagFeas; +#endif + if(Converged) + break; + + /* Modify step parameters and initialize ahead of next iteration */ + Znow = lp->best_solution[0] - rhsmod; + if(Znow > Zub) { + /* Handle exceptional case where we overshoot */ + Phi *= DEF_LAGCONTRACT; + StepSize *= (Zub-Zlb) / (Znow-Zlb); + } + else +#define LagBasisContract +#ifdef LagBasisContract +/* StepSize = Phi * (Zub - Znow) / SqrsumSubGrad; */ + StepSize = Phi * (2-DEF_LAGCONTRACT) * (Zub - Znow) / SqrsumSubGrad; +#else + StepSize = Phi * (Zub - Znow) / SqrsumSubGrad; +#endif + + /* Compute the new dual price vector (Lagrangean multipliers, lambda) */ + for(i = 1; i <= get_Lrows(lp); i++) { + lp->lambda[i] += StepSize * SubGrad[i]; + if((lp->lag_con_type[i] != EQ) && (lp->lambda[i] > 0)) { + /* Handle case where we overshoot and need to correct (see above) */ + if(Znow < Zub) + lp->lambda[i] = 0; + } + } +/* normalizeVector(lp->lambda, get_Lrows(lp)); */ + + /* Save the current vector if it is better */ + if(LagFeas && (Znow < Zbest)) { + + /* Recompute the objective function value in terms of the original values */ + MEMCOPY(BestFeasSol, lp->best_solution, lp->sum+1); + hold = OrigObj[0]; + for(i = 1; i <= lp->columns; i++) + hold += lp->best_solution[lp->rows + i] * OrigObj[i]; + BestFeasSol[0] = hold; + if(lp->lag_trace) + report(lp, NORMAL, "lag_solve: Improved feasible solution at iteration %d of %g\n", + citer, hold); + + /* Reset variables */ + Zbest = Znow; + AnyFeas = TRUE; + nochange = 0; + } + else if(Znow == Zprev) { + nochange++; + if(nochange > LAG_SINGULARLIMIT) { + Phi *= 0.5; + nochange = 0; + } + } + Zprev = Znow; + + /* Recompute the objective function values for the next iteration */ + for(j = 1; j <= lp->columns; j++) { + hold = 0; + for(i = 1; i <= get_Lrows(lp); i++) + hold += lp->lambda[i] * mat_getitem(lp->matL, i, j); + ModObj[j] = OrigObj[j] - my_chsign(is_maxim(lp), hold); +#ifndef DirectOverrideOF + lp_solve_set_mat(lp, 0, j, ModObj[j]); +#endif + } + + /* Recompute the fixed part of the new objective function */ + rhsmod = my_chsign(is_maxim(lp), get_rh(lp, 0)); + for(i = 1; i <= get_Lrows(lp); i++) + rhsmod += lp->lambda[i] * lp->lag_rhs[i]; + + /* Print trace/debugging information, if specified */ + if(lp->lag_trace) { + report(lp, IMPORTANT, "Zub: %10g Zlb: %10g Stepsize: %10g Phi: %10g Feas %d\n", + (REAL) Zub, (REAL) Zlb, (REAL) StepSize, (REAL) Phi, LagFeas); + for(i = 1; i <= get_Lrows(lp); i++) + report(lp, IMPORTANT, "%3d SubGrad %10g lambda %10g\n", + i, (REAL) SubGrad[i], (REAL) lp->lambda[i]); + if(lp->sum < 20) + lp_solve_print_lp(lp); + } + + /* Solve the Lagrangean relaxation, handle failures and compute + the Lagrangean objective value, if successful */ + i = spx_solve(lp); + if(lp->spx_status == UNBOUNDED) { + if(lp->lag_trace) { + report(lp, NORMAL, "lag_solve: Unbounded solution encountered with this OF:\n"); + for(i = 1; i <= lp->columns; i++) + report(lp, NORMAL, MPSVALUEMASK " ", (REAL) ModObj[i]); + } + goto Leave; + } + else if((lp->spx_status == NUMFAILURE) || (lp->spx_status == PROCFAIL) || + (lp->spx_status == USERABORT) || (lp->spx_status == TIMEOUT) || + (lp->spx_status == INFEASIBLE)) { + lp->lag_status = lp->spx_status; + } + + /* Compare optimal bases and contract if we have basis stationarity */ +#ifdef LagBasisContract + same_basis = compare_basis(lp); + if(LagFeas && + !same_basis) { + pop_basis(lp, FALSE); + push_basis(lp, NULL, NULL, NULL); + Phi *= DEF_LAGCONTRACT; + } + if(lp->lag_trace) { + report(lp, DETAILED, "lag_solve: Simplex status code %d, same basis %s\n", + lp->spx_status, my_boolstr(same_basis)); + print_solution(lp, 1); + } +#endif + } + + /* Transfer solution values */ + if(AnyFeas) { + lp->lag_bound = my_chsign(is_maxim(lp), Zbest); + for(i = 0; i <= lp->sum; i++) + lp->solution[i] = BestFeasSol[i]; + transfer_solution(lp, TRUE); + if(!is_maxim(lp)) + for(i = 1; i <= get_Lrows(lp); i++) + lp->lambda[i] = my_flipsign(lp->lambda[i]); + } + + /* Do standard postprocessing */ +Leave: + + /* Set status variables and report */ + if(citer >= num_iter) { + if(AnyFeas) + lp->lag_status = FEASFOUND; + else + lp->lag_status = NOFEASFOUND; + } + else + lp->lag_status = lp->spx_status; + if(lp->lag_status == OPTIMAL) { + report(lp, NORMAL, "\nLagrangean convergence achieved in %d iterations\n", citer); + i = check_solution(lp, lp->columns, + lp->best_solution, lp->orig_upbo, lp->orig_lowbo, DEF_EPSSOLUTION); + } + else { + report(lp, NORMAL, "\nUnsatisfactory convergence achieved over %d Lagrangean iterations.\n", + citer); + if(AnyFeas) + report(lp, NORMAL, "The best feasible Lagrangean objective function value was %g\n", + lp->best_solution[0]); + } + + /* Restore the original objective function */ +#ifdef DirectOverrideOF + set_OF_override(lp, NULL); +#else + for(i = 1; i <= lp->columns; i++) + lp_solve_set_mat(lp, 0, i, OrigObj[i]); +#endif + + /* ... and then free memory */ + FREE(BestFeasSol); + FREE(SubGrad); + FREE(OrigObj); + FREE(ModObj); + pop_basis(lp, FALSE); + + lp->timestart = starttime; + lp->timeend = timeNow(); + lp->do_presolve = oldpresolve; + + return( lp->lag_status ); +} + +STATIC int spx_solve(lprec *lp) +{ + int status, itemp; + MYBOOL iprocessed; + REAL test; + + lp->timestart = timeNow(); + lp->timeend = 0; + + lp->total_iter = 0; + lp->total_bswap = 0; + lp->bb_maxlevel = 1; + lp->bb_totalnodes = 0; + lp->bb_level = 0; + lp->bb_solutionlevel = 0; + lp->bb_compressions = 0; + lp->obj_mincost = lp->infinite; + if(lp->invB != NULL) + lp->bfp_restart(lp); + + lp->spx_status = presolve(lp); + if(lp->spx_status != RUNNING) + goto Leave; + + iprocessed = !lp->wasPreprocessed; + preprocess(lp); + if(userabort(lp, -1)) + goto Leave; + + if(mat_validate(lp->matA)) { + + /* Do standard initializations */ + /*allocINT(lp, &(lp->workrowsINT), lp->rows+1, TRUE);*/ + lp->solutioncount = 0; + lp->real_solution = lp->infinite; + if(is_maxim(lp) && (lp->bb_heuristicOF >= lp->infinite)) + lp->best_solution[0] = -lp->infinite; + else if(!is_maxim(lp) && (lp->bb_heuristicOF <= -lp->infinite)) + lp->best_solution[0] = lp->infinite; + else + lp->best_solution[0] = lp->bb_heuristicOF; + + lp->doInvert = TRUE; + lp->doRebase = TRUE; + lp->bb_break = FALSE; + lp->bb_action = ACTION_NONE; /* TEST */ + + /* Do the call to the real underlying solver (note that + run_BB is replacable with any compatible MIP solver) */ + status = run_BB(lp); + + /* Restore modified problem */ + if(iprocessed) + postprocess(lp); + + if(lp->lag_status != RUNNING) { + if((status == OPTIMAL) || (status == SUBOPTIMAL)) { + itemp = check_solution(lp, lp->columns, lp->best_solution, + lp->orig_upbo, lp->orig_lowbo, DEF_EPSSOLUTION); + if((itemp != OPTIMAL) && (lp->spx_status == OPTIMAL)) + lp->spx_status = itemp; + else if((itemp == OPTIMAL) && (status == SUBOPTIMAL)) + lp->spx_status = status; + } + else { + report(lp, NORMAL, "lp_solve unsuccessful after %d iterations and a last best value of %g\n", + lp->total_iter, lp->best_solution[0]); + if(lp->bb_totalnodes > 0) + report(lp, NORMAL, "lp_solve explored %d nodes before termination\n", + lp->bb_totalnodes); + } + } + /*FREE(lp->workrowsINT); + lp->workrowsINT = NULL;*/ + + goto Leave; + } + + /* If we get here, mat_validate(lp) failed. */ + if(lp->bb_trace || lp->spx_trace) + report(lp, CRITICAL, "spx_solve: The current LP seems to be invalid\n"); + lp->spx_status = NUMFAILURE; + +Leave: + lp->timeend = timeNow(); + + if((lp->lag_status != RUNNING) && (lp->invB != NULL)) { + itemp = lp->bfp_nonzeros(lp, TRUE); + test = 100; + if(lp->total_iter > 0) + test *= (REAL) lp->total_bswap/lp->total_iter; + report(lp, NORMAL, "\n "); + report(lp, NORMAL, "Memo: Largest [%s] inv(B) had %d NZ entries, %.1fx largest basis.\n", + lp->bfp_name(), itemp, lp->bfp_efficiency(lp)); + report(lp, NORMAL, " In the total iteration count %d, %d (%.1f%%) were minor/bound swaps.\n", + lp->total_iter, lp->total_bswap, test); + report(lp, NORMAL, " There were %d refactorizations, on average %.1f major pivots/refact.\n", + lp->bfp_refactcount(lp), get_refactfrequency(lp, TRUE)); + + if(MIP_count(lp) > 0) { + report(lp, NORMAL, " The maximum B&B level was %d, %.1fx MIP order with %g compressions/node.\n", + lp->bb_maxlevel, (REAL) lp->bb_maxlevel / (MIP_count(lp)+lp->int_count), + (REAL) lp->bb_compressions/(lp->bb_totalnodes+1.0)); + if(lp->bb_solutionlevel > 0) + report(lp, NORMAL, " The B&B level was %d at the optimal solution.\n", + lp->bb_solutionlevel); + } + report(lp, NORMAL, " Total solver time was %.3f seconds.\n", + lp->timeend- lp->timestart); + } + + return(lp->spx_status); + +} /* spx_solve */ + +int lin_solve(lprec *lp) +{ + int status = NOTRUN; + + if(lp->duals != NULL) + FREE(lp->duals); + + if(lp->dualsfrom != NULL) + FREE(lp->dualsfrom); + + if(lp->dualstill != NULL) + FREE(lp->dualstill); + + if(lp->objfrom != NULL) + FREE(lp->objfrom); + + if(lp->objtill != NULL) + FREE(lp->objtill); + + /* Do heuristics ahead of solving the model */ + if(heuristics(lp, AUTOMATIC) != RUNNING) + return( INFEASIBLE ); + + /* Solve the full, prepared model */ + status = spx_solve(lp); + if(get_Lrows(lp) > 0) { + if(status == OPTIMAL) + status = lag_solve(lp, lp->bb_heuristicOF, DEF_LAGMAXITERATIONS); + else + report(lp, IMPORTANT, "\nCannot do Lagrangean optimization since core model was not solved.\n"); + } + + return( status ); +} + + Index: src/tools/solver/lp_solve/lp_simplex.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_simplex.h diff -N src/tools/solver/lp_solve/lp_simplex.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_simplex.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,24 @@ +#ifndef HEADER_lp_simplex +#define HEADER_lp_simplex + +#include "lp_types.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Put function headers here */ +STATIC int primloop(lprec *lp, MYBOOL feasible); +STATIC int dualloop(lprec *lp, MYBOOL feasible); +STATIC int spx_run(lprec *lp); +STATIC int spx_solve(lprec *lp); +STATIC int lag_solve(lprec *lp, REAL start_bound, int num_iter); +STATIC int heuristics(lprec *lp, int mode); +STATIC int lin_solve(lprec *lp); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_simplex */ Index: src/tools/solver/lp_solve/lp_solve.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_solve.c diff -N src/tools/solver/lp_solve/lp_solve.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_solve.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,794 @@ +#include +#include +#include +#include "lp_lib.h" +#include "patchlevel.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +#define filetypeLP 1 +#define filetypeMPS 2 +#define filetypeFREEMPS 3 +#define filetypeCPLEX 4 +#define filetypeXLI 5 + +int EndOfPgr(int i) +{ +# if defined FORTIFY + Fortify_LeaveScope(); +# endif + exit(i); +} + +void SIGABRT_func(int sig) + { + EndOfPgr(EXIT_FAILURE); + } + +void print_help(char *argv[]) +{ + printf("Usage of %s version %d.%d.%d.%d:\n", argv[0], MAJORVERSION, MINORVERSION, RELEASE, BUILD); + printf("%s [options] [[<]input_file]\n", argv[0]); + printf("List of options:\n"); + printf("-h\t\tprints this message\n"); +#if defined PARSER_LP + printf("-lp\t\tread from LP file (default)\n"); +#endif + printf("-mps\t\tread from MPS file in fixed format\n"); + printf("-fmps\t\tread from MPS file in free format\n"); +#if defined PARSER_CPLEX + printf("-lpt\t\tread from ILOG CPLEX file\n"); +#endif + printf("-rxli xliname filename\n\t\tread file with xli library\n"); + printf("-rxlidata datafilename\n\t\tdata file name for xli library.\n"); + printf("-rxliopt options\n\t\toptions for xli library.\n"); + printf("-wlp filename\twrite to LP file\n"); + printf("-wmps filename\twrite to MPS file in fixed format\n"); + printf("-wfmps filename\twrite to MPS file in free format\n"); +#if defined PARSER_CPLEX + printf("-wlpt filename\twrite to ILOG CPLEX file\n"); +#endif + printf("-wxli xliname filename\n\t\twrite file with xli library\n"); + printf("-wxliopt options\n\t\toptions for xli library.\n"); + printf("-parse_only\tparse input file but do not solve\n"); + printf("\n"); + printf("-min\t\tMinimize the lp problem (overrules setting in file)\n"); + printf("-max\t\tMaximize the lp problem (overrules setting in file)\n"); + printf("-b \tspecify a lower bound for the objective function\n\t\tto the program. If close enough, may speed up the\n\t\tcalculations.\n"); + printf("-r \tspecify max nbr of pivots between a re-inversion of the matrix\n"); + printf("-piv \tspecify simplex pivot rule\n"); + printf("\t -piv0: Select first\n"); + printf("\t -piv1: Select according to Dantzig\n"); + printf("\t -piv2: Select Devex pricing from Paula Harris (default)\n"); + printf("\t -piv3: Select steepest edge\n"); + printf("These pivot rules can be combined with any of the following:\n"); + printf("-pivf\t\tIn case of Steepest Edge, fall back to DEVEX in primal.\n"); + printf("-pivm\t\tMultiple pricing.\n"); + printf("-piva\t\tTemporarily use First Index if cycling is detected.\n"); + printf("-pivr\t\tAdds a small randomization effect to the selected pricer.\n"); +#if defined EnablePartialOptimization + printf("-pivp\t\tEnabled partial pricing.\n"); + printf("-pivpc\t\tEnabled partial pricing on columns.\n"); + printf("-pivpr\t\tEnabled partial pricing on rows.\n"); +#endif + printf("-pivll\t\tScan entering/leaving columns left rather than right.\n"); + printf("-pivla\t\tScan entering/leaving columns alternatingly left/right.\n"); + printf("-s \tuse automatic problem scaling.\n"); + printf("\t -s:\n"); + printf("\t -s0: Numerical range-based scaling\n"); + printf("\t -s1: Geometric scaling\n"); + printf("\t -s2: Curtis-reid scaling\n"); + printf("\t -s3: Scale to convergence using largest absolute value\n"); + printf("\t -s4: Numerical range-based scaling\n"); + printf("\t -s5: Scale to convergence using logarithmic mean of all values\n"); + printf("\t -s6: Scale based on the simple numerical range\n"); + printf("\t -s7: Scale quadratic\n"); + printf("These scaling rules can be combined with any of the following:\n"); + printf("-sp\t\talso do power scaling.\n"); + printf("-si\t\talso do Integer scaling.\n"); + printf("-se\t\talso do equilibration to scale matrix vales to the -1..1 range.\n"); + printf("-presolve\tpresolve problem before start optimizing\n"); + printf("-presolvel\talso eliminate linearly dependent rows\n"); + printf("-presolves\talso convert constraints to SOSes (only SOS1 handled)\n"); + printf("-presolver\tIf the phase 1 solution process finds that a constraint is\n\t\tredundant then this constraint is deleted\n"); + printf("-C \tbasis crash mode\n"); + printf("\t -C0: No crash basis\n"); + printf("\t -C2: Most feasible basis\n"); + printf("-prim\t\tPrefer the primal simplex for both phases.\n"); + printf("-dual\t\tPrefer the dual simplex for both phases.\n"); + printf("-simplexpp\tSet Phase1 Primal, Phase2 Primal.\n"); + printf("-simplexdp\tSet Phase1 Dual, Phase2 Primal.\n"); + printf("-simplexpd\tSet Phase1 Primal, Phase2 Dual.\n"); + printf("-simplexdd\tSet Phase1 Dual, Phase2 Dual.\n"); + printf("-degen\t\tuse perturbations to reduce degeneracy,\n\t\tcan increase numerical instability\n"); + printf("-degenc\t\tuse column check to reduce degeneracy\n"); + printf("-degend\t\tdynamic check to reduce degeneracy\n"); + printf("-trej \tset minimum pivot value\n"); + printf("-epsd \tset minimum tolerance for reduced costs\n"); + printf("-epsb \tset minimum tolerance for the RHS\n"); + printf("-epsel \tset tolerance\n"); + printf("-epsp \tset the value that is used as perturbation scalar for\n\t\tdegenerative problems\n"); + printf("-improve \titerative improvement level\n"); + printf("\t -improve0: none (default)\n"); + printf("\t -improve1: FTRAN only\n"); + printf("\t -improve2: BTRAN only\n"); + printf("\t -improve3: FTRAN + BTRAN\n"); + printf("\t -improve4: Automatic inverse accuracy control in the dual simplex\n"); + printf("-timeout \tTimeout after sec seconds when not solution found.\n"); + printf("-timeoutok\tIf timeout, take the best yet found solution.\n"); + printf("-bfp \tSet basis factorization package.\n"); + printf("\n"); + printf("-e \tspecifies the epsilon which is used to determine whether a\n\t\tfloating point number is in fact an integer.\n\t\tShould be < 0.5\n"); + printf("-g \n"); + printf("-ga \tspecifies the absolute MIP gap for branch-and-bound.\n\t\tThis specifies the absolute allowed tolerance\n\t\ton the object function. Can result in faster solving times.\n"); + printf("-gr \tspecifies the relative MIP gap for branch-and-bound.\n\t\tThis specifies the relative allowed tolerance\n\t\ton the object function. Can result in faster solving times.\n"); + printf("-f\t\tspecifies that branch-and-bound algorithm stops at first found\n"); + printf("\t\tsolution\n"); + printf("-o \tspecifies that branch-and-bound algorithm stops when objective\n"); + printf("\t\tvalue is better than value\n"); + printf("-c\t\tduring branch-and-bound, take the ceiling branch first\n"); + printf("-ca\t\tduring branch-and-bound, the algorithm chooses branch\n"); + printf("-depth \tset branch-and-bound depth limit\n"); + printf("-n \tspecify which solution number to return\n"); + printf("-B \tspecify branch-and-bound rule\n"); + printf("\t -B0: Select Lowest indexed non-integer column (default)\n"); + printf("\t -B1: Selection based on distance from the current bounds\n"); + printf("\t -B2: Selection based on the largest current bound\n"); + printf("\t -B3: Selection based on largest fractional value\n"); + printf("\t -B4: Simple, unweighted pseudo-cost of a variable\n"); + printf("\t -B5: This is an extended pseudo-costing strategy based on minimizing\n\t the number of integer infeasibilities\n"); + printf("\t -B6: This is an extended pseudo-costing strategy based on maximizing\n\t the normal pseudo-cost divided by the number of infeasibilities.\n\t Similar to (the reciprocal of) a cost/benefit ratio\n"); + printf("These branch-and-bound rules can be combined with any of the following:\n"); + printf("-Bw\t\tWeightReverse branch-and-bound\n"); + printf("-Bb\t\tBranchReverse branch-and-bound\n"); + printf("-Bg\t\tGreedy branch-and-bound\n"); + printf("-Bp\t\tPseudoCost branch-and-bound\n"); + printf("-Bf\t\tDepthFirst branch-and-bound\n"); + printf("-Br\t\tRandomize branch-and-bound\n"); + printf("-BG\t\tGubMode branch-and-bound\n"); + printf("-Bd\t\tDynamic branch-and-bound\n"); + printf("-Bs\t\tRestartMode branch-and-bound\n"); + printf("\n"); + printf("-time\t\tPrint CPU time to parse input and to calculate result.\n"); + printf("-v \tverbose mode, gives flow through the program.\n"); + printf("\t\t if level not provided (-v) then -v4 (NORMAL) is taken.\n"); + printf("\t -v0: NEUTRAL\n"); + printf("\t -v1: CRITICAL\n"); + printf("\t -v2: SEVERE\n"); + printf("\t -v3: IMPORTANT (default)\n"); + printf("\t -v4: NORMAL\n"); + printf("\t -v5: DETAILED\n"); + printf("\t -v6: FULL\n"); + printf("-t\t\ttrace pivot selection\n"); + printf("-d\t\tdebug mode, all intermediate results are printed,\n\t\tand the branch-and-bound decisions\n"); + printf("-R\t\treport information while solving the model\n"); + printf("-Db \tDo a generic readable data dump of key lp_solve model variables\n\t\tbefore solve.\n\t\tPrincipally for run difference and debugging purposes\n"); + printf("-Da \tDo a generic readable data dump of key lp_solve model variables\n\t\tafter solve.\n\t\tPrincipally for run difference and debugging purposes\n"); + printf("-i\t\tprint all intermediate valid solutions.\n\t\tCan give you useful solutions even if the total run time\n\t\tis too long\n"); + printf("-ia\t\tprint all intermediate (only non-zero values) valid solutions.\n\t\tCan give you useful solutions even if the total run time\n\t\tis too long\n"); + printf("-S \tPrint solution. If detail omitted, then -S2 is used.\n"); + printf("\t -S0: Print nothing\n"); + printf("\t -S1: Only objective value\n"); + printf("\t -S2: Obj value+variables (default)\n"); + printf("\t -S3: Obj value+variables+constraints\n"); + printf("\t -S4: Obj value+variables+constraints+duals\n"); + printf("\t -S5: Obj value+variables+constraints+duals+lp model\n"); + printf("\t -S6: Obj value+variables+constraints+duals+lp model+scales\n"); + printf("\t -S7: Obj value+variables+constraints+duals+lp model+scales+lp tableau\n"); +} + +void print_cpu_times(const char *info) +{ + static clock_t last_time = 0; + clock_t new_time; + + new_time = clock(); + fprintf(stderr, "CPU Time for %s: %gs (%gs total since program start)\n", + info, (new_time - last_time) / (double) CLOCKS_PER_SEC, + new_time / (double) CLOCKS_PER_SEC); + last_time = new_time; +} + +#if 0 +int myabortfunc(lprec *lp, void *aborthandle) +{ + /* printf("%f\n",lp->rhs[0]*(lp->maximise ? 1 : -1)); */ + return(0); +} +#endif + +static MYBOOL isNum(char *val) +{ + int ord; + char *pointer; + + ord = strtol(val, &pointer, 10); + return(*pointer == 0); +} + +static void DoReport(lprec *lp, char *str) +{ + fprintf(stderr, "%s %6.1fsec %8g\n", str, time_elapsed(lp), get_working_objective(lp)); +} + +static void __WINAPI LPMessageCB(lprec *lp, void *USERHANDLE, int msg) +{ + if(msg==MSG_LPFEASIBLE) + DoReport(lp, "Feasible solution "); + else if(msg==MSG_LPOPTIMAL) + DoReport(lp, "Real solution "); + else if(msg==MSG_MILPFEASIBLE) + DoReport(lp, "First MILP "); + else if(msg==MSG_MILPBETTER) + DoReport(lp, "Improved MILP "); + else + ; +} + +int main(int argc, char *argv[]) +{ + lprec *lp; + char *filen, *wlp = NULL, *wmps = NULL, *wfmps = NULL, *wlpt = NULL; + int i; + int verbose = IMPORTANT /* CRITICAL */; + MYBOOL debug = FALSE; + MYBOOL report = FALSE; + int print_sol = FALSE; + int floor_first; + int bb_depthlimit; + int solutionlimit; + MYBOOL break_at_first; + int scaling = 0; + double scaleloop = 0; + MYBOOL tracing; + short filetype = filetypeLP; + int anti_degen; + short print_timing = FALSE; + short parse_only = FALSE; + int do_presolve; + short objective = 0; + short PRINT_SOLUTION = 2; + int improve; + int pivoting; + int bb_rule; + int max_num_inv; + int scalemode; + int crashmode; + short timeoutok = FALSE; + long sectimeout; + int result; + MYBOOL preferdual = AUTOMATIC; + int simplextype; + REAL obj_bound; + REAL mip_absgap; + REAL mip_relgap; + REAL epsperturb; + REAL epsilon; + REAL epspivot; + REAL epsd; + REAL epsb; + REAL epsel; + REAL break_at_value; + FILE *fpin = stdin; + char *bfp = NULL; + char *rxliname = NULL, *rxli = NULL, *rxlidata = NULL, *rxlioptions = NULL, *wxliname = NULL, *wxli = NULL, *wxlioptions = NULL; + char *debugdump_before = NULL; + char *debugdump_after = NULL; +# define SCALINGTHRESHOLD 0.03 + + /* read command line arguments */ + +# if defined FORTIFY + Fortify_EnterScope(); +# endif + + lp = lp_solve_make_lp(0, 0); + floor_first = get_bb_floorfirst(lp); + bb_depthlimit = get_bb_depthlimit(lp); + solutionlimit = get_solutionlimit(lp); + break_at_first = is_break_at_first(lp); + tracing = is_trace(lp); + anti_degen = get_anti_degen(lp); + do_presolve = get_presolve(lp); + improve = get_improve(lp); + pivoting = get_pivoting(lp); + bb_rule = get_bb_rule(lp); + scalemode = get_scaling(lp); + crashmode = get_basiscrash(lp); + sectimeout = get_timeout(lp); + obj_bound = get_obj_bound(lp); + mip_absgap = get_mip_gap(lp, TRUE); + mip_relgap = get_mip_gap(lp, FALSE); + epsilon = get_epsilon(lp); + epspivot = get_epspivot(lp); + epsperturb = get_epsperturb(lp); + epsd = get_epsd(lp); + epsb = get_epsb(lp); + epsel = get_epsel(lp); + break_at_value = get_break_at_value(lp); + max_num_inv = get_maxpivot(lp); + simplextype = get_simplextype(lp); + lp_solve_delete_lp(lp); + + for(i = 1; i < argc; i++) { + if(strncmp(argv[i], "-v", 2) == 0) { + if (argv[i][2]) + verbose = atoi(argv[i] + 2); + else + verbose = NORMAL; + } + else if(strcmp(argv[i], "-d") == 0) + debug = TRUE; + else if(strcmp(argv[i], "-R") == 0) + report = TRUE; + else if(strcmp(argv[i], "-i") == 0) + print_sol = TRUE; + else if(strcmp(argv[i], "-ia") == 0) + print_sol = AUTOMATIC ; + else if(strcmp(argv[i], "-c") == 0) + floor_first = BRANCH_CEILING; + else if(strcmp(argv[i], "-ca") == 0) + floor_first = BRANCH_AUTOMATIC; + else if((strcmp(argv[i], "-depth") == 0) && (i + 1 < argc)) + bb_depthlimit = atoi(argv[++i]); + else if(strncmp(argv[i], "-B", 2) == 0) { + bb_rule &= ~7; + if (argv[i][2]) + bb_rule |= atoi(argv[i] + 2); + else + bb_rule |= NODE_FIRSTSELECT; + } + else if(strncmp(argv[i], "-Bw", 2) == 0) + bb_rule |= NODE_WEIGHTREVERSEMODE; + else if(strncmp(argv[i], "-Bb", 2) == 0) + bb_rule |= NODE_BRANCHREVERSEMODE; + else if(strncmp(argv[i], "-Bg", 2) == 0) + bb_rule |= NODE_GREEDYMODE; + else if(strncmp(argv[i], "-Bp", 2) == 0) + bb_rule |= NODE_PSEUDOCOSTMODE; + else if(strncmp(argv[i], "-Bf", 2) == 0) + bb_rule |= NODE_DEPTHFIRSTMODE; + else if(strncmp(argv[i], "-Br", 2) == 0) + bb_rule |= NODE_RANDOMIZEMODE; + else if(strncmp(argv[i], "-BG", 2) == 0) + bb_rule |= NODE_GUBMODE; + else if(strncmp(argv[i], "-Bd", 2) == 0) + bb_rule |= NODE_DYNAMICMODE; + else if(strncmp(argv[i], "-Bs", 2) == 0) + bb_rule |= NODE_RESTARTMODE; + else if((strcmp(argv[i], "-n") == 0) && (i + 1 < argc)) + solutionlimit = atoi(argv[++i]); + else if((strcmp(argv[i], "-b") == 0) && (i + 1 < argc)) + obj_bound = atof(argv[++i]); + else if(((strcmp(argv[i], "-g") == 0) || (strcmp(argv[i], "-ga") == 0)) && (i + 1 < argc)) + mip_absgap = atof(argv[++i]); + else if((strcmp(argv[i], "-gr") == 0) && (i + 1 < argc)) + mip_relgap = atof(argv[++i]); + else if((strcmp(argv[i], "-e") == 0) && (i + 1 < argc)) { + epsilon = atof(argv[++i]); + if((epsilon <= 0.0) || (epsilon >= 0.5)) { + fprintf(stderr, "Invalid epsilon %g; 0 < epsilon < 0.5\n", + (double)epsilon); + EndOfPgr(EXIT_FAILURE); + } + } + else if((strcmp(argv[i], "-r") == 0) && (i + 1 < argc)) + max_num_inv = atoi(argv[++i]); + else if((strcmp(argv[i], "-o") == 0) && (i + 1 < argc)) + break_at_value = atof(argv[++i]); + else if(strcmp(argv[i], "-f") == 0) + break_at_first = TRUE; + else if(strcmp(argv[i], "-timeoutok") == 0) + timeoutok = TRUE; + else if(strcmp(argv[i], "-h") == 0) { + print_help(argv); + EndOfPgr(EXIT_SUCCESS); + } + else if(strcmp(argv[i], "-prim") == 0) + preferdual = FALSE; + else if(strcmp(argv[i], "-dual") == 0) + preferdual = TRUE; + else if(strcmp(argv[i], "-simplexpp") == 0) + simplextype = SIMPLEX_PRIMAL_PRIMAL; + else if(strcmp(argv[i], "-simplexdp") == 0) + simplextype = SIMPLEX_DUAL_PRIMAL; + else if(strcmp(argv[i], "-simplexpd") == 0) + simplextype = SIMPLEX_PRIMAL_DUAL; + else if(strcmp(argv[i], "-simplexdd") == 0) + simplextype = SIMPLEX_DUAL_DUAL; + else if(strcmp(argv[i], "-sp") == 0) + scalemode |= SCALE_POWER2; + else if(strcmp(argv[i], "-si") == 0) + scalemode |= SCALE_INTEGERS; + else if(strcmp(argv[i], "-se") == 0) + scalemode |= SCALE_EQUILIBRATE; + else if(strncmp(argv[i], "-s", 2) == 0) { + scaling = SCALE_MEAN; + scalemode &= ~(SCALE_MEAN | SCALE_GEOMETRIC | SCALE_CURTISREID | SCALE_LOGARITHMIC | SCALE_EXTREME); + if (argv[i][2]) { + switch (atoi(argv[i] + 2)) { + case 1: + scalemode |= SCALE_GEOMETRIC; + break; + case 2: + scalemode |= SCALE_CURTISREID; + break; + case 3: + scalemode |= SCALE_EXTREME; + break; + case 4: + scalemode |= SCALE_MEAN; + break; + case 5: + scalemode |= SCALE_LOGARITHMIC | SCALE_MEAN; + break; + case 6: + scalemode |= SCALE_RANGE; + case 7: + scalemode |= SCALE_QUADRATIC; + break; + } + } + if((i + 1 < argc) && (isNum(argv[i + 1]))) + scaleloop = atoi(argv[++i]); + } + else if(strncmp(argv[i], "-C", 2) == 0) + crashmode = atoi(argv[i] + 2); + else if(strcmp(argv[i], "-t") == 0) + tracing = TRUE; + else if(strncmp(argv[i], "-S", 2) == 0) { + if (argv[i][2]) + PRINT_SOLUTION = (short) atoi(argv[i] + 2); + else + PRINT_SOLUTION = 2; + } + else if(strncmp(argv[i], "-improve", 8) == 0) { + if (argv[i][8]) + improve = atoi(argv[i] + 8); + else + improve = 0; + } + else if(strncmp(argv[i], "-piv", 4) == 0) { + pivoting &= ~3; + if (argv[i][4]) + pivoting |= atoi(argv[i] + 4); + else + pivoting |= PRICER_DEVEX | PRICE_ADAPTIVE; + } + else if(strncmp(argv[i], "-pivf", 5) == 0) + pivoting |= PRICE_PRIMALFALLBACK; + else if(strncmp(argv[i], "-pivm", 5) == 0) + pivoting |= PRICE_MULTIPLE; + else if(strncmp(argv[i], "-pivp", 5) == 0) + pivoting |= PRICE_PARTIAL; + else if(strncmp(argv[i], "-piva", 5) == 0) + pivoting |= PRICE_ADAPTIVE; + else if(strncmp(argv[i], "-pivr", 5) == 0) + pivoting |= PRICE_RANDOMIZE; +#if defined EnablePartialOptimization + else if(strncmp(argv[i], "-pivp", 5) == 0) + pivoting |= PRICE_AUTOPARTIAL; + else if(strncmp(argv[i], "-pivpc", 5) == 0) + pivoting |= PRICE_AUTOPARTIALCOLS; + else if(strncmp(argv[i], "-pivpr", 5) == 0) + pivoting |= PRICE_AUTOPARTIALROWS; +#endif + else if(strncmp(argv[i], "-pivll", 5) == 0) + pivoting |= PRICE_LOOPLEFT; + else if(strncmp(argv[i], "-pivla", 5) == 0) + pivoting |= PRICE_LOOPALTERNATE; +#if defined PARSER_LP + else if(strcmp(argv[i],"-lp") == 0) + filetype = filetypeLP; +#endif + else if((strcmp(argv[i],"-wlp") == 0) && (i + 1 < argc)) + wlp = argv[++i]; + else if(strcmp(argv[i],"-mps") == 0) + filetype = filetypeMPS; + else if(strcmp(argv[i],"-fmps") == 0) + filetype = filetypeFREEMPS; + else if((strcmp(argv[i],"-wmps") == 0) && (i + 1 < argc)) + wmps = argv[++i]; + else if((strcmp(argv[i],"-wfmps") == 0) && (i + 1 < argc)) + wfmps = argv[++i]; +#if defined PARSER_CPLEX + else if(strcmp(argv[i],"-lpt") == 0) + filetype = filetypeCPLEX; + else if((strcmp(argv[i],"-wlpt") == 0) && (i + 1 < argc)) + wlpt = argv[++i]; +#endif + else if(strcmp(argv[i],"-degen") == 0) + anti_degen = ANTIDEGEN_DEFAULT; + else if(strcmp(argv[i],"-degenf") == 0) + anti_degen |= ANTIDEGEN_FIXEDVARS; + else if(strcmp(argv[i],"-degenc") == 0) + anti_degen |= ANTIDEGEN_COLUMNCHECK; + else if(strcmp(argv[i],"-degens") == 0) + anti_degen |= ANTIDEGEN_STALLING; + else if(strcmp(argv[i],"-degenn") == 0) + anti_degen |= ANTIDEGEN_NUMFAILURE; + else if(strcmp(argv[i],"-degenl") == 0) + anti_degen |= ANTIDEGEN_LOSTFEAS; + else if(strcmp(argv[i],"-degeni") == 0) + anti_degen |= ANTIDEGEN_INFEASIBLE; + else if(strcmp(argv[i],"-degend") == 0) + anti_degen |= ANTIDEGEN_DYNAMIC; + else if(strcmp(argv[i],"-degend") == 0) + anti_degen |= ANTIDEGEN_DURINGBB; + else if(strcmp(argv[i],"-degenb") == 0) + anti_degen |= ANTIDEGEN_COLUMNCHECK; + else if(strcmp(argv[i],"-time") == 0) { + if(clock() == -1) + fprintf(stderr, "CPU times not available on this machine\n"); + else + print_timing = TRUE; + } + else if((strcmp(argv[i],"-bfp") == 0) && (i + 1 < argc)) + bfp = argv[++i]; + else if((strcmp(argv[i],"-rxli") == 0) && (i + 2 < argc)) { + rxliname = argv[++i]; + rxli = argv[++i]; + fpin = NULL; + filetype = filetypeXLI; + } + else if((strcmp(argv[i],"-rxlidata") == 0) && (i + 1 < argc)) + rxlidata = argv[++i]; + else if((strcmp(argv[i],"-rxliopt") == 0) && (i + 1 < argc)) + rxlioptions = argv[++i]; + else if((strcmp(argv[i],"-wxli") == 0) && (i + 2 < argc)) { + wxliname = argv[++i]; + wxli = argv[++i]; + } + else if((strcmp(argv[i],"-wxliopt") == 0) && (i + 1 < argc)) + wxlioptions = argv[++i]; + else if((strcmp(argv[i],"-Db") == 0) && (i + 1 < argc)) + debugdump_before = argv[++i]; + else if((strcmp(argv[i],"-Da") == 0) && (i + 1 < argc)) + debugdump_after = argv[++i]; + else if((strcmp(argv[i],"-timeout") == 0) && (i + 1 < argc)) + sectimeout = atol(argv[++i]); + else if((strcmp(argv[i],"-trej") == 0) && (i + 1 < argc)) + epspivot = atof(argv[++i]); + else if((strcmp(argv[i],"-epsp") == 0) && (i + 1 < argc)) + epsperturb = atof(argv[++i]); + else if((strcmp(argv[i],"-epsd") == 0) && (i + 1 < argc)) + epsd = atof(argv[++i]); + else if((strcmp(argv[i],"-epsb") == 0) && (i + 1 < argc)) + epsb = atof(argv[++i]); + else if((strcmp(argv[i],"-epsel") == 0) && (i + 1 < argc)) + epsel = atof(argv[++i]); + else if(strcmp(argv[i],"-parse_only") == 0) + parse_only = TRUE; + else if(strcmp(argv[i],"-presolve") == 0) + do_presolve |= PRESOLVE_ROWS | PRESOLVE_COLS; + else if(strcmp(argv[i],"-presolvel") == 0) + do_presolve |= PRESOLVE_LINDEP; + else if(strcmp(argv[i],"-presolves") == 0) + do_presolve |= PRESOLVE_SOS; + else if(strcmp(argv[i],"-presolver") == 0) + do_presolve |= PRESOLVE_REDUCEMIP; + else if(strcmp(argv[i],"-min") == 0) + objective = -1; + else if(strcmp(argv[i],"-max") == 0) + objective = 1; + else if(fpin == stdin) { + filen = argv[i]; + if(*filen == '<') + filen++; + if((fpin = fopen(filen, "r")) == NULL) { + print_help(argv); + fprintf(stderr,"\nError, Unable to open input file '%s'\n", + argv[i]); + EndOfPgr(EXIT_FAILURE); + } + } + else { + filen = argv[i]; + if(*filen != '>') { + print_help(argv); + fprintf(stderr, "\nError, Unrecognized command line argument '%s'\n", + argv[i]); + EndOfPgr(EXIT_FAILURE); + } + } + } + + signal(SIGABRT,/* (void (*) OF((int))) */ SIGABRT_func); + + switch(filetype) { +#if defined PARSER_LP + case filetypeLP: + lp = read_lp(fpin, verbose, "lp" ); + break; +#endif + case filetypeMPS: + lp = read_mps(fpin, verbose); + break; + case filetypeFREEMPS: + lp = read_freemps(fpin, verbose); + break; +#if defined PARSER_CPLEX + case filetypeCPLEX: + lp = read_lpt(fpin, verbose, "lpt" ); + break; +#endif + case filetypeXLI: + lp = read_XLI(rxliname, rxli, rxlidata, rxlioptions, verbose); + break; + } + + if((fpin != NULL) && (fpin != stdin)) + fclose(fpin); + + if(print_timing) + print_cpu_times("Parsing input"); + + if(lp == NULL) { + fprintf(stderr, "Unable to read model.\n"); + EndOfPgr(EXIT_FAILURE); + } + + if(objective != 0) { + if(objective == 1) + lp_solve_set_maxim(lp); + else + lp_solve_set_minim(lp); + } + + if(wlp != NULL) + write_lp(lp, wlp); + + if(wmps != NULL) + write_mps(lp, wmps); + + if(wfmps != NULL) + write_freemps(lp, wfmps); + + if(wxli != NULL) { + if(!set_XLI(lp, wxliname)) { + fprintf(stderr, "Unable to set XLI library (%s).\n", wxliname); + EndOfPgr(EXIT_FAILURE); + } + write_XLI(lp, wxli, wxlioptions, FALSE); + set_XLI(lp, NULL); + } + +#if defined PARSER_CPLEX + if(wlpt != NULL) + write_lpt(lp, wlpt); +#endif + + if(parse_only) { + lp_solve_delete_lp(lp); + EndOfPgr(0); + } + + if(PRINT_SOLUTION >= 5) + lp_solve_print_lp(lp); + +#if 0 + put_abortfunc(lp,(abortfunc *) myabortfunc, NULL); +#endif + + if(sectimeout>0) + set_timeout(lp, sectimeout); + set_print_sol(lp, print_sol); + set_epsilon(lp, epsilon); + set_epspivot(lp, epspivot); + set_epsperturb(lp, epsperturb); + set_epsd(lp, epsd); + set_epsb(lp, epsb); + set_epsel(lp, epsel); + set_debug(lp, debug); + set_bb_floorfirst(lp, floor_first); + set_bb_depthlimit(lp, bb_depthlimit); + set_solutionlimit(lp, solutionlimit); + set_trace(lp, tracing); + set_obj_bound(lp, obj_bound); + set_break_at_value(lp, break_at_value); + set_break_at_first(lp, break_at_first); + set_mip_gap(lp, TRUE, mip_absgap); + set_mip_gap(lp, FALSE, mip_relgap); + set_anti_degen(lp, anti_degen); + set_presolve(lp, do_presolve | ((PRINT_SOLUTION >= 4) ? PRESOLVE_SENSDUALS : 0)); + set_improve(lp, improve); + set_maxpivot(lp, max_num_inv); + if(preferdual != AUTOMATIC) + set_preferdual(lp, preferdual); + set_pivoting(lp, pivoting); + set_scaling(lp, scalemode); + set_basiscrash(lp, crashmode); + set_bb_rule(lp, bb_rule); + set_simplextype(lp, simplextype); + if(bfp != NULL) + if(!set_BFP(lp, bfp)) { + fprintf(stderr, "Unable to set BFP package.\n"); + EndOfPgr(EXIT_FAILURE); + } + if(debugdump_before != NULL) + print_debugdump(lp, debugdump_before); + if(report) + put_msgfunc(lp, LPMessageCB, NULL, MSG_LPFEASIBLE | MSG_LPOPTIMAL | MSG_MILPFEASIBLE | MSG_MILPBETTER | MSG_PERFORMANCE); + + if(scaling) { + if(scaleloop <= 0) + scaleloop = 5; + if(scaleloop - (int) scaleloop < SCALINGTHRESHOLD) + scaleloop = (int) scaleloop + SCALINGTHRESHOLD; + set_scalelimit(lp, scaleloop); + } + + result = lp_solve_solve(lp); + + if(PRINT_SOLUTION >= 6) + print_scales(lp); + + if(print_timing) + print_cpu_times("solving"); + + if(debugdump_after != NULL) + print_debugdump(lp, debugdump_after); + + if((timeoutok) && (result == TIMEOUT) && (get_solutioncount(lp) > 0)) + result = OPTIMAL; + + switch(result) { + case OPTIMAL: + case PROCBREAK: + case FEASFOUND: + if (PRINT_SOLUTION >= 1) + print_objective(lp); + + if (PRINT_SOLUTION >= 2) + print_solution(lp, 1); + + if (PRINT_SOLUTION >= 3) + print_constraints(lp, 1); + + if (PRINT_SOLUTION >= 4) + print_duals(lp); + + if(tracing) + fprintf(stderr, + "Branch & Bound depth: %d\nNodes processed: %d\nSimplex pivots: %d\nNumber of equal solutions: %d\n", + get_max_level(lp), get_total_nodes(lp), get_total_iter(lp), get_solutioncount(lp)); + break; + case NOMEMORY: + if (PRINT_SOLUTION >= 1) + printf("Out of memory\n"); + break; + case INFEASIBLE: + if (PRINT_SOLUTION >= 1) + printf("This problem is infeasible\n"); + break; + case UNBOUNDED: + if (PRINT_SOLUTION >= 1) + printf("This problem is unbounded\n"); + break; + case PROCFAIL: + if (PRINT_SOLUTION >= 1) + printf("The B&B routine failed\n"); + break; + case TIMEOUT: + if (PRINT_SOLUTION >= 1) + printf("Timeout\n"); + break; + case USERABORT: + if (PRINT_SOLUTION >= 1) + printf("User aborted\n"); + break; + default: + if (PRINT_SOLUTION >= 1) + printf("lp_solve failed\n"); + break; + } + + if (PRINT_SOLUTION >= 7) + print_tableau(lp); + + lp_solve_delete_lp(lp); + + EndOfPgr(result); +} Index: src/tools/solver/lp_solve/lp_types.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_types.h diff -N src/tools/solver/lp_solve/lp_types.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_types.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,172 @@ +#ifndef HEADER_lp_types +#define HEADER_lp_types + +/* Define data types */ +/* ------------------------------------------------------------------------- */ +#ifndef REAL + #define REAL double +#endif + +/*#define LREAL long double*/ /* Defines a global update variable long double */ +#define LREAL REAL /* Defines a global update variable regular variable */ +#define RREAL long double /* Defines a local accumulation long double */ +/*#define RREAL REAL*/ /* Defines a local accumulation regular variable */ + +#ifndef DEF_STRBUFSIZE + #define DEF_STRBUFSIZE 512 +#endif +#ifndef MAXINT32 + #define MAXINT32 2147483647 +#endif +#ifndef MAXUINT32 + #define MAXUINT32 4294967295 +#endif +#ifndef MAXINT64 + #define MAXINT64 9223372036854775807 +#endif +#ifndef MAXUINT64 + #define MAXUINT64 18446744073709551616 +#endif +#ifndef CHAR_BIT + #define CHAR_BIT 8 +#endif +#ifndef MYBOOL +/* #define MYBOOL unsigned int */ /* May be faster on some processors */ + #define MYBOOL unsigned char /* Conserves memory */ +#endif + + +/* Constants */ +/* ------------------------------------------------------------------------- */ +#ifndef NULL + #define NULL 0 +#endif + +#define FALSE 0 +#define TRUE 1 +#define AUTOMATIC 2 +#define DYNAMIC 4 + +/* Result ranges */ +#define XRESULT_FREE 0 +#define XRESULT_PLUS 1 +#define XRESULT_MINUS -1 +#define XRESULT_RC XRESULT_FREE + + +/* Compiler/target settings */ +/* ------------------------------------------------------------------------- */ +#ifdef WIN32 +# define __WINAPI WINAPI +#else +# define __WINAPI +#endif + +#ifndef __BORLANDC__ /* Level 0 BEGIN */ + +#ifdef _USRDLL /* Level 1 BEGIN */ + +#if 1 +#define __EXPORT_TYPE __declspec(dllexport) +#else +/* Set up for the Microsoft compiler */ +#ifdef LP_SOLVE_EXPORTS /* Level 2 BEGIN */ + #define __EXPORT_TYPE __declspec(dllexport) +#else + #define __EXPORT_TYPE __declspec(dllimport) +#endif /* Level 2 END */ +#endif + +#else + +#define __EXPORT_TYPE + +#endif /* Level 1 END */ + +#ifdef __cplusplus + #define __EXTERN_C extern "C" +#else + #define __EXTERN_C +#endif + +#else /* Level 0 ELSE */ + +/* Otherwise set up for the Borland compiler */ +#ifdef __DLL__ /* Level 1 BEGIN */ + +#define _USRDLL +#define __EXTERN_C extern "C" + +#ifdef __READING_THE_DLL /* Level 2 BEGIN */ + #define __EXPORT_TYPE __import +#else + #define __EXPORT_TYPE __export +#endif /* Level 2 END */ + +#else + +#define __EXPORT_TYPE +#define __EXTERN_C extern "C" + +#endif /* Level 1 END */ + +#endif /* Level 0 END */ + + +#if 0 + #define STATIC static +#else + #define STATIC +#endif + +#ifdef __cplusplus + #define __EXTERN_C extern "C" +#else + #define __EXTERN_C +#endif + + +/* Define macros */ +/* ------------------------------------------------------------------------- */ +#define my_min(x, y) ((x) < (y) ? (x) : (y)) +#define my_max(x, y) ((x) > (y) ? (x) : (y)) +#define my_range(x, lo, hi) ((x) < (lo) ? (lo) : ((x) > (hi) ? (hi) : (x))) +#ifndef my_mod + #define my_mod(n, m) ((n) % (m)) +#endif +#define my_if(t, x, y) ((t) ? (x) : (y)) +#define my_sign(x) ((x) < 0 ? -1 : 1) +#define my_chsign(t, x) ( ((t) && ((x) != 0)) ? -(x) : (x)) +#define my_flipsign(x) ( fabs((REAL) (x)) == 0 ? 0 : -(x) ) +#define my_roundzero(val, eps) if (fabs((REAL) (val)) < eps) val = 0 +#define my_inflimit(val) (fabs((REAL) (val)) < lp->infinite ? (val) : lp->infinite * my_sign(val) ) +#if 0 + #define my_precision(val, eps) ((fabs((REAL) (val))) < (eps) ? 0 : (val)) +#else + #define my_precision(val, eps) restoreINT(val, eps) +#endif +#define my_reldiff(x, y) (((x) - (y)) / (1.0 + fabs((REAL) (y)))) +#define my_boundstr(x) (fabs(x) < lp->infinite ? sprintf("%g",x) : ((x) < 0 ? "-Inf" : "Inf") ) +#ifndef my_boolstr + #define my_boolstr(x) (!(x) ? "FALSE" : "TRUE") +#endif +#define my_basisstr(x) ((x) ? "BASIC" : "NON-BASIC") +#define my_lowbound(x) ((FULLYBOUNDEDSIMPLEX) ? (x) : 0) + + +/* Forward declarations */ +/* ------------------------------------------------------------------------- */ +typedef struct _lprec lprec; +typedef struct _INVrec INVrec; +/* typedef struct _pricerec pricerec; */ + +typedef struct _pricerec +{ + REAL theta; + REAL pivot; + int varno; + lprec *lp; + MYBOOL isdual; +} pricerec; + +#endif /* HEADER_lp_types */ Index: src/tools/solver/lp_solve/lp_utils.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_utils.c diff -N src/tools/solver/lp_solve/lp_utils.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_utils.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,786 @@ + +#include "string.h" +#include "commonlib.h" +#include "lp_lib.h" +#include "lp_utils.h" +#ifdef INTEGERTIME + #include +#else + #include +#endif + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* + Miscellaneous utilities as implemented for lp_solve v5.0+ + ---------------------------------------------------------------------------------- + Author: Kjell Eikland + Contact: kjell.eikland@broadpark.no + License terms: GLPL. + + Requires: lp_utils.h, lp_lib.h + + Release notes: + v1.0.0 1 January 2003 Memory allocation, sorting, searching, time and + doubly linked list functions. + v1.1.0 15 May 2004 Added vector packing functionality + + ---------------------------------------------------------------------------------- +*/ + +STATIC MYBOOL allocCHAR(lprec *lp, char **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (char *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (char *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + memset(*ptr, '\0', size * sizeof(**ptr)); + } + else + *ptr = (char *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d bytes failed on line %d of file %s\n", + size * sizeof(**ptr), __LINE__, __FILE__); + return( FALSE ); + } + else + return( TRUE ); +} +STATIC MYBOOL allocMYBOOL(lprec *lp, MYBOOL **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (MYBOOL *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (MYBOOL *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + memset(*ptr, '\0', size * sizeof(**ptr)); + } + else + *ptr = (MYBOOL *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d bytes failed on line %d of file %s\n", + size * sizeof(**ptr), __LINE__, __FILE__); + return( FALSE ); + } + else + return( TRUE ); +} +STATIC MYBOOL allocINT(lprec *lp, int **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (int *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (int *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + memset(*ptr, '\0', size * sizeof(**ptr)); + } + else + *ptr = (int *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d bytes failed on line %d of file %s\n", + size * sizeof(**ptr), __LINE__, __FILE__); + return( FALSE ); + } + else + return( TRUE ); +} +STATIC MYBOOL allocREAL(lprec *lp, REAL **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (REAL *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (REAL *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + memset(*ptr, '\0', size * sizeof(**ptr)); + } + else + *ptr = (REAL *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d bytes failed on line %d of file %s\n", + size * sizeof(**ptr), __LINE__, __FILE__); + return( FALSE ); + } + else + return( TRUE ); +} +STATIC MYBOOL allocLREAL(lprec *lp, LREAL **ptr, int size, MYBOOL clear) +{ + if(clear == TRUE) + *ptr = (LREAL *) calloc(size, sizeof(**ptr)); + else if(clear & AUTOMATIC) { + *ptr = (LREAL *) realloc(*ptr, size * sizeof(**ptr)); + if(clear & TRUE) + memset(*ptr, '\0', size * sizeof(**ptr)); + } + else + *ptr = (LREAL *) malloc(size * sizeof(**ptr)); + if(((*ptr) == NULL) && (size > 0)) { + lp->report(lp, CRITICAL, "alloc of %d bytes failed on line %d of file %s\n", + size * sizeof(**ptr), __LINE__, __FILE__); + return( FALSE ); + } + else + return( TRUE ); +} + +STATIC MYBOOL allocFREE(lprec *lp, void **ptr) +{ + MYBOOL status = TRUE; + + if(*ptr != NULL) { + free(*ptr); + *ptr = NULL; + } + else { + status = FALSE; + lp->report(lp, CRITICAL, "free failed on line %d of file %s\n", + __LINE__, __FILE__); + } + return(status); +} + +REAL *cloneREAL(lprec *lp, REAL *origlist, int size) +{ + REAL *newlist; + + size += 1; + if(allocREAL(lp, &newlist, size, FALSE)) + MEMCOPY(newlist, origlist, size); + return(newlist); +} +MYBOOL *cloneMYBOOL(lprec *lp, MYBOOL *origlist, int size) +{ + MYBOOL *newlist; + + size += 1; + if(allocMYBOOL(lp, &newlist, size, FALSE)) + MEMCOPY(newlist, origlist, size); + return(newlist); +} +int *cloneINT(lprec *lp, int *origlist, int size) +{ + int *newlist; + + size += 1; + if(allocINT(lp, &newlist, size, FALSE)) + MEMCOPY(newlist, origlist, size); + return(newlist); +} + +STATIC void roundVector(LREAL *myvector, int endpos, LREAL roundzero) +{ + if(roundzero > 0) + for(; endpos >= 0; myvector++, endpos--) + if(fabs(*myvector) < roundzero) + *myvector = 0; +/* my_round((*myvector), roundzero); */ /* Experimental */ +} + +STATIC REAL normalizeVector(REAL *myvector, int endpos) +/* Scale the ingoing vector so that its norm is unit, and return the original length */ +{ + int i; + REAL SSQ; + + /* Cumulate squares */ + SSQ = 0; + for(i = 0; i <= endpos; myvector++, i++) + SSQ += (*myvector) * (*myvector); + + /* Normalize */ + SSQ = sqrt(SSQ); + if(SSQ > 0) + for(myvector--; i > 0; myvector--, i--) + (*myvector) /= SSQ; + + return( SSQ ); +} + +/* ---------------------------------------------------------------------------------- */ +/* Other general utilities */ +/* ---------------------------------------------------------------------------------- */ + +STATIC void swapINT(int *item1, int *item2) +{ + STATIC int hold = *item1; + *item1 = *item2; + *item2 = hold; +} + +STATIC void swapREAL(REAL *item1, REAL *item2) +{ + STATIC REAL hold = *item1; + *item1 = *item2; + *item2 = hold; +} + +STATIC void swapPTR(void **item1, void **item2) +{ + STATIC void *hold; + hold = *item1; + *item1 = *item2; + *item2 = hold; +} + + +STATIC REAL restoreINT(REAL valREAL, REAL epsilon) +{ + STATIC REAL valINT, fracREAL, fracABS; + + fracREAL = modf(valREAL, &valINT); + fracABS = fabs(fracREAL); + if(fracABS < epsilon) + return(valINT); + else if(fracABS > 1-epsilon) { + if(fracREAL < 0) + return(valINT-1); + else + return(valINT+1); + } + return(valREAL); +} + +STATIC REAL roundToPrecision(REAL value, REAL precision) +{ +#if 1 + REAL vmod; + int vexp2, vexp10; +# ifndef __HYPER +# ifdef WIN32 +# define __HYPER hyper +# else +# define __HYPER long long +# endif +# endif + __HYPER sign; + + sign = my_sign(value); + value = fabs(value); + + /* Round to integer if possible */ + if(value < precision) + return( 0 ); + else if(value == floor(value)) + return( value*sign ); + else if((value < (REAL) MAXINT64) && + (modf((REAL) (value+precision), &vmod) < precision)) { + sign *= (__HYPER) (value+precision); + return( (REAL) sign ); + } + + /* Optionally round with base 2 representation for additional precision */ +#define roundPrecisionBase2 +#ifdef roundPrecisionBase2 + value = frexp(value, &vexp2); +#else + vexp2 = 0; +#endif + + /* Convert to desired precision */ + vexp10 = (int) log10(value); + precision *= pow(10.0, vexp10); + modf(value/precision+0.5, &value); + value *= sign*precision; + + /* Restore base 10 representation if base 2 was active */ + if(vexp2 != 0) + value = ldexp(value, vexp2); +#endif + + return( value ); +} + + +/* ---------------------------------------------------------------------------------- */ +/* Searching function specialized for lp_solve */ +/* ---------------------------------------------------------------------------------- */ +STATIC int searchFor(int target, int *attributes, int size, int offset, MYBOOL absolute) +{ + int beginPos, endPos; + int newPos, match; + + /* Set starting and ending index offsets */ + beginPos = offset; + endPos = beginPos + size - 1; + + /* Do binary search logic based on a sorted attribute vector */ + newPos = (beginPos + endPos) / 2; + match = attributes[newPos]; + if(absolute) + match = abs(match); + while(endPos - beginPos > LINEARSEARCH) { + if(match < target) { + beginPos = newPos + 1; + newPos = (beginPos + endPos) / 2; + match = attributes[newPos]; + if(absolute) + match = abs(match); + } + else if(match > target) { + endPos = newPos - 1; + newPos = (beginPos + endPos) / 2; + match = attributes[newPos]; + if(absolute) + match = abs(match); + } + else { + beginPos = newPos; + endPos = newPos; + } + } + + /* Do linear (unsorted) search logic */ + if(endPos - beginPos <= LINEARSEARCH) { + match = attributes[beginPos]; + if(absolute) + match = abs(match); + while((beginPos < endPos) && (match != target)) { + beginPos++; + match = attributes[beginPos]; + if(absolute) + match = abs(match); + } + if(match == target) + endPos = beginPos; + } + + /* Return the index if a match was found, or signal failure with a -1 */ + if((beginPos == endPos) && (match == target)) + return(beginPos); + else + return(-1); + +} + + +/* ---------------------------------------------------------------------------------- */ +/* Other supporting math routines */ +/* ---------------------------------------------------------------------------------- */ + +STATIC MYBOOL isINT(lprec *lp, REAL value) +{ +#if 1 + return( (MYBOOL) (modf(fabs(value)+lp->epsint, &value) < 2*lp->epsint) ); +#elif 0 + value -= (REAL)floor(value); + return( (MYBOOL) ((value < lp->epsint) || (value > (1 - lp->epsint)) ); +#else + value += lp->epsint; + return( (MYBOOL) (fabs(value-floor(value)) < 2*lp->epsint) ); +#endif +} + +STATIC MYBOOL isInf(lprec *lp, REAL value) +{ + if(fabs(value) >= lp->infinite) + return( TRUE ); + else + return( FALSE ); +} + +STATIC MYBOOL isOrigFixed(lprec *lp, int varno) +{ + return( (MYBOOL) (lp->orig_upbo[varno] - lp->orig_lowbo[varno] <= lp->epsmachine) ); +} + +STATIC void chsign_bounds(REAL *lobound, REAL *upbound) +{ + REAL temp; + temp = *upbound; + if(fabs(*lobound) > 0) + *upbound = -(*lobound); + else + *upbound = 0; + if(fabs(temp) > 0) + *lobound = -temp; + else + *lobound = 0; +} + + +/* ---------------------------------------------------------------------------------- */ +/* Define routines for doubly linked lists of integers */ +/* ---------------------------------------------------------------------------------- */ + +STATIC int createLink(int size, LLrec **linkmap, MYBOOL *usedpos) +{ + int i, j; + MYBOOL reverse; + + *linkmap = (LLrec *) calloc(1, sizeof(**linkmap)); + if(*linkmap == NULL) + return( -1 ); + + reverse = (MYBOOL) (size < 0); + if(reverse) + size = -size; + (*linkmap)->map = (int *) calloc(2*(size + 1), sizeof(int)); + if((*linkmap)->map == NULL) + return( -1 ); + + (*linkmap)->size = size; + j = 0; + if(usedpos == NULL) + (*linkmap)->map[0] = 0; + else { + for(i = 1; i <= size; i++) + if(!usedpos[i] ^ reverse) { + /* Set the forward link */ + (*linkmap)->map[j] = i; + /* Set the backward link */ + (*linkmap)->map[size+i] = j; + j = i; + (*linkmap)->count++; + } + } + (*linkmap)->map[2*size+1] = j; + + return( (*linkmap)->count ); +} + +STATIC MYBOOL freeLink(LLrec **linkmap) +{ + MYBOOL status = TRUE; + + if((linkmap == NULL) || (*linkmap == NULL)) + status = FALSE; + else { + if((*linkmap)->map != NULL) + free((*linkmap)->map); + free(*linkmap); + *linkmap = NULL; + } + return( status ); +} + +STATIC int sizeLink(LLrec *linkmap) +{ + return(linkmap->size); +} + +STATIC MYBOOL isActiveLink(LLrec *linkmap, int itemnr) +{ + if((linkmap->map[itemnr] != 0) || + (linkmap->map[linkmap->size+itemnr] != 0) || + (linkmap->map[0] == itemnr)) + return( TRUE ); + else + return( FALSE ); +} + +STATIC int countActiveLink(LLrec *linkmap) +{ + return(linkmap->count); +} + +STATIC int countInactiveLink(LLrec *linkmap) +{ + return(linkmap->size-linkmap->count); +} + +STATIC int firstActiveLink(LLrec *linkmap) +{ + return(linkmap->map[0]); +} + +STATIC int lastActiveLink(LLrec *linkmap) +{ + return(linkmap->map[2*linkmap->size+1]); +} + +STATIC MYBOOL appendLink(LLrec *linkmap, int newitem) +{ + int k, size; + size = linkmap->size; + + if(linkmap->map[newitem] != 0) + return( FALSE ); + + /* Link forward */ + k = linkmap->map[2*size+1]; + linkmap->map[k] = newitem; + + /* Link backward */ + linkmap->map[size+newitem] = k; + linkmap->map[2*size+1] = newitem; + + /* Update count and return */ + linkmap->count++; + + return( TRUE ); +} + +STATIC MYBOOL insertLink(LLrec *linkmap, int afteritem, int newitem) +{ + int k, size; + + size = linkmap->size; + + if(linkmap->map[newitem] != 0) + return( FALSE ); + + if(afteritem == linkmap->map[2*size+1]) + appendLink(linkmap, newitem); + else { + /* Link forward */ + k = linkmap->map[afteritem]; + linkmap->map[afteritem] = newitem; + linkmap->map[newitem] = k; + + /* Link backward */ + linkmap->map[size+k] = newitem; + linkmap->map[size+newitem] = afteritem; + + /* Update count */ + linkmap->count++; + } + + return( TRUE ); +} + +STATIC MYBOOL fillLink(LLrec *linkmap) +{ + int k, size; + size = linkmap->size; + + k = firstActiveLink(linkmap); + if(k != 0) + return( FALSE ); + for(k = 1; k <= size; k++) + appendLink(linkmap, k); + return( TRUE ); +} + +STATIC int nextActiveLink(LLrec *linkmap, int backitemnr) +{ + if((backitemnr < 0) || (backitemnr > linkmap->size)) + return( -1 ); + else + return(linkmap->map[backitemnr]); +} + +STATIC int prevActiveLink(LLrec *linkmap, int forwitemnr) +{ + if((forwitemnr <= 0) || (forwitemnr > linkmap->size+1)) + return( -1 ); + else + return(linkmap->map[linkmap->size+forwitemnr]); +} + +STATIC int firstInactiveLink(LLrec *linkmap) +{ + int i, n; + + if(countInactiveLink(linkmap) == 0) + return( 0 ); + n = 1; + i = firstActiveLink(linkmap); + while(i == n) { + n++; + i = nextActiveLink(linkmap, i); + } + return( n ); +} + +STATIC int lastInactiveLink(LLrec *linkmap) +{ + int i, n; + + if(countInactiveLink(linkmap) == 0) + return( 0 ); + n = linkmap->size; + i = lastActiveLink(linkmap); + while(i == n) { + n--; + i = prevActiveLink(linkmap, i); + } + return( n ); +} + +STATIC int nextInactiveLink(LLrec *linkmap, int backitemnr) +{ + do { + backitemnr++; + } while((backitemnr <= linkmap->size) && isActiveLink(linkmap, backitemnr)); + if(backitemnr <= linkmap->size) + return( backitemnr ); + else + return( 0 ); +} + +STATIC int prevInactiveLink(LLrec *linkmap, int forwitemnr) +{ + return( 0 ); +} + +STATIC MYBOOL removeLink(LLrec *linkmap, int itemnr) +{ + int prevnr, nextnr, size; + + size = linkmap->size; + + if((itemnr <= 0) || (itemnr > size)) + return( FALSE ); + + /* Get link data at the specified position */ + nextnr = linkmap->map[itemnr]; + prevnr = linkmap->map[size+itemnr]; + + /* Update forward link */ + linkmap->map[prevnr] = linkmap->map[itemnr]; + linkmap->map[itemnr] = 0; + + /* Update backward link */ + if(nextnr == 0) + linkmap->map[2*size+1] = prevnr; + else + linkmap->map[size+nextnr] = linkmap->map[size+itemnr]; + linkmap->map[size+itemnr] = 0; + + /* Decrement the count */ + linkmap->count--; + + return( TRUE ); +} + +STATIC LLrec *cloneLink(LLrec *sourcemap) +{ + LLrec *testmap; + + createLink(sourcemap->size, &testmap, NULL); + MEMCOPY(testmap->map, sourcemap->map, 2*(sourcemap->size+1)); + testmap->size = sourcemap->size; + testmap->count = sourcemap->count; + + return(testmap); +} + +STATIC int compareLink(LLrec *linkmap1, LLrec *linkmap2) +{ + int test; + + test = memcmp(&linkmap1->size, &linkmap2->size, sizeof(int)); + if(test == 0) + test = memcmp(&linkmap1->count, &linkmap2->count, sizeof(int)); + if(test == 0) + test = memcmp(linkmap1->map, linkmap2->map, sizeof(int)*(2*linkmap1->size+1)); + + return( test ); +} + +STATIC MYBOOL verifyLink(LLrec *linkmap, int itemnr, MYBOOL doappend) +{ + LLrec *testmap; + + testmap = cloneLink(linkmap); + if(doappend) { + appendLink(testmap, itemnr); + removeLink(testmap, itemnr); + } + else { + int previtem = prevActiveLink(testmap, itemnr); + removeLink(testmap, itemnr); + insertLink(testmap, previtem, itemnr); + } + itemnr = compareLink(linkmap, testmap); + freeLink(&testmap); + return((MYBOOL) (itemnr == 0)); +} + +/* Packed vector routines */ +STATIC PVrec *createPackedVector(int size, REAL *values, int *workvector) +{ + int i, k; + REGISTER REAL ref; + PVrec *newPV = NULL; + MYBOOL localWV = (MYBOOL) (workvector == NULL); + + if(localWV) + workvector = (int *) malloc((size+1)*sizeof(*workvector)); + + /* Tally equal-valued vector entries - also check if it is worth compressing */ + k = 0; + workvector[k] = 1; + ref = values[1]; + for(i = 2; i <= size; i++) { + if(fabs(ref - values[i]) > 1.0e-16) { + k++; + workvector[k] = i; + ref = values[i]; + } + } + if(k > size / 2) { + if(localWV) + FREE(workvector); + return( newPV ); + } + + /* Create the packing object, adjust the position vector and allocate value vector */ + newPV = (PVrec *) malloc(sizeof(*newPV)); + k++; /* Adjust from index to to count */ + newPV->count = k; + if(localWV) + newPV->startpos = (int *) realloc(workvector, (k + 1)*sizeof(*(newPV->startpos))); + else { + newPV->startpos = (int *) malloc((k + 1)*sizeof(*(newPV->startpos))); + MEMCOPY(newPV->startpos, workvector, k); + } + newPV->startpos[k] = size + 1; /* Store terminal index + 1 for searching purposes */ + newPV->value = (REAL *) malloc(k*sizeof(*(newPV->value))); + + /* Fill the values vector before returning */ + for(i = 0; i < k; i++) + newPV->value[i] = values[newPV->startpos[i]]; + + return( newPV ); +} + +STATIC MYBOOL unpackPackedVector(PVrec *PV, REAL **target) +{ + int i, ii, k; + REGISTER REAL ref; + + /* Test for validity of the target and create it if necessary */ + if(target == NULL) + return( FALSE ); + if(*target == NULL) + allocREAL(NULL, target, PV->startpos[PV->count], FALSE); + + /* Expand the packed vector into the target */ + i = PV->startpos[0]; + for(k = 0; k < PV->count; k++) { + ii = PV->startpos[k+1]; + ref = PV->value[k]; + while (i < ii) { + (*target)[i] = ref; + i++; + } + } + return( TRUE ); +} + +STATIC REAL getvaluePackedVector(PVrec *PV, int index) +{ + index = searchFor(index, PV->startpos, PV->count, 0, FALSE); + index = abs(index)-1; + if(index >= 0) + return( PV->value[index] ); + else + return( 0 ); +} + +STATIC MYBOOL freePackedVector(PVrec **PV) +{ + if((PV == NULL) || (*PV == NULL)) + return( FALSE ); + + FREE((*PV)->value); + FREE((*PV)->startpos); + FREE(*PV); + return( TRUE ); +} + Index: src/tools/solver/lp_solve/lp_utils.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_utils.h diff -N src/tools/solver/lp_solve/lp_utils.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_utils.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,85 @@ +#ifndef HEADER_lp_utils +#define HEADER_lp_utils + +#include "lp_types.h" + +typedef struct _LLrec +{ + int size; /* The allocated list size */ + int count; /* The current entry count */ + int *map; /* The list of forward and backward-mapped entries */ +} LLrec; + +typedef struct _PVrec +{ + int count; /* The allocated list item count */ + int *startpos; /* Starting index of the current value */ + REAL *value; /* The list of forward and backward-mapped entries */ +} PVrec; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Put function headers here */ +STATIC MYBOOL allocCHAR(lprec *lp, char **ptr, int size, MYBOOL clear); +STATIC MYBOOL allocMYBOOL(lprec *lp, MYBOOL **ptr, int size, MYBOOL clear); +STATIC MYBOOL allocINT(lprec *lp, int **ptr, int size, MYBOOL clear); +STATIC MYBOOL allocREAL(lprec *lp, REAL **ptr, int size, MYBOOL clear); +STATIC MYBOOL allocLREAL(lprec *lp, LREAL **ptr, int size, MYBOOL clear); +STATIC MYBOOL allocFREE(lprec *lp, void **ptr); +REAL *cloneREAL(lprec *lp, REAL *origlist, int size); +MYBOOL *cloneMYBOOL(lprec *lp, MYBOOL *origlist, int size); +int *cloneINT(lprec *lp, int *origlist, int size); + +STATIC void roundVector(LREAL *myvector, int endpos, LREAL roundzero); +STATIC REAL normalizeVector(REAL *myvector, int endpos); + +STATIC void swapINT(int *item1, int *item2); +STATIC void swapREAL(REAL *item1, REAL *item2); +STATIC void swapPTR(void **item1, void **item2); +STATIC REAL restoreINT(REAL valREAL, REAL epsilon); +STATIC REAL roundToPrecision(REAL value, REAL precision); + +STATIC int searchFor(int target, int *attributes, int size, int offset, MYBOOL absolute); + +STATIC MYBOOL isINT(lprec *lp, REAL value); +STATIC MYBOOL isInf(lprec *lp, REAL value); +STATIC MYBOOL isOrigFixed(lprec *lp, int varno); +STATIC void chsign_bounds(REAL *lobound, REAL *upbound); + +/* Doubly linked list routines */ +STATIC int createLink(int size, LLrec **linkmap, MYBOOL *usedpos); +STATIC MYBOOL freeLink(LLrec **linkmap); +STATIC int sizeLink(LLrec *linkmap); +STATIC MYBOOL isActiveLink(LLrec *linkmap, int itemnr); +STATIC int countActiveLink(LLrec *linkmap); +STATIC int countInactiveLink(LLrec *linkmap); +STATIC int firstActiveLink(LLrec *linkmap); +STATIC int lastActiveLink(LLrec *linkmap); +STATIC MYBOOL appendLink(LLrec *linkmap, int newitem); +STATIC MYBOOL insertLink(LLrec *linkmap, int afteritem, int newitem); +STATIC MYBOOL fillLink(LLrec *linkmap); +STATIC int nextActiveLink(LLrec *linkmap, int backitemnr); +STATIC int prevActiveLink(LLrec *linkmap, int forwitemnr); +STATIC int firstInactiveLink(LLrec *linkmap); +STATIC int lastInactiveLink(LLrec *linkmap); +STATIC int nextInactiveLink(LLrec *linkmap, int backitemnr); +STATIC int prevInactiveLink(LLrec *linkmap, int forwitemnr); +STATIC MYBOOL removeLink(LLrec *linkmap, int itemnr); +STATIC LLrec *cloneLink(LLrec *sourcemap); +STATIC int compareLink(LLrec *linkmap1, LLrec *linkmap2); +STATIC MYBOOL verifyLink(LLrec *linkmap, int itemnr, MYBOOL doappend); + +/* Packed vector routines */ +STATIC PVrec *createPackedVector(int size, REAL *values, int *workvector); +STATIC MYBOOL unpackPackedVector(PVrec *PV, REAL **target); +STATIC REAL getvaluePackedVector(PVrec *PV, int index); +STATIC MYBOOL freePackedVector(PVrec **PV); + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_utils */ Index: src/tools/solver/lp_solve/lp_wlp.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_wlp.c diff -N src/tools/solver/lp_solve/lp_wlp.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_wlp.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,245 @@ + +#include "lp_lib.h" +#include "lp_utils.h" +#include "lp_wlp.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +/* ------------------------------------------------------------------------- */ +/* Input and output of lp format model files for lp_solve */ +/* ------------------------------------------------------------------------- */ + +STATIC void write_lpcomment(FILE *output, char const *string, MYBOOL newlinebefore) +{ + fprintf(output, "%s/* %s */\n", (newlinebefore) ? "\n" : "", string); +} + +STATIC void write_lprow(lprec *lp, int rowno, FILE *output) +{ + int i, ie, j; + REAL a; + MATrec *mat = lp->matA; + MYBOOL first = TRUE; + + if(rowno == 0) + i = 0; + else + i = mat->row_end[rowno-1]; + ie = mat->row_end[rowno]; + for(; i < ie; i++) { + j = ROW_MAT_COL(mat->row_mat[i]); + if(is_splitvar(lp, j)) + continue; +#if 0 + a = get_mat_byindex(lp, i, TRUE); +#else + a = ROW_MAT_VALUE(mat->row_mat[i]); + a = my_chsign(is_chsign(lp, rowno), a); + a = unscaled_mat(lp, a, rowno, j); +#endif + if(!first) + fputc(' ', output); + else + first = FALSE; + if(a == -1) + fprintf(output, "-"); + else if(a == 1) + fprintf(output, "+"); + else + fprintf(output, "%+.12g ", (double)a); + fprintf(output, "%s", get_col_name(lp, j)); + } +} + +MYBOOL LP_writefile(lprec *lp, char *filename) +{ + int i, j, b; + MYBOOL ok; + REAL a; + FILE *output = stdout; + char *ptr; + +#ifdef Paranoia + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "LP_writefile: Cannot write to LP file while in row entry mode.\n"); + return(FALSE); + } +#endif + if(!mat_validate(lp->matA)) { + report(lp, IMPORTANT, "LP_writefile: Could not validate the data matrix.\n"); + return(FALSE); + } + + ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); + if(!ok) + return(ok); + if(filename == NULL && lp->outstream != NULL) + output = lp->outstream; + + /* Write the objective function */ + write_lpcomment(output, "Objective function", FALSE); + if(is_maxim(lp)) + fprintf(output, "max: "); + else + fprintf(output, "min: "); + + write_lprow(lp, 0, output); + a = get_rh(lp, 0); + if(a) + fprintf(output, " %+.12g", a); + fprintf(output, ";\n"); + + /* Write constraints */ + if(lp->rows > 0) + write_lpcomment(output, "Constraints", TRUE); + for(j = 1; j <= lp->rows; j++) { + if(lp->names_used && (lp->row_name[j] != NULL)) + ptr = get_row_name(lp, j); + else + ptr = NULL; + if((ptr != NULL) && (*ptr)) + fprintf(output, "%s: ", ptr); + +#ifndef SingleBoundedRowInLP + /* Write the ranged part of the constraint, if specified */ + if ((lp->orig_upbo[j]) && (lp->orig_upbo[j] < lp->infinite)) { + if(my_chsign(is_chsign(lp, j), lp->orig_rh[j] / (lp->scaling_used ? lp->scalars[j] : 1.0)) == -lp->infinite) + fprintf(output, "-Inf %s ", (is_chsign(lp, j)) ? ">=" : "<="); + else + fprintf(output, "%+.12g %s ", + (lp->orig_upbo[j]-lp->orig_rh[j]) * (is_chsign(lp, j) ? 1.0 : -1.0) / (lp->scaling_used ? lp->scalars[j] : 1.0), + (is_chsign(lp, j)) ? ">=" : "<="); + } +#endif + + write_lprow(lp, j, output); + if(lp->orig_upbo[j] == 0) + fprintf(output, " ="); + else if(is_chsign(lp, j)) + fprintf(output, " >="); + else + fprintf(output, " <="); + if(fabs(get_rh(lp, j) + lp->infinite) < 1) + fprintf(output, " -Inf;\n"); + else + fprintf(output, " %.12g;\n", get_rh(lp, j)); + +#ifdef SingleBoundedRowInLP + /* Write the ranged part of the constraint, if specified */ + if ((lp->orig_upbo[j]) && (lp->orig_upbo[j] < lp->infinite)) { + if(lp->names_used && (lp->row_name[j] != NULL)) + ptr = get_row_name(lp, j); + else + ptr = NULL; + if((ptr != NULL) && (*ptr)) + fprintf(output, "%s: ", ptr); + write_lprow(lp, j, output); + fprintf(output, " %s %g;\n", + (is_chsign(lp, j)) ? "<=" : ">=", + (lp->orig_upbo[j]-lp->orig_rh[j]) * (is_chsign(lp, j) ? 1.0 : -1.0) / (lp->scaling_used ? lp->scalars[j] : 1.0)); + } +#endif + } + + /* Write bounds on variables */ + write_lpcomment(output, "Variable bounds", TRUE); + for(i = lp->rows + 1; i <= lp->sum; i++) + if(!is_splitvar(lp, i - lp->rows)) { + if(lp->orig_lowbo[i] == lp->orig_upbo[i]) + fprintf(output, "%s = %.12g;\n", get_col_name(lp, i - lp->rows), get_upbo(lp, i - lp->rows)); + else { +#ifndef SingleBoundedRowInLP + if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite)) + fprintf(output, "%.12g <= %s <= %.12g;\n", + get_lowbo(lp, i - lp->rows), + get_col_name(lp, i - lp->rows), + get_upbo(lp, i - lp->rows)); + else +#endif + { + if(lp->orig_lowbo[i] != 0) + if(lp->orig_lowbo[i] == -lp->infinite) + fprintf(output, "%s >= -Inf;\n", get_col_name(lp, i - lp->rows)); + else + fprintf(output, "%s >= %.12g;\n", + get_col_name(lp, i - lp->rows), get_lowbo(lp, i - lp->rows)); + if(lp->orig_upbo[i] != lp->infinite) + fprintf(output, "%s <= %.12g;\n", + get_col_name(lp, i - lp->rows), get_upbo(lp, i - lp->rows)); + } + } + } + + /* Write optional integer section */ + if(lp->int_count > 0) { + write_lpcomment(output, "Integer definitions", TRUE); + i = 1; + while(i <= lp->columns && !is_int(lp, i)) + i++; + if(i <= lp->columns) { + fprintf(output, "int %s", get_col_name(lp, i)); + i++; + for(; i <= lp->columns; i++) + if((!is_splitvar(lp, i)) && (is_int(lp, i))) + fprintf(output, ",%s", get_col_name(lp, i)); + fprintf(output, ";\n"); + } + } + + /* Write optional SEC section */ + if(lp->sc_count > 0) { + write_lpcomment(output, "Semi-continuous variables", TRUE); + i = 1; + while(i <= lp->columns && !is_semicont(lp, i)) + i++; + if(i <= lp->columns) { + fprintf(output, "sec %s", get_col_name(lp, i)); + i++; + for(; i <= lp->columns; i++) + if((!is_splitvar(lp, i)) && (is_semicont(lp, i))) + fprintf(output, ",%s", get_col_name(lp, i)); + fprintf(output, ";\n"); + } + } + + /* Write optional SOS section */ + if(SOS_count(lp) > 0) { + SOSgroup *SOS = lp->SOS; + write_lpcomment(output, "SOS definitions", TRUE); + for(b = 0, i = 0; i < SOS->sos_count; b = SOS->sos_list[i]->priority, i++) { + fprintf(output, "SOS\n%s: ", + (SOS->sos_list[i]->name == NULL) || + (*SOS->sos_list[i]->name==0) ? "SOS" : SOS->sos_list[i]->name); /* formatnumber12((double) lp->sos_list[i]->priority) */ + + for(a = 0.0, j = 1; j <= SOS->sos_list[i]->size; a = SOS->sos_list[i]->weights[j], j++) + if(SOS->sos_list[i]->weights[j] == ++a) + fprintf(output, "%s%s", + (j > 1) ? "," : "", + get_col_name(lp, SOS->sos_list[i]->members[j])); + else + fprintf(output, "%s%s:%.12g", + (j > 1) ? "," : "", + get_col_name(lp, SOS->sos_list[i]->members[j]), + SOS->sos_list[i]->weights[j]); + if(SOS->sos_list[i]->priority == ++b) + fprintf(output, " <= %d;\n", SOS->sos_list[i]->type); + else + fprintf(output, " <= %d:%d;\n", SOS->sos_list[i]->type, SOS->sos_list[i]->priority); + } + } + + ok = TRUE; + + if(filename != NULL) + fclose(output); + return(ok); +} + +MYBOOL LP_writehandle(lprec *lp, FILE *output) +{ + set_outputstream(lp, output); + return(LP_writefile(lp, NULL)); +} Index: src/tools/solver/lp_solve/lp_wlp.h =================================================================== RCS file: src/tools/solver/lp_solve/lp_wlp.h diff -N src/tools/solver/lp_solve/lp_wlp.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_wlp.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,20 @@ +#ifndef HEADER_lp_lp +#define HEADER_lp_lp + +#include "lp_types.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Put function headers here */ +MYBOOL LP_writefile(lprec *lp, char *filename); +MYBOOL LP_writehandle(lprec *lp, FILE *output); + + +#ifdef __cplusplus + } +#endif + +#endif /* HEADER_lp_lp */ Index: src/tools/solver/lp_solve/lp_wlpt.c =================================================================== RCS file: src/tools/solver/lp_solve/lp_wlpt.c diff -N src/tools/solver/lp_solve/lp_wlpt.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/lp_wlpt.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,238 @@ + +#include "lp_lib.h" +#include "lp_utils.h" +#include "lp_wlp.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + +#include + +/* ------------------------------------------------------------------------- */ +/* Input and output of lp format model files for lp_solve */ +/* ------------------------------------------------------------------------- */ + +static void write_lpcomment(FILE *output, char const *string, MYBOOL newlinebefore) +{ + fprintf(output, "%s\\* %s *\\\n", (newlinebefore) ? "\n" : "", string); +} + +static void write_lprow(lprec *lp, int rowno, FILE *output) +{ + int i, ie, j; + REAL a; + MATrec *mat = lp->matA; + + if(rowno == 0) + i = 0; + else + i = mat->row_end[rowno-1]; + ie = mat->row_end[rowno]; + for(; i < ie; i++) { + j = ROW_MAT_COL(mat->row_mat[i]); + if(is_splitvar(lp, j)) + continue; +#if 0 + a = get_mat_byindex(lp, i, TRUE); +#else + a = ROW_MAT_VALUE(mat->row_mat[i]); + a = my_chsign(is_chsign(lp, rowno), a); + a = unscaled_mat(lp, a, rowno, j); +#endif + if(a == -1) + fprintf(output, " -"); + else if(a == 1) + fprintf(output, " +"); + else + fprintf(output, " %+.12g ", (double)a); + fprintf(output, "%s", get_col_name(lp, j)); + } +} + +MYBOOL __EXPORT_TYPE __WINAPI write_lpt(lprec *lp, char *filename) +{ + int i, j, b; + MYBOOL ok = FALSE, ok2; + REAL a, constant_term; + FILE *output = stdout; + char *ptr; + +#ifdef Paranoia + if(lp->matA->is_roworder) { + report(lp, IMPORTANT, "write_lpt: Cannot write to LP file while in row entry mode.\n"); + return(FALSE); + } +#endif + if(!mat_validate(lp->matA)) { + report(lp, IMPORTANT, "write_lpt: Could not validate the data matrix.\n"); + return(FALSE); + } + + ok = (MYBOOL) ((filename == NULL) || ((output = fopen(filename,"w")) != NULL)); + if(!ok) + return(ok); + if(filename == NULL && lp->outstream != NULL) + output = lp->outstream; + + /* Write the objective function */ + write_lpcomment(output, "Objective function", FALSE); + if(is_maxim(lp)) + fprintf(output, "Maximize\n"); + else + fprintf(output, "Minimize\n"); + + if(lp->names_used && (lp->row_name[0] != NULL)) + ptr = get_row_name(lp, 0); + else + ptr = NULL; + if((ptr != NULL) && (*ptr)) + fprintf(output, " %s:", ptr); + write_lprow(lp, 0, output); + constant_term = get_rh(lp, 0); + if(constant_term) + fprintf(output, " +constant_term"); + fprintf(output, "\n"); + + /* Write constraints */ + write_lpcomment(output, "Constraints", TRUE); + fprintf(output, "Subject To\n"); + for(j = 1; j <= lp->rows; j++) { + if(lp->names_used && (lp->row_name[j] != NULL)) + ptr = get_row_name(lp, j); + else + ptr = NULL; + if((ptr != NULL) && (*ptr)) + fprintf(output, " %s:", ptr); + + write_lprow(lp, j, output); + if(lp->orig_upbo[j] == 0) + fprintf(output, " ="); + else if(is_chsign(lp, j)) + fprintf(output, " >="); + else + fprintf(output, " <="); + if(fabs(get_rh(lp, j) + lp->infinite) < 1) + fprintf(output, " -Inf;\n"); + else + fprintf(output, " %.12g\n", get_rh(lp, j)); + + /* Write the ranged part of the constraint, if specified */ + if ((lp->orig_upbo[j]) && (lp->orig_upbo[j] < lp->infinite)) { + if((ptr != NULL) && (*ptr)) + fprintf(output, " %*s ", strlen(ptr), ""); + write_lprow(lp, j, output); + fprintf(output, " %s %g\n", + (is_chsign(lp, j)) ? "<=" : ">=", + (lp->orig_upbo[j]-lp->orig_rh[j]) * (is_chsign(lp, j) ? 1.0 : -1.0) / (lp->scaling_used ? lp->scalars[j] : 1.0)); + } + } + + /* Write bounds on variables */ + ok2 = FALSE; + if(constant_term) { + write_lpcomment(output, "Variable bounds", TRUE); + fprintf(output, "Bounds\n"); + fprintf(output, " constant_term = %.12g\n", constant_term); + ok2 = TRUE; + } + for(i = lp->rows + 1; i <= lp->sum; i++) + if(!is_splitvar(lp, i - lp->rows)) { + if(lp->orig_lowbo[i] == lp->orig_upbo[i]) { + if(!ok2) { + write_lpcomment(output, "Variable bounds", TRUE); + fprintf(output, "Bounds\n"); + ok2 = TRUE; + } + fprintf(output, " %s = %.12g\n", get_col_name(lp, i - lp->rows), get_upbo(lp, i - lp->rows)); + } + else { + if(lp->orig_lowbo[i] != 0) { + if(!ok2) { + write_lpcomment(output, "Variable bounds", TRUE); + fprintf(output, "Bounds\n"); + ok2 = TRUE; + } + if(lp->orig_lowbo[i] == -lp->infinite) + fprintf(output, " %s >= -Inf\n", get_col_name(lp, i - lp->rows)); + else + fprintf(output, " %s >= %.12g\n", get_col_name(lp, i - lp->rows), + (double)lp->orig_lowbo[i] * (lp->scaling_used && (lp->orig_lowbo[i] != -lp->infinite) ? lp->scalars[i] : 1.0)); + } + if(lp->orig_upbo[i] != lp->infinite) { + if(!ok2) { + write_lpcomment(output, "Variable bounds", TRUE); + fprintf(output, "Bounds\n"); + ok2 = TRUE; + } + fprintf(output, " %s <= %.12g\n", get_col_name(lp, i - lp->rows), + (double)lp->orig_upbo[i] * (lp->scaling_used ? lp->scalars[i] : 1.0)); + } + } + } + + /* Write optional integer section */ + if(lp->int_count > 0) { + write_lpcomment(output, "Integer definitions", TRUE); + i = 1; + while(i <= lp->columns && !is_int(lp, i)) + i++; + if(i <= lp->columns) { + fprintf(output, "General\n"); + for(; i <= lp->columns; i++) + if((!is_splitvar(lp, i)) && (is_int(lp, i))) + fprintf(output, " %s", get_col_name(lp, i)); + fprintf(output, "\n"); + } + } + + /* Write optional SEC section */ + if(lp->sc_count > 0) { + write_lpcomment(output, "Semi-continuous variables", TRUE); + i = 1; + while(i <= lp->columns && !is_semicont(lp, i)) + i++; + if(i <= lp->columns) { + fprintf(output, "Semi-continuous\n"); + for(; i <= lp->columns; i++) + if((!is_splitvar(lp, i)) && (is_semicont(lp, i))) + fprintf(output, " %s", get_col_name(lp, i)); + fprintf(output, "\n"); + } + } + + /* Write optional SOS section */ + if(SOS_count(lp) > 0) { + SOSgroup *SOS = lp->SOS; + write_lpcomment(output, "SOS definitions", TRUE); + fprintf(output, "SOS\n"); + for(b = 0, i = 0; i < SOS->sos_count; /* b = lp->sos_list[i]->priority, */ i++) { + fprintf(output, " S%d:: ", SOS->sos_list[i]->type); + + for(a = 0.0, j = 1; j <= SOS->sos_list[i]->size; /* a = lp->sos_list[i]->weights[j], */ j++) + if(SOS->sos_list[i]->weights[j] == ++a) + fprintf(output, "%s%s", + (j > 1) ? " " : "", + get_col_name(lp, SOS->sos_list[i]->members[j])); + else + fprintf(output, "%s%s:%.12g", + (j > 1) ? " " : "", + get_col_name(lp, SOS->sos_list[i]->members[j]), + SOS->sos_list[i]->weights[j]); + fprintf(output, "\n"); + } + } + + fprintf(output, "\nEnd\n"); + ok = TRUE; + + if(filename != NULL) + fclose(output); + return(ok); +} + +MYBOOL __WINAPI write_LPT(lprec *lp, FILE *output) +{ + set_outputstream(lp, output); + return(write_lpt(lp, NULL)); +} Index: src/tools/solver/lp_solve/lpkit.c =================================================================== RCS file: src/tools/solver/lp_solve/lpkit.c diff -N src/tools/solver/lp_solve/lpkit.c --- src/tools/solver/lp_solve/lpkit.c 5 Dec 2002 18:26:32 -0000 1.15 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,762 +0,0 @@ -/* - * lpkit.c: LP SOLVE Toolkit. - * - * Authors: - * Jeroen Dirks (jeroend@tor.numetrix.com) - * Michel Berkelaar (michel@ics.ele.tue.nl) - * Jukka-Pekka Iivonen (jiivonen@hutcs.cs.hut.fi) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License, version 2, as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include -#include -#include "lpkit.h" -#include "lpglob.h" -#include -#include -#include -#include - - -/* Globals */ -int lp_solve_Level; -gnm_float lp_solve_Trej; -gnm_float lp_solve_Extrad; - - -lprec * -lp_solve_make_lp (int rows, int columns) -{ - lprec *newlp; - int i, sum; - - if (rows < 0 || columns < 0) - g_print ("rows < 0 or columns < 0"); - - sum = rows + columns; - - newlp = g_new (lprec, 1); - - strcpy (newlp->lp_name, "unnamed"); - - newlp->verbose = FALSE; - newlp->print_duals = FALSE; - newlp->print_sol = FALSE; - newlp->debug = FALSE; - newlp->print_at_invert = FALSE; - newlp->trace = FALSE; - - newlp->rows = rows; - newlp->columns = columns; - newlp->sum = sum; - newlp->rows_alloc = rows; - newlp->columns_alloc = columns; - newlp->sum_alloc = sum; - newlp->names_used = FALSE; - - newlp->obj_bound = DEF_INFINITE; - newlp->infinite = DEF_INFINITE; - newlp->epsilon = DEF_EPSILON; - newlp->epsb = DEF_EPSB; - newlp->epsd = DEF_EPSD; - newlp->epsel = DEF_EPSEL; - newlp->non_zeros = 0; - newlp->mat_alloc = 1; - newlp->mat = g_new0 (matrec, newlp->mat_alloc); - newlp->col_no = g_new0 (int, newlp->mat_alloc + 1); - newlp->col_end = g_new0 (int, columns + 1); - newlp->row_end = g_new0 (int, rows + 1); - newlp->row_end_valid = FALSE; - newlp->orig_rh = g_new0 (gnm_float, rows + 1); - newlp->rh = g_new0 (gnm_float, rows + 1); - newlp->rhs = g_new0 (gnm_float, rows + 1); - - newlp->must_be_int = g_new0 (char, sum + 1); - for (i = 0; i <= sum; i++) - newlp->must_be_int[i] = FALSE; - - newlp->orig_upbo = g_new0 (gnm_float, sum + 1); - for (i = 0; i <= sum; i++) - newlp->orig_upbo[i] = newlp->infinite; - - newlp->upbo = g_new0 (gnm_float, sum + 1); - newlp->orig_lowbo = g_new0 (gnm_float, sum + 1); - newlp->lowbo = g_new0 (gnm_float, sum + 1); - - newlp->basis_valid = TRUE; - newlp->bas = g_new0 (int, rows+1); - newlp->basis = g_new0 (char, sum + 1); - newlp->lower = g_new0 (char, sum + 1); - - for (i = 0; i <= rows; i++) { - newlp->bas[i] = i; - newlp->basis[i] = TRUE; - } - - for (i = rows + 1; i <= sum; i++) - newlp->basis[i] = FALSE; - - - for (i = 0 ; i <= sum; i++) - newlp->lower[i] = TRUE; - - newlp->eta_valid = TRUE; - newlp->eta_size = 0; - newlp->eta_alloc = INITIAL_MAT_SIZE; - newlp->max_num_inv = DEFNUMINV; - - newlp->nr_lagrange = 0; - - newlp->eta_value = g_new0 (gnm_float, newlp->eta_alloc); - newlp->eta_row_nr = g_new0 (int, newlp->eta_alloc); - - /* +1 reported by Christian Rank */ - newlp->eta_col_end = g_new0 (int, newlp->rows_alloc + - newlp->max_num_inv + 1); - - newlp->bb_rule = FIRST_NI; - newlp->break_at_int = FALSE; - newlp->break_value = 0; - - newlp->iter = 0; - newlp->total_iter = 0; - newlp->max_total_iter = 1000; /* Default */ - - newlp->solution = g_new0 (gnm_float, sum + 1); - newlp->best_solution = g_new0 (gnm_float, sum + 1); - newlp->duals = g_new0 (gnm_float, rows + 1); - - newlp->maximise = FALSE; - newlp->floor_first = TRUE; - - newlp->scaling_used = FALSE; - newlp->columns_scaled = FALSE; - - newlp->ch_sign = g_new0 (char, rows + 1); - for (i = 0; i <= rows; i++) - newlp->ch_sign[i] = FALSE; - - newlp->valid = FALSE; - - /* create two hash tables for names */ - - return (newlp); -} - -void -lp_solve_delete_lp (lprec *lp) -{ - int i; - - if (lp->names_used) { - g_free (lp->row_name); - g_free (lp->col_name); - } - - g_free (lp->mat); - g_free (lp->col_no); - g_free (lp->col_end); - g_free (lp->row_end); - g_free (lp->orig_rh); - g_free (lp->rh); - g_free (lp->rhs); - g_free (lp->must_be_int); - g_free (lp->orig_upbo); - g_free (lp->orig_lowbo); - g_free (lp->upbo); - g_free (lp->lowbo); - g_free (lp->bas); - g_free (lp->basis); - g_free (lp->lower); - g_free (lp->eta_value); - g_free (lp->eta_row_nr); - g_free (lp->eta_col_end); - g_free (lp->solution); - g_free (lp->best_solution); - g_free (lp->duals); - g_free (lp->ch_sign); - if (lp->scaling_used) - g_free (lp->scale); - if (lp->nr_lagrange > 0) { - g_free (lp->lag_rhs); - g_free (lp->lambda); - g_free (lp->lag_con_type); - for (i = 0; i < lp->nr_lagrange; i++) - g_free (lp->lag_row[i]); - g_free (lp->lag_row); - } - - g_free (lp); -} - -static void -inc_mat_space (lprec *lp, int maxextra) -{ - if (lp->non_zeros + maxextra >= lp->mat_alloc) { - /* let's allocate at least INITIAL_MAT_SIZE entries */ - if (lp->mat_alloc < INITIAL_MAT_SIZE) - lp->mat_alloc = INITIAL_MAT_SIZE; - - /* increase the size by 50% each time it becomes too small */ - while (lp->non_zeros + maxextra >= lp->mat_alloc) - lp->mat_alloc *= 1.5; - - lp->mat = g_renew (matrec, lp->mat, lp->mat_alloc); - lp->col_no = g_renew (int, lp->col_no, lp->mat_alloc + 1); - } -} - -void -lp_solve_set_max_iter (lprec *lp, int max) -{ - lp->max_total_iter = max; -} - -void -lp_solve_set_max_time (lprec *lp, int max, gnm_float st) -{ - lp->max_time = max; - lp->start_time = st; -} - -void -lp_solve_set_mat (lprec *lp, int Row, int Column, gnm_float Value) -{ - int elmnr, lastelm, i; - - /* This function is very inefficient if used to add new matrix - * entries in other places than at the end of the matrix. OK for - * replacing existing non-zero values */ - - if (Row > lp->rows || Row < 0) - g_print ("Row out of range"); - if (Column > lp->columns || Column < 1) - g_print ("Column out of range"); - - /* scaling is performed twice? MB */ - if (lp->scaling_used) - Value *= lp->scale[Row] * lp->scale[lp->rows + Column]; - - if (lp->basis[Column] == TRUE && Row > 0) - lp->basis_valid = FALSE; - lp->eta_valid = FALSE; - - /* find out if we already have such an entry */ - elmnr = lp->col_end[Column - 1]; - while ((elmnr < lp->col_end[Column]) && (lp->mat[elmnr].row_nr != Row)) - elmnr++; - - if ((elmnr != lp->col_end[Column]) && (lp->mat[elmnr].row_nr == Row)) { - /* there is an existing entry */ - if (ABS (Value) > lp->epsilon) { - /* we replace it by something non-zero */ - if (lp->scaling_used) { - if (lp->ch_sign[Row]) - lp->mat[elmnr].value = -Value * - lp->scale[Row] * lp->scale[Column]; - else - lp->mat[elmnr].value = Value * - lp->scale[Row] * lp->scale[Column]; - } - else { /* no scaling */ - if (lp->ch_sign[Row]) - lp->mat[elmnr].value = -Value; - else - lp->mat[elmnr].value = Value; - } - } - else { /* setting existing non-zero entry to zero. Remove - * the entry */ - - /* this might remove an entire column, or leave - * just a bound. No nice solution for that yet */ - - /* Shift the matrix */ - lastelm = lp->non_zeros; - for (i = elmnr; i < lastelm ; i++) - lp->mat[i] = lp->mat[i + 1]; - for (i = Column; i <= lp->columns; i++) - lp->col_end[i]--; - - lp->non_zeros--; - } - } - else if (ABS (Value) > lp->epsilon) { - /* no existing entry. make new one only if not nearly zero */ - /* check if more space is needed for matrix */ - inc_mat_space (lp, 1); - - /* Shift the matrix */ - lastelm = lp->non_zeros; - for (i = lastelm; i > elmnr ; i--) - lp->mat[i] = lp->mat[i - 1]; - for (i = Column; i <= lp->columns; i++) - lp->col_end[i]++; - - /* Set new element */ - lp->mat[elmnr].row_nr = Row; - - if (lp->scaling_used) { - if (lp->ch_sign[Row]) - lp->mat[elmnr].value = -Value * - lp->scale[Row] * lp->scale[Column]; - else - lp->mat[elmnr].value = Value * lp->scale[Row] * - lp->scale[Column]; - } - else { /* no scaling */ - if (lp->ch_sign[Row]) - lp->mat[elmnr].value = -Value; - else - lp->mat[elmnr].value = Value; - } - - lp->row_end_valid = FALSE; - - lp->non_zeros++; - } -} - -void -lp_solve_set_upbo (lprec *lp, int column, gnm_float value) -{ - if (column > lp->columns || column < 1) - g_print ("Column out of range"); - if (lp->scaling_used) - value /= lp->scale[lp->rows + column]; - if (value < lp->orig_lowbo[lp->rows + column]) - g_print ("Upperbound must be >= lowerbound"); - lp->eta_valid = FALSE; - lp->orig_upbo[lp->rows + column] = value; -} - -void -lp_solve_set_lowbo (lprec *lp, int column, gnm_float value) -{ - if (column > lp->columns || column < 1) - g_print ("Column out of range"); - if (lp->scaling_used) - value /= lp->scale[lp->rows + column]; - if (value > lp->orig_upbo[lp->rows + column]) - g_print ("Upperbound must be >= lowerbound"); - /* - if (value < 0) - g_print ("Lower bound cannot be < 0"); - */ - lp->eta_valid = FALSE; - lp->orig_lowbo[lp->rows + column] = value; -} - -void -lp_solve_set_int (lprec *lp, int column, gboolean must_be_int) -{ - if (column > lp->columns || column < 1) - g_print ("Column out of range"); - lp->must_be_int[lp->rows + column] = must_be_int; - if (must_be_int == TRUE) - if (lp->columns_scaled) - lp_solve_unscale_columns (lp); -} - -void -lp_solve_set_rh (lprec *lp, int row, gnm_float value) -{ - if (row > lp->rows || row < 0) - g_print ("Row out of Range"); - - if ((row == 0) && (!lp->maximise)) /* setting of RHS of OF IS - * meaningful */ - value = -value; - if (lp->scaling_used) { - if (lp->ch_sign[row]) - lp->orig_rh[row] = -value * lp->scale[row]; - else - lp->orig_rh[row] = value * lp->scale[row]; - } - else - if (lp->ch_sign[row]) - lp->orig_rh[row] = -value; - else - lp->orig_rh[row] = value; - lp->eta_valid = FALSE; -} - -void -lp_solve_set_maxim (lprec *lp) -{ - int i; - if (lp->maximise == FALSE) { - for (i = 0; i < lp->non_zeros; i++) - if (lp->mat[i].row_nr == 0) - lp->mat[i].value *= -1; - lp->eta_valid = FALSE; - lp->orig_rh[0] *= -1; - } - lp->maximise = TRUE; - lp->ch_sign[0] = TRUE; -} - -void -lp_solve_set_minim (lprec *lp) -{ - int i; - if (lp->maximise == TRUE) { - for (i = 0; i < lp->non_zeros; i++) - if (lp->mat[i].row_nr == 0) - lp->mat[i].value = -lp->mat[i].value; - lp->eta_valid = FALSE; - lp->orig_rh[0] *= -1; - } - lp->maximise = FALSE; - lp->ch_sign[0] = FALSE; -} - -void -lp_solve_set_constr_type (lprec *lp, int row, SolverConstraintType con_type) -{ - int i; - if (row > lp->rows || row < 1) - g_print ("Row out of Range"); - if (con_type == SolverEQ) { - lp->orig_upbo[row] = 0; - lp->basis_valid = FALSE; - if (lp->ch_sign[row]) { - for (i = 0; i < lp->non_zeros; i++) - if (lp->mat[i].row_nr == row) - lp->mat[i].value *= -1; - lp->eta_valid = FALSE; - lp->ch_sign[row] = FALSE; - if (lp->orig_rh[row] != 0) - lp->orig_rh[row] *= -1; - } - } else if (con_type == SolverLE) { - lp->orig_upbo[row] = lp->infinite; - lp->basis_valid = FALSE; - if (lp->ch_sign[row]) { - for (i = 0; i < lp->non_zeros; i++) - if (lp->mat[i].row_nr == row) - lp->mat[i].value *= -1; - lp->eta_valid = FALSE; - lp->ch_sign[row] = FALSE; - if (lp->orig_rh[row] != 0) - lp->orig_rh[row] *= -1; - } - } else if (con_type == SolverGE) { - lp->orig_upbo[row] = lp->infinite; - lp->basis_valid = FALSE; - if (!lp->ch_sign[row]) { - for (i = 0; i < lp->non_zeros; i++) - if (lp->mat[i].row_nr == row) - lp->mat[i].value *= -1; - lp->eta_valid = FALSE; - lp->ch_sign[row] = TRUE; - if (lp->orig_rh[row] != 0) - lp->orig_rh[row] *= -1; - } - } else - g_print ("Constraint type not (yet) implemented"); -} - -/* This is useful for debugging. */ -void -lp_solve_print_lp (lprec *lp) -{ - int i, j; - gnm_float *fatmat; - - fatmat = g_new (gnm_float, (lp->rows + 1) * lp->columns); - for (i = 1; i <= lp->columns; i++) - for (j = lp->col_end[i - 1]; j < lp->col_end[i]; j++) - fatmat[(i - 1) * (lp->rows + 1) + lp->mat[j].row_nr] = - lp->mat[j].value; - - printf ("problem name: %s\n", lp->lp_name); - printf (" "); - for (j = 1; j <= lp->columns; j++) - if (lp->names_used) - printf ("%8s ", lp->col_name[j]); - else - printf ("Var[%3d] ", j); - if (lp->maximise) { - printf ("\nMaximise "); - for (j = 0; j < lp->columns; j++) - printf ("%8g ", (double) -fatmat[j * (lp->rows + 1)]); - } else { - printf ("\nMinimize "); - for (j = 0; j < lp->columns; j++) - printf ("%8g ", (double) fatmat[j * (lp->rows + 1)]); - } - printf ("\n"); - for (i = 1; i <= lp->rows; i++) { - if (lp->names_used) - printf ("%-9s ", lp->row_name[i]); - else - printf ("Row[%3d] ", i); - for (j = 0; j < lp->columns; j++) - if (lp->ch_sign[i] && fatmat[j*(lp->rows+1) + i] != 0) - printf ("%8g ", - (double)-fatmat[j * (lp->rows+1) + i]); - else - printf ("%8g ", - (double)fatmat[j * (lp->rows + 1) + i]); - if (lp->orig_upbo[i] != 0) { - if (lp->ch_sign[i]) - printf (">= "); - else - printf ("<= "); - } else - printf (" = "); - if (lp->ch_sign[i]) - printf ("%8g", (double)-lp->orig_rh[i]); - else - printf ("%8g", (double)lp->orig_rh[i]); - if (lp->orig_lowbo[i] != 0) { - printf (" %s = %8g", (lp->ch_sign[i]) ? "lowbo" : - "upbo", (double)lp->orig_lowbo[i]); - } - if ((lp->orig_upbo[i] != lp->infinite) - && (lp->orig_upbo[i] != 0.0)) { - printf (" %s = %8g", (lp->ch_sign[i]) ? "upbo" : - "lowbo", (double)lp->orig_upbo[i]); - } - printf ("\n"); - } - printf ("Type "); - for (i = 1; i <= lp->columns; i++) - if (lp->must_be_int[lp->rows + i] == TRUE) - printf (" Int "); - else - printf (" Real "); - printf ("\nupbo "); - for (i = 1; i <= lp->columns; i++) - if (lp->orig_upbo[lp->rows + i] == lp->infinite) - printf (" Infinite"); - else - printf ("%8g ", (double)lp->orig_upbo[lp->rows + i]); - printf ("\nlowbo "); - for (i = 1; i <= lp->columns; i++) - printf ("%8g ", (double)lp->orig_lowbo[lp->rows + i]); - printf ("\n"); - for (i = 0; i < lp->nr_lagrange; i++) { - printf ("lag[%d] ", i); - for (j = 1; j <= lp->columns; j++) - printf ("%8g ", (double)lp->lag_row[i][j]); - if (lp->orig_upbo[i] == lp->infinite) { - if (lp->lag_con_type[i] == SolverGE) - printf (">= "); - else if (lp->lag_con_type[i] == SolverLE) - printf ("<= "); - else if (lp->lag_con_type[i] == SolverEQ) - printf (" = "); - } - printf ("%8g\n", (double)lp->lag_rhs[i]); - } - - g_free (fatmat); -} - -static gnm_float -minmax_to_scale (gnm_float min, gnm_float max) -{ - gnm_float scale; - - /* should do something sensible when min or max is 0, MB */ - if ((min == 0) || (max == 0)) - return 1; - - /* scale = 1 / pow (10, (log10(min) + log10(max)) / 2); */ - /* Jon van Reet noticed: can be simplified to: */ - scale = 1 / sqrtgnum (min * max); - - return scale; -} - -void -lp_solve_unscale_columns (lprec *lp) -{ - int i, j; - - /* unscale mat */ - for (j = 1; j <= lp->columns; j++) - for (i = lp->col_end[j - 1]; i < lp->col_end[j]; i++) - lp->mat[i].value /= lp->scale[lp->rows + j]; - - /* unscale bounds as well */ - for (i = lp->rows + 1; i <= lp->sum; i++) { /* was <, changed by PN */ - if (lp->orig_lowbo[i] != 0) - lp->orig_lowbo[i] *= lp->scale[i]; - if (lp->orig_upbo[i] != lp->infinite) - lp->orig_upbo[i] *= lp->scale[i]; - } - - for (i = lp->rows + 1; i<= lp->sum; i++) - lp->scale[i] = 1; - lp->columns_scaled = FALSE; - lp->eta_valid = FALSE; -} - -void -lp_solve_auto_scale (lprec *lp) -{ - int i, j, row_nr; - gnm_float *row_max, *row_min, *scalechange, absval; - gnm_float col_max, col_min; - - if (!lp->scaling_used) { - lp->scale = g_new (gnm_float, lp->sum_alloc + 1); - for (i = 0; i <= lp->sum; i++) - lp->scale[i] = 1; - } - - row_max = g_new (gnm_float, lp->rows + 1); - row_min = g_new (gnm_float, lp->rows + 1); - scalechange = g_new (gnm_float, lp->sum + 1); - - /* initialise min and max values */ - for (i = 0; i <= lp->rows; i++) { - row_max[i] = 0; - row_min[i] = lp->infinite; - } - - /* calculate min and max absolute values of rows */ - for (j = 1; j <= lp->columns; j++) - for (i = lp->col_end[j - 1]; i < lp->col_end[j]; i++) { - row_nr = lp->mat[i].row_nr; - absval = ABS (lp->mat[i].value); - if (absval != 0) { - row_max[row_nr] = MAX (row_max[row_nr], - absval); - row_min[row_nr] = MIN (row_min[row_nr], - absval); - } - } - /* calculate scale factors for rows */ - for (i = 0; i <= lp->rows; i++) { - scalechange[i] = minmax_to_scale (row_min[i], row_max[i]); - lp->scale[i] *= scalechange[i]; - } - - /* now actually scale the matrix */ - for (j = 1; j <= lp->columns; j++) - for (i = lp->col_end[j - 1]; i < lp->col_end[j]; i++) - lp->mat[i].value *= scalechange[lp->mat[i].row_nr]; - - /* and scale the rhs and the row bounds (RANGES in MPS!!) */ - for (i = 0; i <= lp->rows; i++) { - lp->orig_rh[i] *= scalechange[i]; - - if ((lp->orig_upbo[i] < lp->infinite) - && (lp->orig_upbo[i] != 0)) - lp->orig_upbo[i] *= scalechange[i]; - - if (lp->orig_lowbo[i] != 0) - lp->orig_lowbo[i] *= scalechange[i]; - } - - g_free (row_max); - g_free (row_min); - - /* calculate column scales */ - for (j = 1; j <= lp->columns; j++) { - if (lp->must_be_int[lp->rows + j]) { - /* do not scale integer columns */ - scalechange[lp->rows + j] = 1; - } else { - col_max = 0; - col_min = lp->infinite; - for (i = lp->col_end[j - 1]; i < lp->col_end[j]; i++) { - if (lp->mat[i].value != 0) { - col_max = MAX (col_max, - ABS (lp->mat[i].value)); - col_min = MIN (col_min, - ABS (lp->mat[i].value)); - } - } - scalechange[lp->rows + j] = minmax_to_scale (col_min, - col_max); - lp->scale[lp->rows + j] *= scalechange[lp->rows + j]; - } - } - - /* scale mat */ - for (j = 1; j <= lp->columns; j++) - for (i = lp->col_end[j - 1]; i < lp->col_end[j]; i++) - lp->mat[i].value *= scalechange[lp->rows + j]; - - /* scale bounds as well */ - for (i = lp->rows + 1; i <= lp->sum; i++) { /* was <, changed by PN */ - if (lp->orig_lowbo[i] != 0) - lp->orig_lowbo[i] /= scalechange[i]; - if (lp->orig_upbo[i] != lp->infinite) - lp->orig_upbo[i] /= scalechange[i]; - } - lp->columns_scaled = TRUE; - - g_free (scalechange); - lp->scaling_used = TRUE; - lp->eta_valid = FALSE; -} - -/* This is useful for debugging. */ -void -lp_solve_print_solution (lprec *lp) -{ - int i; - FILE *stream; - - stream = stdout; - - fprintf (stream, "Value of objective function: %g\n", - (double)lp->best_solution[0]); - - /* print normal variables */ - for (i = 1; i <= lp->columns; i++) - if (lp->names_used) - fprintf (stream, "%-20s %g\n", lp->col_name[i], - (double)lp->best_solution[lp->rows + i]); - else - fprintf (stream, "Var [%d] %g\n", i, - (double)lp->best_solution[lp->rows + i]); - - /* print achieved constraint values */ - if (lp->verbose) { - fprintf (stream, "\nActual values of the constraints:\n"); - for (i = 1; i <= lp->rows; i++) - if (lp->names_used) - fprintf (stream, "%-20s %g\n", lp->row_name[i], - (double)lp->best_solution[i]); - else - fprintf (stream, "Row [%d] %g\n", i, - (double)lp->best_solution[i]); - } - - if ((lp->verbose || lp->print_duals)) { - if (lp->max_level != 1) - fprintf (stream, - "These are the duals from the node that " - "gave the optimal solution.\n"); - else - fprintf (stream, "\nDual values:\n"); - for (i = 1; i <= lp->rows; i++) - if (lp->names_used) - fprintf (stream, "%-20s %g\n", lp->row_name[i], - (double)lp->duals[i]); - else - fprintf (stream, "Row [%d] %g\n", i, - (double)lp->duals[i]); - } - fflush (stream); -} Index: src/tools/solver/lp_solve/lpkit.h =================================================================== RCS file: /cvs/gnome/gnumeric/src/tools/solver/lp_solve/lpkit.h,v retrieving revision 1.16 retrieving revision 1.17 diff -u -w -r1.16 -r1.17 --- src/tools/solver/lp_solve/lpkit.h 5 Dec 2002 18:26:32 -0000 1.16 +++ src/tools/solver/lp_solve/lpkit.h 24 Jul 2004 02:52:40 -0000 1.17 @@ -1,380 +1,36 @@ -/* - * Main header file of the LP_SOLVE toolkit. - * - * Original by Jeroen Dirks, 21-2-95 - * Maintained by Michel Berkelaar - * - * Glib & Gnumeric bindings by Jukka-Pekka Iivonen, 2002 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License, version 2, as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ +#include "lp_lib.h" -#ifndef __SOLVER_LPKIT_H__ -#define __SOLVER_LPKIT_H__ - -#include -#include "numbers.h" -#include "solver.h" -#include -#include -#include -#include - - -#define DEFNUMINV 50 -#define INITIAL_MAT_SIZE 10000 - -/* lag_solve extra status values */ -#define FEAS_FOUND 6 -#define NO_FEAS_FOUND 7 -#define BREAK_BB 8 - -#define FIRST_NI 0 -#define RAND_NI 1 - - -/* FIXME: use Gnumeric's round instead */ -#ifdef CHECK -#define my_round(val, eps) { \ - gnm_float absv; \ - absv = ((val) < 0 ? -(val) : (val)); \ - if(absv < (eps)) \ - val = 0; \ - if(Warn_count < MAX_WARN_COUNT) \ - { \ - if(absv > 0.5 * (eps) && absv < 2 * (eps)) \ - { \ - Warn_count++; \ - fprintf(stderr, \ - "Warning Value close to epsilon V: %e E: %e\n", \ - (double)absv, (double)(eps)); \ - if(Warn_count == MAX_WARN_COUNT) \ - { \ - fprintf(stderr, \ - "*** Surpressing further rounding warnings\n"); \ - } \ - } \ - } \ -} - -#else -#define my_round(val,eps) if (((val) < 0 ? -(val) : (val)) < (eps)) val = 0; -#endif - - -#define DEF_INFINITE 1e24 /* limit for dynamic range */ -#define DEF_EPSB 5.01e-7 /* for rounding RHS values to 0 determine - infeasibility basis */ -#define DEF_EPSEL 1e-8 /* for rounding other values (vectors) to 0 */ -#define DEF_EPSD 1e-6 /* for rounding reduced costs to zero */ -#define DEF_EPSILON 1e-3 /* to determine if a float value is integer */ - -#define PREJ 1e-3 /* pivot reject (try others first) */ - -#define ETA_START_SIZE 10000 /* start size of array Eta. Realloced if needed */ #define NAMELEN 25 #define MAXSTRL (NAMELEN-1) +#define MALLOC(ptr, nr, type)\ + ((((nr) == 0) || ((ptr = (type *) malloc((size_t)((nr) * sizeof(*ptr)))) == NULL)) ? \ + report(NULL, CRITICAL, "malloc of %d bytes failed on line %d of file %s\n",\ + (nr) * sizeof(*ptr), __LINE__, __FILE__), (ptr = NULL /* (void *) 0 */) : \ + ptr\ + ) + +#define CALLOC(ptr, nr, type)\ + ((((nr) == 0) || ((ptr = (type *) calloc((size_t)(nr), sizeof(*ptr))) == NULL)) ? \ + report(NULL, CRITICAL, "calloc of %d bytes failed on line %d of file %s\n",\ + (nr) * sizeof(*ptr), __LINE__, __FILE__), (ptr = NULL /* (void *) 0 */) : \ + ptr\ + ) + +#define REALLOC(ptr, nr, type)\ + ((((nr) == 0) || ((ptr = (type *) realloc(ptr, (size_t)((nr) * sizeof(*ptr)))) == NULL)) ? \ + report(NULL, CRITICAL, "realloc of %d bytes failed on line %d of file %s\n",\ + (nr) * sizeof(*ptr), __LINE__, __FILE__), (ptr = NULL /* (void *) 0 */) : \ + ptr\ + ) -typedef char nstring[NAMELEN]; - -typedef struct _matrec -{ - int row_nr; - gnm_float value; -} matrec; - -typedef struct _column -{ - int row; - gnm_float value; - struct _column *next ; -} column; - -typedef struct _constraint_name -{ - char name[NAMELEN]; - int row; - struct _constraint_name *next; -} constraint_name; - -typedef struct _bound -{ - gnm_float upbo; - gnm_float lowbo; -} bound; - -typedef struct _tmp_store_struct -{ - nstring name; - int row; - gnm_float value; - gnm_float rhs_value; - short relat; -} tmp_store_struct; - -typedef struct _rside /* contains relational operator and rhs value */ -{ - gnm_float value; - struct _rside *next; - short relat; -} rside; - - -/* fields indicated with ## may be modified directly */ -/* pointers will have their array size in the comments */ - -typedef struct _lprec -{ - nstring lp_name; /* the name of the lp */ - - gboolean verbose; /* ## Verbose flag */ - gboolean print_duals; /* ## PrintDuals flag for PrintSolution - */ - gboolean print_sol; /* ## used in lp_solve */ - gboolean debug; /* ## Print B&B information */ - gboolean print_at_invert; /* ## Print information at every - * reinversion */ - gboolean trace; /* ## Print information on pivot - * selection */ - gboolean anti_degen; /* ## Do perturbations */ - - int rows; /* Nr of constraint rows in the problem - */ - int rows_alloc; /* The allocated memory for Rows sized - * data */ - int columns; /* The number of columns (= variables) */ - int columns_alloc; - int sum; /* The size of the variables + the - * slacks */ - int sum_alloc; - - gboolean names_used; /* Flag to indicate if names for rows and - * columns are used */ - nstring *row_name; /* rows_alloc+1 */ - nstring *col_name; /* columns_alloc+1 */ - - /* Row[0] of the sparce matrix is the objective function */ - - int non_zeros; /* The number of elements in the sparce - * matrix*/ - int mat_alloc; /* The allocated size for matrix sized - * structures */ - matrec *mat; /* mat_alloc :The sparse matrix */ - int *col_end; /* columns_alloc+1 :Cend[i] is the index - * of the first element after column i. - * column[i] is stored in elements - * col_end[i-1] to col_end[i]-1 */ - int *col_no; /* mat_alloc :From Row 1 on, col_no - * contains the column nr. of the - * nonzero elements, row by row */ - gboolean row_end_valid; /* true if row_end & col_no are valid */ - int *row_end; /* rows_alloc+1 :row_end[i] is the index - * of the first element in Colno after - * row i */ - gnm_float *orig_rh; /* rows_alloc+1 :The RHS after scaling & - * sign changing, but before `Bound - * transformation' */ - gnm_float *rh; /* rows_alloc+1 :As orig_rh, but after - * Bound transformation */ - gnm_float *rhs; /* rows_alloc+1 :The RHS of the current - * simplex tableau */ - char *must_be_int; /* sum_alloc+1 :TRUE if variable must be - * Integer */ - gnm_float *orig_upbo; /* sum_alloc+1 :Bound before - * transformations */ - gnm_float *orig_lowbo; /* " " */ - gnm_float *upbo; /* " " :Upper bound after - * transformation & B&B work */ - gnm_float *lowbo; /* " " :Lower bound after - * transformation & B&B work */ - - gboolean basis_valid; /* TRUE is the basis is still valid */ - int *bas; /* rows_alloc+1 :The basis column list */ - char *basis; /* sum_alloc+1 : basis[i] is TRUE if - * the column is in the basis */ - char *lower; /* " " :TRUE is the variable is - * at its lower bound (or in the basis), - * it is FALSE if the variable is at - * its upper bound */ - - gboolean eta_valid; /* TRUE if current Eta structures are - * valid */ - int eta_alloc; /* The allocated memory for Eta */ - int eta_size; /* The number of Eta columns */ - int num_inv; /* The number of real pivots */ - int max_num_inv; /* ## The number of real pivots between - * reinversions */ - gnm_float *eta_value; /* eta_alloc :The Structure containing - * the values of Eta */ - int *eta_row_nr; /* " " :The Structure containing - * the Row indexes of Eta */ - int *eta_col_end; /* rows_alloc + MaxNumInv : - * eta_col_end[i] is the start index of - * the next Eta column */ - - short bb_rule; /* what rule for selecting B&B variables - */ - - gboolean break_at_int; /* TRUE if stop at first integer better - * than break_value */ - gnm_float break_value; - - gnm_float obj_bound; /* ## Objective function bound for - * speedup of B&B */ - int iter; /* The number of iterations in the - * simplex solver (LP) */ - int total_iter; /* The total number of iterations (B&B) - * (ILP) */ - int max_total_iter; /* The maximum total number of iterations (B&B) - * (ILP) */ - int max_time; /* The maximum time (sec.) before timeout. */ - gnm_float start_time; /* The starting time of the solving. */ - int max_level; /* The Deepest B&B level of the last - * solution */ - int total_nodes; /* total number of nodes processed in - * b&b */ - gnm_float *solution; /* sum_alloc+1 :The Solution of the - * last LP, - * 0 = The Optimal Value, - * 1..rows The Slacks, - * rows+1..sum The Variables */ - gnm_float *best_solution; /* " " :The Best 'Integer' - * Solution */ - gnm_float *duals; /* rows_alloc+1 :The dual variables of - * the last LP */ - - gboolean maximise; /* TRUE if the goal is to maximise the - * objective function */ - gboolean floor_first; /* TRUE if B&B does floor bound first */ - char *ch_sign; /* rows_alloc+1 :TRUE if the Row in the - * matrix has changed sign - * (a`x > b, x>=0) is translated to - * s + -a`x = -b with x>=0, s>=0) */ - - gboolean scaling_used; /* TRUE if scaling is used */ - gboolean columns_scaled; /* TRUE is the columns are scaled too, - * Only use if all variables are - * non-integer */ - gnm_float *scale; /* sum_alloc+1:0..Rows the scaling of - * the Rows, Rows+1..Sum the scaling - * of the columns */ - - int nr_lagrange; /* Nr. of Langrangian relaxation - * constraints */ - gnm_float **lag_row; /* NumLagrange, columns+1:Pointer to - * pointer of rows */ - gnm_float *lag_rhs; /* NumLagrange :Pointer to pointer of - * Rhs */ - gnm_float *lambda; /* NumLagrange :Lambda Values */ - - SolverConstraintType - *lag_con_type; /* NumLagrange :TRUE if constraint type - * SolverEQ */ - gnm_float lag_bound; /* the lagrangian lower bound */ - - gboolean valid; /* Has this lp pased the 'test' */ - gnm_float infinite; /* ## numerical stuff */ - gnm_float epsilon; /* ## */ - gnm_float epsb; /* ## */ - gnm_float epsd; /* ## */ - gnm_float epsel; /* ## */ -} lprec; - - - -/* function interface for the user */ - -lprec *lp_solve_make_lp (int rows, int columns); -/* create and initialise a lprec structure - defaults: - Empty (Rows * Columns) matrix, - Minimise the objective function - constraints all type <= - Upperbounds all Infinite - no integer variables - floor first in B&B - no scaling - default basis */ - -void lp_solve_delete_lp (lprec *lp); -/* Remove problem from memory */ - -void lp_solve_set_max_iter (lprec *lp, int max); -/* Set the maximum number of iterations. */ - -void lp_solve_set_max_time (lprec *lp, int max, gnm_float st); -/* Set the maximum time. */ - -void lp_solve_set_mat (lprec *lp, int row, int column, gnm_float value); -/* fill in element (Row,Column) of the matrix - Row in [0..Rows] and Column in [1..Columns] */ - -void lp_solve_set_obj_fn (lprec *lp, int col, gnm_float value); -/* set the objective function of the matrix */ - -void lp_solve_set_upbo (lprec *lp, int column, gnm_float value); -/* Set the upperbound of a variable */ - -void lp_solve_set_lowbo (lprec *lp, int column, gnm_float value); -/* Set the lowerbound of a variable */ - -void lp_solve_set_int (lprec *lp, int column, gboolean must_be_int); -/* Set the type of variable, if must_be_int = TRUE then the variable must - * be integer */ - -void lp_solve_set_rh (lprec *lp, int row, gnm_float value); -/* Set the right hand side of a constraint row */ - -void lp_solve_set_maxim (lprec *lp); -/* maximise the objective function */ - -void lp_solve_set_minim (lprec *lp); -/* minimise the objective function */ - -void lp_solve_set_constr_type (lprec *lp, int row, - SolverConstraintType con_type); -/* Set the type of constraint in row Row (SolverLE, SolverGE, SolverEQ) */ - -void lp_solve_auto_scale (lprec *lp); -/* Automatic scaling of the problem */ - -SolverStatus lp_solve_solve (lprec *lp); -/* Solve the problem */ - -void lp_solve_print_lp (lprec *lp); -/* Print the current problem, only usefull in very small (test) problems. - * Shows the effect of scaling */ - -void lp_solve_print_solution (lprec *lp); -/* Print the solution to stdout */ - -gnm_float lp_solve_get_solution (lprec *lp, int column); -/* returns the value of a variable at the given column */ - -gnm_float lp_solve_get_value_of_obj_fn (lprec *lp); -/* returns the value of the objective function. */ - -gnm_float lp_solve_get_dual (lprec *lp, int row); -/* returns the value of a dual. */ +#if defined FREE +# undef FREE +#endif -/* functions used internaly by the lp toolkit */ -void lp_solve_unscale_columns (lprec *lp); -void btran (lprec *lp, gnm_float *row); -void invert (lprec *lp); -void presolve (lprec *lp); +#define FREE(ptr) if (ptr != NULL) {free(ptr), ptr = NULL;} else -#endif +#define MALLOCCPY(nptr, optr, nr, type)\ + (MALLOC(nptr, nr, type), (nptr != NULL) ? memcpy(nptr, optr, (size_t)((nr) * sizeof(*optr))) : 0, nptr) +typedef char nstring[NAMELEN]; Index: src/tools/solver/lp_solve/solve.c =================================================================== RCS file: src/tools/solver/lp_solve/solve.c diff -N src/tools/solver/lp_solve/solve.c --- src/tools/solver/lp_solve/solve.c 5 Dec 2002 18:26:32 -0000 1.18 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,1759 +0,0 @@ -/* - * solve.c: Simplex algorithm. - * - * Authors: - * Michel Berkelaar (michel@ics.ele.tue.nl) - * Jeroen Dirks (jeroend@tor.numetrix.com) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License, version 2, as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include -#include -#include -#include "lpkit.h" -#include "lpglob.h" -#include "lp-solve-debug.h" - -/* Globals used by solver */ -static gboolean JustInverted; -static SolverStatus Status; -static gboolean Doiter; -static gboolean DoInvert; -static gboolean Break_bb; - - -static void -ftran (lprec *lp, gnm_float *pcol) -{ - int i, j, k, r, *rowp; - gnm_float theta, *valuep; - - for (i = 1; i <= lp->eta_size; i++) { - k = lp->eta_col_end[i] - 1; - r = lp->eta_row_nr[k]; - theta = pcol[r]; - if (theta != 0) { - j = lp->eta_col_end[i - 1]; - - /* CPU intensive loop, let's do pointer arithmetic */ - for (rowp = lp->eta_row_nr + j, - valuep = lp->eta_value + j; - j < k; j++, rowp++, valuep++) - pcol[*rowp] += theta * *valuep; - - pcol[r] *= lp->eta_value[k]; - } - } - - /* round small values to zero */ - for (i = 0; i <= lp->rows; i++) - my_round(pcol[i], lp->epsel); -} /* ftran */ - - -void -btran (lprec *lp, gnm_float *row) -{ - int i, j, k, *rowp; - gnm_float f, *valuep; - - for (i = lp->eta_size; i >= 1; i--) { - f = 0; - k = lp->eta_col_end[i] - 1; - j = lp->eta_col_end[i - 1]; - - for (rowp = lp->eta_row_nr + j, valuep = lp->eta_value + j; - j <= k; - j++, rowp++, valuep++) - f += row[*rowp] * *valuep; - - my_round(f, lp->epsel); - row[lp->eta_row_nr[k]] = f; - } -} /* btran */ - - -static gboolean -isvalid (lprec *lp) -{ - int i, j, *rownum, *colnum; - int *num, row_nr; - - if (!lp->row_end_valid) { - num = g_new (int, lp->rows + 1); - rownum = g_new (int, lp->rows + 1); - - for (i = 0; i <= lp->rows; i++) { - num[i] = 0; - rownum[i] = 0; - } - - for (i = 0; i < lp->non_zeros; i++) - rownum[lp->mat[i].row_nr]++; - - lp->row_end[0] = 0; - - for (i = 1; i <= lp->rows; i++) - lp->row_end[i] = lp->row_end[i - 1] + rownum[i]; - - for (i = 1; i <= lp->columns; i++) - for (j = lp->col_end[i - 1]; j < lp->col_end[i]; j++) { - row_nr = lp->mat[j].row_nr; - if (row_nr != 0) { - num[row_nr]++; - lp->col_no[lp->row_end[row_nr - 1] - + num[row_nr]] = i; - } - } - - g_free (num); - g_free (rownum); - lp->row_end_valid = TRUE; - } - - if (lp->valid) - return TRUE; - - rownum = g_new0(int, lp->rows + 1); - colnum = g_new0(int, lp->columns + 1); - - for (i = 1 ; i <= lp->columns; i++) - for (j = lp->col_end[i - 1]; j < lp->col_end[i]; j++) { - colnum[i]++; - rownum[lp->mat[j].row_nr]++; - } - - for (i = 1; i <= lp->columns; i++) - if (colnum[i] == 0) { - if (lp->names_used) - g_print ("Warning: Variable %s not used in " - "any constraints\n", - lp->col_name[i]); - else - g_print ("Warning: Variable %d not used in " - "any constraints\n", - i); - } - g_free (rownum); - g_free (colnum); - lp->valid = TRUE; - return TRUE; -} - -static void -resize_eta (lprec *lp, int min_size) -{ - while (lp->eta_alloc <= min_size) - lp->eta_alloc *= 1.5; - /* fprintf(stderr, "resizing eta to size %d\n", lp->eta_alloc); */ - lp->eta_value = g_renew (gnm_float, lp->eta_value, lp->eta_alloc + 1); - lp->eta_row_nr = g_renew (int, lp->eta_row_nr, lp->eta_alloc + 1); -} /* resize_eta */ - -static void -condensecol (lprec *lp, int row_nr, gnm_float *pcol) -{ - int i, elnr, min_size; - - elnr = lp->eta_col_end[lp->eta_size]; - - min_size = elnr + lp->rows + 2; - if (min_size >= lp->eta_alloc) /* maximum local growth of Eta */ - resize_eta(lp, min_size); - - for (i = 0; i <= lp->rows; i++) - if (i != row_nr && pcol[i] != 0) { - lp->eta_row_nr[elnr] = i; - lp->eta_value[elnr] = pcol[i]; - elnr++; - } - - lp->eta_row_nr[elnr] = row_nr; - lp->eta_value[elnr] = pcol[row_nr]; - elnr++; - lp->eta_col_end[lp->eta_size + 1] = elnr; -} /* condensecol */ - - -static void -addetacol (lprec *lp) -{ - int i, j, k; - gnm_float theta; - - j = lp->eta_col_end[lp->eta_size]; - lp->eta_size++; - k = lp->eta_col_end[lp->eta_size] - 1; - theta = 1 / (gnm_float) lp->eta_value[k]; - lp->eta_value[k] = theta; - for (i = j; i < k; i++) - lp->eta_value[i] *= -theta; - JustInverted = FALSE; -} /* addetacol */ - - -static void -setpivcol (lprec *lp, - gboolean lower, - int varin, - gnm_float *pcol) -{ - int i, colnr; - - for (i = 0; i <= lp->rows; i++) - pcol[i] = 0; - - if (lower) { - if (varin > lp->rows) { - colnr = varin - lp->rows; - for (i = lp->col_end[colnr - 1]; - i < lp->col_end[colnr]; i++) - pcol[lp->mat[i].row_nr] = lp->mat[i].value; - pcol[0] -= lp_solve_Extrad; - } - else - pcol[varin] = 1; - } - else { /* !lower */ - if (varin > lp->rows) { - colnr = varin - lp->rows; - for (i = lp->col_end[colnr - 1]; - i < lp->col_end[colnr]; i++) - pcol[lp->mat[i].row_nr] = -lp->mat[i].value; - pcol[0] += lp_solve_Extrad; - } else - pcol[varin] = -1; - } - - ftran(lp, pcol); -} /* setpivcol */ - - -static void -minoriteration (lprec *lp, int colnr, int row_nr) -{ - int i, j, k, wk, varin, varout, elnr; - gnm_float piv = 0, theta; - - varin = colnr + lp->rows; - elnr = lp->eta_col_end[lp->eta_size]; - wk = elnr; - lp->eta_size++; - - if (lp_solve_Extrad != 0) { - lp->eta_row_nr[elnr] = 0; - lp->eta_value[elnr] = -lp_solve_Extrad; - elnr++; - if (elnr >= lp->eta_alloc) - resize_eta(lp, elnr); - } - - for (j = lp->col_end[colnr - 1] ; j < lp->col_end[colnr]; j++) { - k = lp->mat[j].row_nr; - - if (k == 0 && lp_solve_Extrad != 0) - lp->eta_value[lp->eta_col_end[lp->eta_size - 1]] += - lp->mat[j].value; - else if (k != row_nr) { - lp->eta_row_nr[elnr] = k; - lp->eta_value[elnr] = lp->mat[j].value; - elnr++; - if (elnr >= lp->eta_alloc) - resize_eta(lp, elnr); - } - else - piv = lp->mat[j].value; - } - - lp->eta_row_nr[elnr] = row_nr; - lp->eta_value[elnr] = 1 / piv; - theta = lp->rhs[row_nr] / piv; - lp->rhs[row_nr] = theta; - - for (i = wk; i < elnr; i++) - lp->rhs[lp->eta_row_nr[i]] -= theta * lp->eta_value[i]; - - varout = lp->bas[row_nr]; - lp->bas[row_nr] = varin; - lp->basis[varout] = FALSE; - lp->basis[varin] = TRUE; - - for (i = wk; i < elnr; i++) - lp->eta_value[i] /= -piv; - - lp->eta_col_end[lp->eta_size] = elnr + 1; -} /* minoriteration */ - - -static void -rhsmincol (lprec *lp, gnm_float theta, int row_nr, int varin) -{ - int i, j, k, varout; - gnm_float f; - - if (row_nr > lp->rows + 1) { - g_print ("Error: rhsmincol called with row_nr: " - "%d, rows: %d\n", row_nr, lp->rows); - g_print ("This indicates numerical instability\n"); - exit(EXIT_FAILURE); - } - - j = lp->eta_col_end[lp->eta_size]; - k = lp->eta_col_end[lp->eta_size + 1]; - for (i = j; i < k; i++) { - f = lp->rhs[lp->eta_row_nr[i]] - theta * lp->eta_value[i]; - my_round(f, lp->epsb); - lp->rhs[lp->eta_row_nr[i]] = f; - } - - lp->rhs[row_nr] = theta; - varout = lp->bas[row_nr]; - lp->bas[row_nr] = varin; - lp->basis[varout] = FALSE; - lp->basis[varin] = TRUE; -} /* rhsmincol */ - - -void -invert (lprec *lp) -{ - int i, j, v, wk, numit, varnr, row_nr, colnr, varin; - gnm_float theta; - gnm_float *pcol; - short *frow; - short *fcol; - int *rownum, *col, *row; - int *colnum; - - if (lp->print_at_invert) - fprintf(stderr, - "Start Invert iter %d eta_size %d rhs[0] %g \n", - lp->iter, lp->eta_size, (double) -lp->rhs[0]); - - rownum = g_new0 (int, lp->rows + 1); - col = g_new0 (int, lp->rows + 1); - row = g_new0 (int, lp->rows + 1); - pcol = g_new0 (gnm_float, lp->rows + 1); - frow = g_new0 (short, lp->rows + 1); - fcol = g_new0 (short, lp->columns + 1); - colnum = g_new0 (int, lp->columns + 1); - - for (i = 0; i <= lp->rows; i++) - frow[i] = TRUE; - - for (i = 0; i < lp->columns; i++) - fcol[i] = FALSE; - - for (i = 0; i < lp->rows; i++) - rownum[i] = 0; - - for (i = 0; i <= lp->columns; i++) - colnum[i] = 0; - - for (i = 0; i <= lp->rows; i++) - if (lp->bas[i] > lp->rows) - fcol[lp->bas[i] - lp->rows - 1] = TRUE; - else - frow[lp->bas[i]] = FALSE; - - for (i = 1; i <= lp->rows; i++) - if (frow[i]) - for (j = lp->row_end[i - 1] + 1; - j <= lp->row_end[i]; j++) { - wk = lp->col_no[j]; - if (fcol[wk - 1]) { - colnum[wk]++; - rownum[i - 1]++; - } - } - - for (i = 1; i <= lp->rows; i++) - lp->bas[i] = i; - - for (i = 1; i <= lp->rows; i++) - lp->basis[i] = TRUE; - - for (i = 1; i <= lp->columns; i++) - lp->basis[i + lp->rows] = FALSE; - - for (i = 0; i <= lp->rows; i++) - lp->rhs[i] = lp->rh[i]; - - for (i = 1; i <= lp->columns; i++) { - varnr = lp->rows + i; - if (!lp->lower[varnr]) { - theta = lp->upbo[varnr]; - for (j = lp->col_end[i - 1]; j < lp->col_end[i]; j++) - lp->rhs[lp->mat[j].row_nr] -= theta - * lp->mat[j].value; - } - } - - for (i = 1; i <= lp->rows; i++) - if (!lp->lower[i]) - lp->rhs[i] -= lp->upbo[i]; - - lp->eta_size = 0; - v = 0; - row_nr = 0; - lp->num_inv = 0; - numit = 0; - - while (v < lp->rows) { - row_nr++; - if (row_nr > lp->rows) - row_nr = 1; - - v++; - - if (rownum[row_nr - 1] == 1) - if (frow[row_nr]) { - v = 0; - j = lp->row_end[row_nr - 1] + 1; - - while (!(fcol[lp->col_no[j] - 1])) - j++; - - colnr = lp->col_no[j]; - fcol[colnr - 1] = FALSE; - colnum[colnr] = 0; - - for (j = lp->col_end[colnr - 1]; - j < lp->col_end[colnr]; j++) - if (frow[lp->mat[j].row_nr]) - rownum[lp->mat[j].row_nr - 1]--; - - frow[row_nr] = FALSE; - minoriteration(lp, colnr, row_nr); - } - } - - v = 0; - colnr = 0; - - while (v < lp->columns) { - colnr++; - if (colnr > lp->columns) - colnr = 1; - - v++; - - if (colnum[colnr] == 1) - if (fcol[colnr - 1]) { - v = 0; - j = lp->col_end[colnr - 1] + 1; - - while (!(frow[lp->mat[j - 1].row_nr])) - j++; - - row_nr = lp->mat[j - 1].row_nr; - frow[row_nr] = FALSE; - rownum[row_nr - 1] = 0; - - for (j = lp->row_end[row_nr - 1] + 1; - j <= lp->row_end[row_nr]; j++) - if (fcol[lp->col_no[j] - 1]) - colnum[lp->col_no[j]]--; - - fcol[colnr - 1] = FALSE; - numit++; - col[numit - 1] = colnr; - row[numit - 1] = row_nr; - } - } - for (j = 1; j <= lp->columns; j++) - if (fcol[j - 1]) { - fcol[j - 1] = FALSE; - setpivcol(lp, lp->lower[lp->rows + j], - j + lp->rows, pcol); - row_nr = 1; - - while ((row_nr <= lp->rows) && (!(frow[row_nr] - && pcol[row_nr]))) - row_nr++; - - /* if (row_nr == lp->rows + 1) */ - if (row_nr > lp->rows) /* problems! */ - g_print ("Inverting failed"); - - frow[row_nr] = FALSE; - condensecol(lp, row_nr, pcol); - theta = lp->rhs[row_nr] / (gnm_float) pcol[row_nr]; - rhsmincol(lp, theta, row_nr, lp->rows + j); - addetacol(lp); - } - - for (i = numit - 1; i >= 0; i--) { - colnr = col[i]; - row_nr = row[i]; - varin = colnr + lp->rows; - - for (j = 0; j <= lp->rows; j++) - pcol[j] = 0; - - for (j = lp->col_end[colnr - 1]; j < lp->col_end[colnr]; j++) - pcol[lp->mat[j].row_nr] = lp->mat[j].value; - - pcol[0] -= lp_solve_Extrad; - condensecol (lp, row_nr, pcol); - theta = lp->rhs[row_nr] / (gnm_float) pcol[row_nr]; - rhsmincol (lp, theta, row_nr, varin); - addetacol (lp); - } - - for (i = 1; i <= lp->rows; i++) - my_round (lp->rhs[i], lp->epsb); - - if (lp->print_at_invert) - fprintf (stderr, - "End Invert eta_size %d rhs[0] %g\n", - lp->eta_size, (double) -lp->rhs[0]); - - JustInverted = TRUE; - DoInvert = FALSE; - - g_free (rownum); - g_free (col); - g_free (row); - g_free (pcol); - g_free (frow); - g_free (fcol); - g_free (colnum); -} /* invert */ - -static gboolean -colprim (lprec *lp, - int *colnr, - gboolean minit, - gnm_float *drow) -{ - int varnr, i, j; - gnm_float f, dpiv; - - dpiv = -lp->epsd; - (*colnr) = 0; - - if (!minit) { - for (i = 1; i <= lp->sum; i++) - drow[i] = 0; - drow[0] = 1; - btran(lp, drow); - for (i = 1; i <= lp->columns; i++) { - varnr = lp->rows + i; - if (!lp->basis[varnr]) - if (lp->upbo[varnr] > 0) { - f = 0; - for (j = lp->col_end[i - 1]; - j < lp->col_end[i]; j++) - f += drow[lp->mat[j].row_nr] - * lp->mat[j].value; - drow[varnr] = f; - } - } - for (i = 1; i <= lp->sum; i++) - my_round(drow[i], lp->epsd); - } - for (i = 1; i <= lp->sum; i++) - if (!lp->basis[i]) - if (lp->upbo[i] > 0) { - if (lp->lower[i]) - f = drow[i]; - else - f = -drow[i]; - if (f < dpiv) { - dpiv = f; - (*colnr) = i; - } - } - if (lp->trace) { - if ((*colnr)>0) - fprintf (stderr, "col_prim:%d, reduced cost: %g\n", - (*colnr), (double)dpiv); - else - fprintf (stderr, - "col_prim: no negative reduced costs found, " - "optimality!\n"); - } - if (*colnr == 0) { - Doiter = FALSE; - DoInvert = FALSE; - Status = SolverOptimal; - } - return((*colnr) > 0); -} /* colprim */ - -static gboolean -rowprim (lprec *lp, - int colnr, - int *row_nr, - gnm_float *theta, - gnm_float *pcol) -{ - int i; - gnm_float f = -42, quot; - - (*row_nr) = 0; - (*theta) = lp->infinite; - for (i = 1; i <= lp->rows; i++) { - f = pcol[i]; - if (f != 0) { - if (ABS(f) < lp_solve_Trej) { - lp_solve_debug_print (lp, - "pivot %g rejected, " - "too small (limit %g)\n", - (double)f, - (double) lp_solve_Trej); - } - else { /* pivot alright */ - quot = 2 * lp->infinite; - if (f > 0) - quot = lp->rhs[i] / (gnm_float) f; - else if (lp->upbo[lp->bas[i]] < lp->infinite) - quot = (lp->rhs[i] - - lp->upbo[lp->bas[i]]) - / (gnm_float) f; - my_round (quot, lp->epsel); - if (quot < (*theta)) { - (*theta) = quot; - (*row_nr) = i; - } - } - } - } - if ((*row_nr) == 0) - for (i = 1; i <= lp->rows; i++) { - f = pcol[i]; - if (f != 0) { - quot = 2 * lp->infinite; - if (f > 0) - quot = lp->rhs[i] / (gnm_float) f; - else - if (lp->upbo[lp->bas[i]] < lp->infinite) - quot = (lp->rhs[i] - - lp->upbo[lp->bas[i]]) / - (gnm_float) f; - my_round (quot, lp->epsel); - if (quot < (*theta)) { - (*theta) = quot; - (*row_nr) = i; - } - } - } - - if ((*theta) < 0) { - fprintf (stderr, "Warning: Numerical instability, qout = %g\n", - (double) (*theta)); - fprintf (stderr, - "pcol[%d] = %18g, rhs[%d] = %18g , upbo = %g\n", - (*row_nr), (double) f, (*row_nr), - (double) lp->rhs[(*row_nr)], - (double) lp->upbo[lp->bas[(*row_nr)]]); - } - if ((*row_nr) == 0) { - if (lp->upbo[colnr] == lp->infinite) { - Doiter = FALSE; - DoInvert = FALSE; - Status = SolverUnbounded; - } else { - i = 1; - while (pcol[i] >= 0 && i <= lp->rows) - i++; - if (i > lp->rows) { /* empty column with upperbound! */ - lp->lower[colnr] = FALSE; - lp->rhs[0] += lp->upbo[colnr]*pcol[0]; - Doiter = FALSE; - DoInvert = FALSE; - } - else if (pcol[i]<0) { - (*row_nr) = i; - } - } - } - if ((*row_nr) > 0) - Doiter = TRUE; - if (lp->trace) - fprintf (stderr, "row_prim:%d, pivot element:%18g\n", (*row_nr), - (double) pcol[(*row_nr)]); - - return ((*row_nr) > 0); -} /* rowprim */ - - -static gboolean -rowdual (lprec *lp, int *row_nr) -{ - int i; - gnm_float f, g, minrhs; - gboolean artifs; - - (*row_nr) = 0; - minrhs = -lp->epsb; - i = 0; - artifs = FALSE; - - while (i < lp->rows && !artifs) { - i++; - f = lp->upbo[lp->bas[i]]; - if (f == 0 && (lp->rhs[i] != 0)) { - artifs = TRUE; - (*row_nr) = i; - } else { - if (lp->rhs[i] < f - lp->rhs[i]) - g = lp->rhs[i]; - else - g = f - lp->rhs[i]; - if (g < minrhs) { - minrhs = g; - (*row_nr) = i; - } - } - } - - if (lp->trace) { - if ((*row_nr) > 0) { - fprintf (stderr, - "row_dual:%d, rhs of selected row: " - "%18g\n", - (*row_nr), (double) lp->rhs[(*row_nr)]); - if (lp->upbo[lp->bas[(*row_nr)]] < lp->infinite) - fprintf (stderr, - "\t\tupper bound of basis variable:" - "%18g\n", - (double) lp->upbo[lp->bas[(*row_nr)]]); - } else - fprintf(stderr, "row_dual: no infeasibilities found\n"); - } - - return ((*row_nr) > 0); -} /* rowdual */ - - -static gboolean -coldual (lprec *lp, - int row_nr, - int *colnr, - gboolean minit, - gnm_float *prow, - gnm_float *drow) -{ - int i, j, k, r, varnr, *rowp, row; - gnm_float theta, quot, pivot, d, f, g, *valuep, value; - - Doiter = FALSE; - if (!minit) { - for (i = 0; i <= lp->rows; i++) { - prow[i] = 0; - drow[i] = 0; - } - - drow[0] = 1; - prow[row_nr] = 1; - - for (i = lp->eta_size; i >= 1; i--) { - d = 0; - f = 0; - k = lp->eta_col_end[i] - 1; - r = lp->eta_row_nr[k]; - j = lp->eta_col_end[i - 1]; - - /* this is one of the loops where the program - * consumes a lot of CPU time */ - /* let's help the compiler by doing some pointer - * arithmetic instead of array indexing */ - for (rowp = lp->eta_row_nr + j, - valuep = lp->eta_value + j; - j <= k; - j++, rowp++, valuep++) { - f += prow[*rowp] * *valuep; - d += drow[*rowp] * *valuep; - } - - my_round (f, lp->epsel); - prow[r] = f; - my_round (d, lp->epsd); - drow[r] = d; - } - - for (i = 1; i <= lp->columns; i++) { - varnr = lp->rows + i; - if (!lp->basis[varnr]) { - matrec *matentry; - - d = - lp_solve_Extrad * drow[0]; - f = 0; - k = lp->col_end[i]; - j = lp->col_end[i - 1]; - - /* this is one of the loops where the program - * consumes a lot of cpu time */ - /* let's help the compiler with pointer - * arithmetic instead of array indexing */ - for (matentry = lp->mat + j; - j < k; - j++, matentry++) { - row = (*matentry).row_nr; - value = (*matentry).value; - d += drow[row] * value; - f += prow[row] * value; - } - - my_round (f, lp->epsel); - prow[varnr] = f; - my_round (d, lp->epsd); - drow[varnr] = d; - } - } - } - - if (lp->rhs[row_nr] > lp->upbo[lp->bas[row_nr]]) - g = -1; - else - g = 1; - - pivot = 0; - (*colnr) = 0; - theta = lp->infinite; - - for (i = 1; i <= lp->sum; i++) { - if (lp->lower[i]) - d = prow[i] * g; - else - d = -prow[i] * g; - - if ((d < 0) && (!lp->basis[i]) && (lp->upbo[i] > 0)) { - if (lp->lower[i]) - quot = -drow[i] / (gnm_float) d; - else - quot = drow[i] / (gnm_float) d; - if (quot < theta) { - theta = quot; - pivot = d; - (*colnr) = i; - } - else if ((quot == theta) - && (ABS(d) > ABS(pivot))) { - pivot = d; - (*colnr) = i; - } - } - } - - if (lp->trace) - fprintf (stderr, - "col_dual:%d, pivot element: %18g\n", (*colnr), - (double) prow[(*colnr)]); - - if ((*colnr) > 0) - Doiter = TRUE; - - return((*colnr) > 0); -} /* coldual */ - -static gboolean -iteration (lprec *lp, - int row_nr, - int varin, - gnm_float *theta, - gnm_float up, - gboolean *minit, - char *low, - gboolean primal, - SolverStatus *solver_status) -{ - int i, k, varout; - gnm_float f; - gnm_float pivot; - const int TIME_CHECK_INTERVAL = 10; - - lp->iter++; - - /* Check if maximum number of iteration has been exceeded. */ - if (lp->iter + lp->total_iter > lp->max_total_iter) { - *solver_status = SolverMaxIterExc; - return TRUE; - } - - /* Check if maximum time has been exceeded. */ - if (lp->iter % TIME_CHECK_INTERVAL == 0) { - GTimeVal t; - gnm_float elapsed; - - g_get_current_time (&t); - elapsed = t.tv_sec + t.tv_usec / (gnm_float) G_USEC_PER_SEC - - lp->start_time; - if (elapsed > lp->max_time) { - *solver_status = SolverMaxTimeExc; - return TRUE; - } - } - - if (((*minit) = (*theta) > (up + lp->epsb))) { - (*theta) = up; - (*low) = !(*low); - } - - k = lp->eta_col_end[lp->eta_size + 1]; - pivot = lp->eta_value[k - 1]; - - for (i = lp->eta_col_end[lp->eta_size]; i < k; i++) { - f = lp->rhs[lp->eta_row_nr[i]] - (*theta) * lp->eta_value[i]; - my_round (f, lp->epsb); - lp->rhs[lp->eta_row_nr[i]] = f; - } - - if (!(*minit)) { - lp->rhs[row_nr] = (*theta); - varout = lp->bas[row_nr]; - lp->bas[row_nr] = varin; - lp->basis[varout] = FALSE; - lp->basis[varin] = TRUE; - - if (primal && pivot < 0) - lp->lower[varout] = FALSE; - - if (!(*low) && up < lp->infinite) { - (*low) = TRUE; - lp->rhs[row_nr] = up - lp->rhs[row_nr]; - for (i = lp->eta_col_end[lp->eta_size]; i < k; i++) - lp->eta_value[i] = -lp->eta_value[i]; - } - - addetacol (lp); - lp->num_inv++; - } - - if (lp->trace) { - fprintf (stderr, "Theta = %g ", (double)(*theta)); - if ((*minit)) { - if (!lp->lower[varin]) - fprintf (stderr, - "Iteration: %d, variable %d changed " - "from 0 to its upper bound of %g\n", - lp->iter, varin, - (double) lp->upbo[varin]); - else - fprintf (stderr, - "Iteration: %d, variable %d changed " - "its upper bound of %g to 0\n", - lp->iter, varin, - (double) lp->upbo[varin]); - } - else - fprintf (stderr, - "Iteration: %d, variable %d entered basis at: %g\n", - lp->iter, varin, (double)lp->rhs[row_nr]); - if (!primal) { - f = 0; - for (i = 1; i <= lp->rows; i++) - if (lp->rhs[i] < 0) - f -= lp->rhs[i]; - else - if (lp->rhs[i] > lp->upbo[lp->bas[i]]) - f += lp->rhs[i] - - lp->upbo[lp->bas[i]]; - fprintf (stderr, "feasibility gap of this basis: %g\n", - (double) f); - } else - fprintf (stderr, - "objective function value of this feasible " - "basis: %g\n", - (double) lp->rhs[0]); - } - - return FALSE; -} /* iteration */ - - -/* A divizion by zero occured in solvelp. Report it and try to recover. - */ -static void -catchdiv0 (int row_nr) -{ - fprintf (stderr, "An attempt was made to divide by zero (Pcol[%d])\n", - row_nr); - fprintf (stderr, "This indicates numerical instability\n"); - - Doiter = FALSE; - - if (!JustInverted) { - fprintf (stderr, "Trying to recover. Reinverting Eta\n"); - DoInvert = TRUE; - } else { - fprintf (stderr, "Can't reinvert, failure\n"); - Status = SolverFailure; - } -} - - -static int -solvelp (lprec *lp) -{ - int i, j, varnr; - gnm_float f, theta; - gboolean primal; - gnm_float *drow, *prow, *Pcol; - gboolean minit; - int colnr, row_nr; - - drow = g_new0 (gnm_float, lp->sum + 1); - prow = g_new0 (gnm_float, lp->sum + 1); - Pcol = g_new0 (gnm_float, lp->rows + 1); - - lp->iter = 0; - minit = FALSE; - Status = SolverRunning; - DoInvert = FALSE; - Doiter = FALSE; - - for (i = 1, primal = TRUE; (i <= lp->rows) && primal; i++) - primal = (lp->rhs[i] >= 0) - && (lp->rhs[i] <= lp->upbo[lp->bas[i]]); - - if (lp->trace) { - if (primal) - fprintf(stderr, "Start at feasible basis\n"); - else - fprintf(stderr, "Start at infeasible basis\n"); - } - - if (!primal) { - drow[0] = 1; - - for (i = 1; i <= lp->rows; i++) - drow[i] = 0; - - /* fix according to Joerg Herbers */ - btran (lp, drow); - - lp_solve_Extrad = 0; - - for (i = 1; i <= lp->columns; i++) { - varnr = lp->rows + i; - drow[varnr] = 0; - - for (j = lp->col_end[i - 1]; j < lp->col_end[i]; j++) - if (drow[lp->mat[j].row_nr] != 0) - drow[varnr] += drow[lp->mat[j].row_nr] - * lp->mat[j].value; - - if (drow[varnr] < lp_solve_Extrad) - lp_solve_Extrad = drow[varnr]; - } - } else - lp_solve_Extrad = 0; - - if (lp->trace) - fprintf (stderr, "lp_solve_Extrad = %g\n", - (double) lp_solve_Extrad); - - minit = FALSE; - - while (Status == SolverRunning) { - Doiter = FALSE; - DoInvert = FALSE; - - if (primal) { - if (colprim (lp, &colnr, minit, drow)) { - setpivcol (lp, lp->lower[colnr], colnr, Pcol); - - if (rowprim (lp, colnr, &row_nr, &theta, Pcol)) - condensecol (lp, row_nr, Pcol); - } - } else /* not primal */ { - if (!minit) - rowdual (lp, &row_nr); - - if (row_nr > 0 ) { - if (coldual (lp, row_nr, &colnr, minit, prow, - drow)) { - setpivcol (lp, lp->lower[colnr], colnr, - Pcol); - - /* getting div by zero here. Catch it - * and try to recover */ - if (Pcol[row_nr] == 0) - catchdiv0 (row_nr); - else { - condensecol (lp, row_nr, Pcol); - f = lp->rhs[row_nr] - - lp->upbo[lp->bas[row_nr]]; - - if (f > 0) { - theta = f / - (gnm_float) Pcol[row_nr]; - if (theta <= - lp->upbo[colnr]) - lp->lower[lp->bas[row_nr]] = - !lp->lower[lp->bas[row_nr]]; - } - else /* f <= 0 */ - theta = lp->rhs[row_nr] / - (gnm_float) Pcol[row_nr]; - } - } else - Status = SolverInfeasible; - } else { - primal = TRUE; - Doiter = FALSE; - lp_solve_Extrad = 0; - DoInvert = TRUE; - } - } - - if (Doiter) - iteration (lp, row_nr, colnr, &theta, lp->upbo[colnr], - &minit, &lp->lower[colnr], primal, &Status); - - if (lp->num_inv >= lp->max_num_inv) - DoInvert = TRUE; - - if (DoInvert) { - if (lp->print_at_invert) - fprintf (stderr, "Inverting: Primal = %d\n", - primal); - invert (lp); - } - } - - lp->total_iter += lp->iter; - - g_free (drow); - g_free (prow); - g_free (Pcol); - - return (Status); -} /* solvelp */ - - -static gboolean -is_int (lprec *lp, int i) -{ - gnm_float value, error; - - value = lp->solution[i]; - error = value - floorgnum (value); - - if (error < lp->epsilon) - return TRUE; - - if (error > (1 - lp->epsilon)) - return TRUE; - - return FALSE; -} /* is_int */ - - -static void -construct_solution (lprec *lp) -{ - int i, j, basi; - gnm_float f; - - /* zero all results of rows */ - memset (lp->solution, '\0', (lp->rows + 1) * sizeof(gnm_float)); - - lp->solution[0] = -lp->orig_rh[0]; - - if (lp->scaling_used) { - lp->solution[0] /= lp->scale[0]; - - for (i = lp->rows + 1; i <= lp->sum; i++) - lp->solution[i] = lp->lowbo[i] * lp->scale[i]; - - for (i = 1; i <= lp->rows; i++) { - basi = lp->bas[i]; - if (basi > lp->rows) - lp->solution[basi] += lp->rhs[i] - * lp->scale[basi]; - } - for (i = lp->rows + 1; i <= lp->sum; i++) - if (!lp->basis[i] && !lp->lower[i]) - lp->solution[i] += lp->upbo[i] * lp->scale[i]; - - for (j = 1; j <= lp->columns; j++) { - f = lp->solution[lp->rows + j]; - if (f != 0) - for (i = lp->col_end[j - 1]; - i < lp->col_end[j]; i++) - lp->solution[lp->mat[i].row_nr] += - (f / lp->scale[lp->rows+j]) - * (lp->mat[i].value / - lp->scale[lp->mat[i].row_nr]); - } - - for (i = 0; i <= lp->rows; i++) { - if (ABS(lp->solution[i]) < lp->epsb) - lp->solution[i] = 0; - else if (lp->ch_sign[i]) - lp->solution[i] = -lp->solution[i]; - } - } else { /* no scaling */ - for (i = lp->rows + 1; i <= lp->sum; i++) - lp->solution[i] = lp->lowbo[i]; - - for (i = 1; i <= lp->rows; i++) { - basi = lp->bas[i]; - if (basi > lp->rows) - lp->solution[basi] += lp->rhs[i]; - } - - for (i = lp->rows + 1; i <= lp->sum; i++) - if (!lp->basis[i] && !lp->lower[i]) - lp->solution[i] += lp->upbo[i]; - - for (j = 1; j <= lp->columns; j++) { - f = lp->solution[lp->rows + j]; - if (f != 0) - for (i = lp->col_end[j - 1]; - i < lp->col_end[j]; i++) - lp->solution[lp->mat[i].row_nr] += - f * lp->mat[i].value; - } - - for (i = 0; i <= lp->rows; i++) { - if (ABS(lp->solution[i]) < lp->epsb) - lp->solution[i] = 0; - else if (lp->ch_sign[i]) - lp->solution[i] = -lp->solution[i]; - } - } -} /* construct_solution */ - -static void -calculate_duals (lprec *lp) -{ - int i; - - /* initialize */ - lp->duals[0] = 1; - for (i = 1; i <= lp->rows; i++) - lp->duals[i] = 0; - - btran(lp, lp->duals); - - if (lp->scaling_used) - for (i = 1; i <= lp->rows; i++) - lp->duals[i] *= lp->scale[i] / lp->scale[0]; - - /* the dual values are the reduced costs of the slacks */ - /* When the slack is at its upper bound, change the sign. */ - for (i = 1; i <= lp->rows; i++) { - if (lp->basis[i]) - lp->duals[i] = 0; - /* added a test if variable is different from 0 because - * sometime you get -0 and this is different from 0 on - * for example INTEL processors (ie 0 != -0 on INTEL !) PN */ - else if ((lp->ch_sign[0] == lp->ch_sign[i]) && lp->duals[i]) - lp->duals[i] = - lp->duals[i]; - } -} /* calculate_duals */ - -static void -check_if_less (gnm_float x, - gnm_float y, - gnm_float value) -{ - if (x >= y) { - fprintf (stderr, - "Error: new upper or lower bound is not more " - "restrictive\n"); - fprintf (stderr, "bound 1: %g, bound 2: %g, value: %g\n", - (double)x, (double)y, (double)value); - /* exit(EXIT_FAILURE); */ - } -} - - -static int -milpsolve (lprec *lp, - gnm_float *upbo, - gnm_float *lowbo, - char *sbasis, - char *slower, - int *sbas, - gboolean recursive) -{ - int i, j, is_worse; - int notint = -1; - gnm_float theta, tmpreal; - SolverStatus failure; - - if (Break_bb) - return(BREAK_BB); - - lp_solve_Level++; - lp->total_nodes++; - - if (lp_solve_Level > lp->max_level) - lp->max_level = lp_solve_Level; - - lp_solve_debug_print(lp, "starting solve"); - - /* make fresh copies of upbo, lowbo, rh as solving changes them */ - memcpy (lp->upbo, upbo, (lp->sum + 1) * sizeof(gnm_float)); - memcpy (lp->lowbo, lowbo, (lp->sum + 1) * sizeof(gnm_float)); - memcpy (lp->rh, lp->orig_rh, (lp->rows + 1) * sizeof(gnm_float)); - - /* make shure we do not do memcpy(lp->basis, lp->basis ...) ! */ - if (recursive) { - memcpy (lp->basis, sbasis, (lp->sum + 1) * sizeof(char)); - memcpy (lp->lower, slower, (lp->sum + 1) * sizeof(char)); - memcpy (lp->bas, sbas, (lp->rows + 1) * sizeof(int)); - } - - if (lp->anti_degen) { /* randomly disturb bounds */ - for (i = 1; i <= lp->columns; i++) { - tmpreal = (gnm_float) (rand() % 100 * 0.00001); - if (tmpreal > lp->epsb) - lp->lowbo[i + lp->rows] -= tmpreal; - tmpreal = (gnm_float) (rand() % 100 * 0.00001); - if (tmpreal > lp->epsb) - lp->upbo[i + lp->rows] += tmpreal; - } - lp->eta_valid = FALSE; - } - - if (!lp->eta_valid) { - /* transform to all lower bounds to zero */ - for (i = 1; i <= lp->columns; i++) - if ((theta = lp->lowbo[lp->rows + i]) != 0) { - if (lp->upbo[lp->rows + i] < lp->infinite) - lp->upbo[lp->rows + i] -= theta; - for (j = lp->col_end[i - 1]; - j < lp->col_end[i]; j++) - lp->rh[lp->mat[j].row_nr] -= - theta * lp->mat[j].value; - } - invert(lp); - lp->eta_valid = TRUE; - } - - failure = solvelp(lp); - - if (lp->anti_degen && (failure == SolverOptimal)) { - /* restore to original problem, solve again starting from - * the basis found for the disturbed problem */ - - /* restore original problem */ - memcpy (lp->upbo, upbo, (lp->sum + 1) * sizeof(gnm_float)); - memcpy (lp->lowbo, lowbo, (lp->sum + 1) * sizeof(gnm_float)); - memcpy (lp->rh, lp->orig_rh, - (lp->rows + 1) * sizeof(gnm_float)); - - /* transform to all lower bounds zero */ - for (i = 1; i <= lp->columns; i++) - if ((theta = lp->lowbo[lp->rows + i]) != 0) { - if (lp->upbo[lp->rows + i] < lp->infinite) - lp->upbo[lp->rows + i] -= theta; - for (j = lp->col_end[i - 1]; - j < lp->col_end[i]; j++) - lp->rh[lp->mat[j].row_nr] -= - theta * lp->mat[j].value; - } - invert(lp); - lp->eta_valid = TRUE; - failure = solvelp(lp); /* and solve again */ - } - - if (failure != SolverOptimal) - lp_solve_debug_print (lp, "this problem has no solution, it " - "is %s", - (failure == SolverUnbounded) ? - "unbounded" : "infeasible"); - - if (failure == SolverInfeasible && lp->verbose) - fprintf (stderr, "level %d INF\n", lp_solve_Level); - - if (failure == SolverOptimal) { /* there is a good solution */ - construct_solution (lp); - - /* because of reports of solution > upbo */ - /* check_solution(lp, upbo, lowbo); get too many hits ?? */ - - lp_solve_debug_print (lp, "a solution was found"); - lp_solve_debug_print_solution (lp); - - /* if this solution is worse than the best sofar, this - * branch must die */ - - /* if we can only have integer SolverOF values, we might - * consider requiring to be at least 1 better than the - * best sofar, MB */ - - if (lp->maximise) - is_worse = lp->solution[0] <= lp->best_solution[0]; - else /* minimising! */ - is_worse = lp->solution[0] >= lp->best_solution[0]; - - if (is_worse) { - if (lp->verbose) - fprintf (stderr, - "level %d OPT NOB value %g bound " - "%g\n", - lp_solve_Level, - (double) lp->solution[0], - (double) lp->best_solution[0]); - lp_solve_debug_print (lp, - "but it was worse than the best " - "sofar, discarded"); - lp_solve_Level--; - return (SolverMilpFailure); - } - - /* check if solution contains enough ints */ - if (lp->bb_rule == FIRST_NI) { - for (notint = 0, i = lp->rows + 1; - i <= lp->sum && notint == 0; - i++) { - if (lp->must_be_int[i] && !is_int(lp, i)) { - if (lowbo[i] == upbo[i]) { /* this var - * is - * already - * fixed */ - fprintf(stderr, - "Warning: integer var " - "%d is already fixed " - "at %d, but has " - "non-integer value " - "%g\n", - i - lp->rows, - (int)lowbo[i], - (double) lp->solution[i]); - fprintf(stderr, - "Perhaps the -e " - "option should be " - "used\n"); - } else - notint = i; - } - } - } - if (lp->bb_rule == RAND_NI) { - int nr_not_int, select_not_int; - nr_not_int = 0; - - for (i = lp->rows + 1; i <= lp->sum; i++) - if (lp->must_be_int[i] && !is_int(lp, i)) - nr_not_int++; - - if (nr_not_int == 0) - notint = 0; - else { - select_not_int = (rand() % nr_not_int) + 1; - i = lp->rows + 1; - while (select_not_int > 0) { - if (lp->must_be_int[i] - && !is_int(lp, i)) - select_not_int--; - i++; - } - notint = i - 1; - } - } - - if (lp->verbose) { - if (notint) - fprintf (stderr, "level %d OPT value %g\n", - lp_solve_Level, - (double) lp->solution[0]); - else - fprintf (stderr, - "level %d OPT INT value %g\n", - lp_solve_Level, - (double) lp->solution[0]); - } - - if (notint) { /* there is at least one value not yet int */ - /* set up two new problems */ - gnm_float *new_upbo, *new_lowbo; - gnm_float new_bound; - char *new_lower, *new_basis; - int *new_bas; - SolverStatus resone, restwo = SolverMilpFailure; - - /* allocate room for them */ - new_upbo = g_new (gnm_float, lp->sum + 1); - new_lowbo = g_new (gnm_float, lp->sum + 1); - new_lower = g_new (char, lp->sum + 1); - new_basis = g_new (char, lp->sum + 1); - new_bas = g_new (int, lp->rows + 1); - memcpy (new_upbo, upbo, (lp->sum + 1) - * sizeof(gnm_float)); - memcpy (new_lowbo, lowbo, (lp->sum + 1) - * sizeof(gnm_float)); - memcpy (new_lower, lp->lower, (lp->sum + 1) - * sizeof(char)); - memcpy (new_basis, lp->basis, (lp->sum + 1) - * sizeof(char)); - memcpy (new_bas, lp->bas, (lp->rows + 1) - * sizeof(int)); - - if (lp->names_used) - lp_solve_debug_print(lp, "not enough ints. " - "Selecting var %s, val: " - "%g", - lp->col_name[notint - - lp->rows], - (double) lp->solution[notint]); - else - lp_solve_debug_print(lp, - "not enough ints. " - "Selecting Var [%d], " - "val: %g", - notint, - (double) lp->solution[notint]); - lp_solve_debug_print(lp, "current bounds:\n"); - lp_solve_debug_print_bounds(lp, upbo, lowbo); - - if (lp->floor_first) { - new_bound = ceilgnum (lp->solution[notint]) - 1; - - /* this bound might conflict */ - if (new_bound < lowbo[notint]) { - lp_solve_debug_print(lp, - "New upper bound " - "value %g " - "conflicts with " - "old lower bound " - "%g\n", - (double) new_bound, - (double) lowbo[notint]); - resone = SolverMilpFailure; - } - else { /* bound feasible */ - check_if_less(new_bound, upbo[notint], - lp->solution[notint]); - new_upbo[notint] = new_bound; - lp_solve_debug_print(lp, "starting " - "first subproblem" - " with bounds:"); - lp_solve_debug_print_bounds(lp, - new_upbo, - lowbo); - lp->eta_valid = FALSE; - resone = milpsolve(lp, new_upbo, lowbo, - new_basis, - new_lower, - new_bas, TRUE); - lp->eta_valid = FALSE; - } - new_bound += 1; - if (new_bound > upbo[notint]) { - lp_solve_debug_print(lp, - "New lower bound " - "value %g " - "conflicts with " - "old upper bound " - "%g\n", - (double) new_bound, - (double) upbo[notint]); - restwo = SolverMilpFailure; - } - else { /* bound feasible */ - check_if_less (lowbo[notint], - new_bound, - lp->solution[notint]); - new_lowbo[notint] = new_bound; - lp_solve_debug_print(lp, - "starting second " - "subproblem with " - "bounds:"); - lp_solve_debug_print_bounds(lp, upbo, - new_lowbo); - lp->eta_valid = FALSE; - if (resone < SolverMaxIterExc) - restwo = milpsolve(lp, upbo, new_lowbo, - new_basis, - new_lower, - new_bas, TRUE); - lp->eta_valid = FALSE; - } - } - else { /* take ceiling first */ - new_bound = ceilgnum (lp->solution[notint]); - /* this bound might conflict */ - if (new_bound > upbo[notint]) { - lp_solve_debug_print(lp, - "New lower bound " - "value %g " - "conflicts with " - "old upper bound " - "%g\n", - (double) new_bound, - (double) upbo[notint]); - resone = SolverMilpFailure; - } else { /* bound feasible */ - check_if_less(lowbo[notint], new_bound, - lp->solution[notint]); - new_lowbo[notint] = new_bound; - lp_solve_debug_print(lp, - "starting first " - "subproblem with " - "bounds:"); - lp_solve_debug_print_bounds(lp, upbo, - new_lowbo); - lp->eta_valid = FALSE; - resone = milpsolve(lp, upbo, new_lowbo, - new_basis, - new_lower, - new_bas, TRUE); - lp->eta_valid = FALSE; - } - new_bound -= 1; - if (new_bound < lowbo[notint]) { - lp_solve_debug_print(lp, - "New upper bound " - "value %g " - "conflicts with " - "old lower bound " - "%g\n", - (double) new_bound, - (double) lowbo[notint]); - restwo = SolverMilpFailure; - } else { /* bound feasible */ - check_if_less(new_bound, upbo[notint], - lp->solution[notint]); - new_upbo[notint] = new_bound; - lp_solve_debug_print(lp, - "starting second " - "subproblem with " - "bounds:"); - lp_solve_debug_print_bounds(lp, - new_upbo, - lowbo); - lp->eta_valid = FALSE; - if (resone < SolverMaxIterExc) - restwo = milpsolve(lp, new_upbo, lowbo, - new_basis, - new_lower, - new_bas, TRUE); - lp->eta_valid = FALSE; - } - } - if (resone == SolverMaxIterExc || restwo == SolverMaxIterExc) - failure = SolverMaxIterExc; - else if (resone == SolverMaxTimeExc || - restwo == SolverMaxTimeExc) - failure = SolverMaxTimeExc; - else if ((resone != SolverOptimal && - resone != SolverMilpFailure) - || /* both failed and must have been infeasible */ - (restwo != SolverOptimal && - restwo != SolverMilpFailure)) - failure = SolverInfeasible; - else - failure = SolverOptimal; - - g_free (new_upbo); - g_free (new_lowbo); - g_free (new_basis); - g_free (new_lower); - g_free (new_bas); - } else { /* all required values are int */ - lp_solve_debug_print(lp, "--> valid solution found"); - - if (lp->maximise) - is_worse = lp->solution[0] < - lp->best_solution[0]; - else - is_worse = lp->solution[0] > - lp->best_solution[0]; - - if (!is_worse) { /* Current solution better */ - if (lp->debug || (lp->verbose - && !lp->print_sol)) - fprintf(stderr, - "*** new best solution: old: " - "%g, new: %g ***\n", - (double)lp->best_solution[0], - (double)lp->solution[0]); - memcpy (lp->best_solution, lp->solution, - (lp->sum + 1) * sizeof(gnm_float)); - calculate_duals(lp); - - if (lp->print_sol) - lp_solve_print_solution (lp); - - if (lp->break_at_int) { - if (lp->maximise && - (lp->best_solution[0] > - lp->break_value)) - Break_bb = TRUE; - - if (!lp->maximise && - (lp->best_solution[0] < - lp->break_value)) - Break_bb = TRUE; - } - } - } - } - - lp_solve_Level--; - - /* failure can have the values SolverOptimal, SolverUnbounded - * and SolverInfeasible. */ - return (failure); -} /* milpsolve */ - - -SolverStatus -lp_solve_solve (lprec *lp) -{ - int result, i; - - lp->total_iter = 0; - lp->max_level = 1; - lp->total_nodes = 0; - - if (isvalid(lp)) { - if (lp->maximise && lp->obj_bound == lp->infinite) - lp->best_solution[0] = -lp->infinite; - else if (!lp->maximise && lp->obj_bound == -lp->infinite) - lp->best_solution[0] = lp->infinite; - else - lp->best_solution[0] = lp->obj_bound; - - lp_solve_Level = 0; - - if (!lp->basis_valid) { - for (i = 0; i <= lp->rows; i++) { - lp->basis[i] = TRUE; - lp->bas[i] = i; - } - - for (i = lp->rows + 1; i <= lp->sum; i++) - lp->basis[i] = FALSE; - - for (i = 0; i <= lp->sum; i++) - lp->lower[i] = TRUE; - - lp->basis_valid = TRUE; - } - - lp->eta_valid = FALSE; - Break_bb = FALSE; - result = milpsolve(lp, lp->orig_upbo, lp->orig_lowbo, - lp->basis, lp->lower, lp->bas, FALSE); - return (result); - } - - /* if we get here, isvalid(lp) failed. I suggest we return - * SolverFailure - * fprintf (stderr, "Error, the current LP seems to be invalid\n"); - */ - return (SolverFailure); -} /* solve */ Index: src/tools/solver/lp_solve/yacc_read.c =================================================================== RCS file: src/tools/solver/lp_solve/yacc_read.c diff -N src/tools/solver/lp_solve/yacc_read.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/yacc_read.c 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,1085 @@ +/* + ============================================================================ + NAME : yacc_read.c + + PURPOSE : translation of lp-problem and storage in sparse matrix + + SHORT : Subroutines for yacc program to store the input in an intermediate + data-structure. The yacc and lex programs translate the input. First the + problemsize is determined and the date is read into an intermediate + structure, then readinput fills the sparse matrix. + + USAGE : call yyparse(); to start reading the input. call readinput(); to + fill the sparse matrix. + ============================================================================ + Rows : contains the amount of rows + 1. Rows-1 is the amount of constraints + (no bounds) Rows also contains the rownr 0 which is the objective function + + Columns : contains the amount of columns (different variable names found in + the constraints) + + Nonnuls : contains the amount of nonnuls = sum of different entries of all + columns in the constraints and in the objectfunction + + Hash_tab : contains all columnnames on the first level of the structure the + row information is kept under each column structure in a linked list (also + the objective funtion is in this structure) Bound information is also + stored under under the column name + + First_rside : points to a linked list containing all relational operators + and the righthandside values of the constraints the linked list is in + reversed order with respect to the rownumbers + ============================================================================ */ +#include +#include +#include +#include "lpkit.h" +#include "yacc_read.h" + +#ifdef FORTIFY +# include "lp_fortify.h" +#endif + + +#define tol 1.0e-10 +#define coldatastep 100 + +#define HASHSIZE 10007 /* A prime number! */ + +static short Maximise; +static short Ignore_int_decl; +static short int_decl; +static short Ignore_sec_decl; +static short sos_decl; +static int Rows; +static int Columns; +static int Non_zeros; +static short *relat; +static int Verbose; +static hashtable *Hash_tab; +static hashtable *Hash_constraints; +static jmp_buf jump_buf; +static int *lineno; +static int Lin_term_count; + +struct structSOSvars { + char *name; + REAL weight; + struct structSOSvars *next; +}; + +static struct structSOS { + char *name; + short type; + int Nvars; + int weight; + struct structSOSvars *SOSvars, *LastSOSvars; + struct structSOS *next; +} *FirstSOS, *LastSOS; + +static struct _tmp_store_struct +{ + nstring name; + int row; + REAL value; + REAL rhs_value; + short relat; +} tmp_store; + +static struct rside /* contains relational operator and rhs value */ +{ + int row; + REAL value; + REAL range_value; + struct rside *next; + short relat; + short range_relat; + char negate; +} *First_rside, *rs; + +struct column +{ + int row; + REAL value; + struct column *next; + struct column *prev; +}; + +static struct structcoldata { + int must_be_int; + int must_be_sec; + REAL upbo; + REAL lowbo; + struct column *firstcol; + struct column *col; +} *coldata; + +static void error(int verbose, char const *string) +{ + if(Verbose >= verbose) + report(NULL, verbose, "%s on line %d\n", string, *lineno); +} + +/* + * error handling routine for yyparse() + */ +void read_error(char const *string) +{ + error(CRITICAL, string); +} + +/* called when lex gets a fatal error */ +void lex_fatal_error(char const *msg) +{ + read_error(msg); + longjmp(jump_buf, 1); +} + +void add_row() +{ + Rows++; + rs = NULL; + Lin_term_count = 0; +} + +void check_int_sec_sos_decl(int within_int_decl, int within_sec_decl, int sos_decl0) +{ + Ignore_int_decl = TRUE; + Ignore_sec_decl = TRUE; + sos_decl = 0; + if(within_int_decl) { + Ignore_int_decl = FALSE; + int_decl = (short) within_int_decl; + } + else if(within_sec_decl) { + Ignore_sec_decl = FALSE; + } + else if(sos_decl0) { + sos_decl = (short) sos_decl0; + } +} + +static void add_int_var(char *name, short int_decl) +{ + hashelem *hp; + + if((hp = findhash(name, Hash_tab)) == NULL) { + char buf[256]; + + sprintf(buf, "Unknown variable %s declared integer, ignored", name); + error(NORMAL, buf); + } + else if(coldata[hp->index].must_be_int) { + char buf[256]; + + sprintf(buf, "Variable %s declared integer more than once, ignored", name); + error(NORMAL, buf); + } + else { + coldata[hp->index].must_be_int = TRUE; + if(int_decl == 2) { + if(coldata[hp->index].lowbo != -DEF_INFINITE * (REAL) 10.0) { + char buf[256]; + + sprintf(buf, "Variable %s: lower bound on variable redefined", name); + error(NORMAL, buf); + } + coldata[hp->index].lowbo = 0; + if(coldata[hp->index].upbo < DEF_INFINITE) { + char buf[256]; + + sprintf(buf, "Variable %s: upper bound on variable redefined", name); + error(NORMAL, buf); + } + coldata[hp->index].upbo = 1; + } + } +} + +static void add_sec_var(char *name) +{ + hashelem *hp; + + if((hp = findhash(name, Hash_tab)) == NULL) { + char buf[256]; + + sprintf(buf, "Unknown variable %s declared semi-continuous, ignored", name); + error(NORMAL, buf); + } + else if(coldata[hp->index].must_be_sec) { + char buf[256]; + + sprintf(buf, "Variable %s declared semi-continuous more than once, ignored", name); + error(NORMAL, buf); + } + else + coldata[hp->index].must_be_sec = TRUE; +} + +static int add_sos_name(char *name) +{ + struct structSOS *SOS; + + if(CALLOC(SOS, 1, struct structSOS) == NULL) + return(FALSE); + + if(MALLOC(SOS->name, strlen(name) + 1, char) == NULL) + { + FREE(SOS); + return(FALSE); + } + strcpy(SOS->name, name); + SOS->type = 0; + + if(FirstSOS == NULL) + FirstSOS = SOS; + else + LastSOS->next = SOS; + LastSOS = SOS; + + return(TRUE); +} + +static int add_sos_var(char *name) +{ + struct structSOSvars *SOSvar; + + if(name != NULL) { + if(CALLOC(SOSvar, 1, struct structSOSvars) == NULL) + return(FALSE); + + if(MALLOC(SOSvar->name, strlen(name) + 1, char) == NULL) + { + FREE(SOSvar); + return(FALSE); + } + strcpy(SOSvar->name, name); + + if(LastSOS->SOSvars == NULL) + LastSOS->SOSvars = SOSvar; + else + LastSOS->LastSOSvars->next = SOSvar; + LastSOS->LastSOSvars = SOSvar; + LastSOS->Nvars = LastSOS->Nvars + 1; + } + LastSOS->LastSOSvars->weight = 0; + + return(TRUE); +} + +void storevarandweight(char *name) +{ + if(!Ignore_int_decl) + add_int_var(name, int_decl); + else if(!Ignore_sec_decl) + add_sec_var(name); + else if(sos_decl==1) + add_sos_name(name); + else if(sos_decl==2) + add_sos_var(name); +} + +int set_sos_type(int SOStype) +{ + if(LastSOS != NULL) + LastSOS->type = (short) SOStype; + return(TRUE); +} + +int set_sos_weight(int weight, int sos_decl) +{ + if(sos_decl==1) + LastSOS->weight = weight; + else + LastSOS->LastSOSvars->weight = weight; + return(TRUE); +} + +void set_obj_dir(int maximise) +{ + Maximise = (short) maximise; +} + +static int inccoldata() +{ + if(Columns == 0) + CALLOC(coldata, coldatastep, struct structcoldata); + else if((Columns%coldatastep) == 0) + REALLOC(coldata, Columns + coldatastep, struct structcoldata); + + if(coldata != NULL) { + coldata[Columns].upbo = (REAL) DEF_INFINITE; + coldata[Columns].lowbo = (REAL) -DEF_INFINITE * (REAL) 10.0; /* temporary. If still this value then 0 will be taken */ + coldata[Columns].col = NULL; + coldata[Columns].firstcol = NULL; + coldata[Columns].must_be_int = FALSE; + coldata[Columns].must_be_sec = FALSE; + } + + return(coldata != NULL); +} + +/* + * initialisation of hashstruct and globals. + */ +static int init_read(int verbose) +{ + int ok = FALSE; + + Verbose = verbose; + set_obj_dir(TRUE); + Rows = 0; + Non_zeros = 0; + Columns = 0; + FirstSOS = LastSOS = NULL; + Lin_term_count = 0; + if (CALLOC(First_rside, 1, struct rside) != NULL) { + rs = First_rside; + rs->value = rs->range_value = 0; + /* first row (nr 0) is always the objective function */ + rs->relat = OF; + rs->range_relat = -1; + Hash_tab = NULL; + Hash_constraints = NULL; + if (((Hash_tab = create_hash_table(HASHSIZE, 0)) == NULL) || + ((Hash_constraints = create_hash_table(HASHSIZE, 0)) == NULL)){ + FREE(First_rside); + FREE(Hash_tab); + FREE(Hash_constraints); + } + else + ok = TRUE; + } + return(ok); +} /* init */ + +/* + * clears the tmp_store variable after all information has been copied + */ +void null_tmp_store(int init_Lin_term_count) +{ + tmp_store.value = 0; + tmp_store.rhs_value = 0; + tmp_store.name[0] = 0; + if(init_Lin_term_count) + Lin_term_count = 0; +} + +/* + * variable : pointer to text array with name of variable + * row : the rownumber of the constraint + * value : value of matrixelement + * A(row, variable). + * Sign : (global) determines the sign of value. + * store() : stores value in matrix + * A(row, variable). If A(row, variable) already contains data, + * value is added to the existing value. + */ +static int store(char *variable, + int row, + REAL value) +{ + hashelem *h_tab_p; + struct column *col_p; + + if(value == 0) { + char buf[256]; + + sprintf(buf, "(store) Warning, variable %s has an effective coefficient of 0, Ignored", variable); + error(NORMAL, buf); + /* return(TRUE); */ + } + + if((h_tab_p = findhash(variable, Hash_tab)) == NULL) { + if (((h_tab_p = puthash(variable, Columns, NULL, Hash_tab)) == NULL) + ) return(FALSE); + inccoldata(); + Columns++; /* counter for calloc of final array */ + if(value) { + if (CALLOC(col_p, 1, struct column) == NULL) + return(FALSE); + Non_zeros++; /* for calloc of final arrays */ + col_p->row = row; + col_p->value = value; + coldata[h_tab_p->index].firstcol = coldata[h_tab_p->index].col = col_p; + } + } + else if((coldata[h_tab_p->index].col == NULL) || (coldata[h_tab_p->index].col->row != row)) { + if(value) { + if (CALLOC(col_p, 1, struct column) == NULL) + return(FALSE); + Non_zeros++; /* for calloc of final arrays */ + if(coldata[h_tab_p->index].col != NULL) + coldata[h_tab_p->index].col->prev = col_p; + else + coldata[h_tab_p->index].firstcol = col_p; + col_p->value = value; + col_p->row = row; + col_p->next = coldata[h_tab_p->index].col; + coldata[h_tab_p->index].col = col_p; + } + } + else if(value) { + coldata[h_tab_p->index].col->value += value; + if(fabs(coldata[h_tab_p->index].col->value) < tol) /* eliminitate rounding errors */ + coldata[h_tab_p->index].col->value = 0; + } + return(TRUE); +} /* store */ + +static int storefirst() +{ + struct rside *rp; + + if ((rs != NULL) && (rs->row == tmp_store.row)) + return(TRUE); + + /* make space for the rhs information */ + if (CALLOC(rp, 1, struct rside) == NULL) + return(FALSE); + rp->next = First_rside; + First_rside = rs = rp; + rs->row = /* row */ tmp_store.row; + rs->value = tmp_store.rhs_value; + rs->relat = tmp_store.relat; + rs->range_relat = -1; + + if(tmp_store.value != 0) { + if (!store(tmp_store.name, tmp_store.row, tmp_store.value)) + return(FALSE); + } + else { + char buf[256]; + + sprintf(buf, "Warning, variable %s has an effective coefficient of 0, ignored", tmp_store.name); + error(NORMAL, buf); + } + null_tmp_store(FALSE); + return(TRUE); +} + +/* + * store relational operator given in yylex[0] in the rightside list. + * Also checks if it constraint was a bound and if so stores it in the + * boundslist + */ +int store_re_op(char *yytext, int HadConstraint, int HadVar, int Had_lineair_sum) +{ + short tmp_relat; + + switch(yytext[0]) { + + case '=': + tmp_relat = EQ; + break; + + case '>': + tmp_relat = GE; + break; + + case '<': + tmp_relat = LE; + break; + + default: + { + char buf[256]; + + sprintf(buf, "Error: unknown relational operator %s", yytext); + error(CRITICAL, buf); + } + return(FALSE); + break; + } + + if(/* Lin_term_count > 1 */ HadConstraint && HadVar) {/* it is not a bound */ + if(Lin_term_count == 1) + if(!storefirst()) + return(FALSE); + rs->relat = tmp_relat; + } + else if(/* Lin_term_count == 0 */ HadConstraint && !Had_lineair_sum /* HadVar */ /* && (rs != NULL) */) { /* it is a range */ + if(Lin_term_count == 1) + if(!storefirst()) + return(FALSE); + if(rs == NULL) { /* range before row, already reported */ + error(CRITICAL, "Error: range for undefined row"); + return(FALSE); + } + + if(rs->negate) + switch (tmp_relat) { + case LE: + tmp_relat = GE; + break; + case GE: + tmp_relat = LE; + break; + } + + if(rs->range_relat != -1) { + error(CRITICAL, "Error: There was already a range for this row"); + return(FALSE); + } + else if(tmp_relat == rs->relat) { + error(CRITICAL, "Error: relational operator for range is the same as relation operator for equation"); + return(FALSE); + } + else + rs->range_relat = tmp_relat; + } + else /* could be a bound */ + tmp_store.relat = tmp_relat; + + return(TRUE); +} /* store_re_op */ + +int negate_constraint() +{ + if(rs != NULL) + rs->negate = TRUE; + + return(TRUE); +} + +/* + * store RHS value in the rightside structure + * if type = true then + */ +int rhs_store(REAL value, int HadConstraint, int HadVar, int Had_lineair_sum) +{ + if(/* Lin_term_count > 1 */ (HadConstraint && HadVar) || (Rows == 0)){ /* not a bound */ + if (Rows == 0) + value = -value; + /* if(Lin_term_count < 2) */ + if(rs == NULL) + tmp_store.rhs_value += value; + else + + if(rs == NULL) { + error(CRITICAL, "Error: No variable specified"); + return(FALSE); + } + else + rs->value += value; + } + else if(/* Lin_term_count == 0 */ HadConstraint && !HadVar) { /* a range */ + if(rs == NULL) /* if range before row, already reported */ + tmp_store.rhs_value += value; + else if(rs->range_relat < 0) /* was a bad range; ignore */; + else { + if(rs->negate) + value = -value; + if(((rs->relat == LE) && (rs->range_relat == GE) && + (rs->value < value)) || + ((rs->relat == GE) && (rs->range_relat == LE) && + (rs->value > value)) || + ((rs->relat == EQ) || (rs->range_relat == EQ))) { + rs->range_relat = -2; + error(CRITICAL, "Error: range restriction conflicts"); + return(FALSE); + } + else + rs->range_value += value; + } + } + else /* a bound */ + tmp_store.rhs_value += value; + return(TRUE); +} /* RHS_store */ + +/* + * store all data in the right place + * count the amount of lineair terms in a constraint + * only store in data-structure if the constraint is not a bound + */ +int var_store(char *var, REAL value, int HadConstraint, int HadVar, int Had_lineair_sum) +{ + int row; + + row = Rows; + if(strlen(var) > MAXSTRL) { + char buf[256]; + + sprintf(buf, "Variable name '%-.*s' too long, at most %d characters allowed", + MAXSTRL, var, MAXSTRL); + error(CRITICAL, buf); + return(FALSE); + } + /* also in a bound the same var name can occur more than once. Check for + this. Don't increment Lin_term_count */ + + if(Lin_term_count != 1 || strcmp(tmp_store.name, var) != 0) + Lin_term_count++; + + /* always store objective function with rownr == 0. */ + if(row == 0) + return(store(var, row, value)); + + if(Lin_term_count == 1) { /* don't store yet. could be a bound */ + strcpy(tmp_store.name, var); + tmp_store.row = row; + tmp_store.value += value; + return(TRUE); + } + + if(Lin_term_count == 2) { /* now you can also store the first variable */ + if(!storefirst()) + return(FALSE); + /* null_tmp_store(FALSE); */ + } + + return(store(var, row, value)); +} /* var_store */ + + + +/* + * store the information in tmp_store because it is a bound + */ +int store_bounds(int warn) +{ + if(tmp_store.value != 0) { + hashelem *h_tab_p; + REAL boundvalue; + + if((h_tab_p = findhash(tmp_store.name, Hash_tab)) == NULL) { + /* a new columnname is found, create an entry in the hashlist */ + if ((h_tab_p = puthash(tmp_store.name, Columns, NULL, Hash_tab)) == NULL) { + error(CRITICAL, "Not enough memory"); + return(FALSE); + } + inccoldata(); + Columns++; /* counter for calloc of final array */ + } + + if(tmp_store.value < 0) { /* divide by negative number, */ + /* relational operator may change */ + if(tmp_store.relat == GE) + tmp_store.relat = LE; + else if(tmp_store.relat == LE) + tmp_store.relat = GE; + } + + boundvalue = tmp_store.rhs_value / tmp_store.value; + +#if FALSE + /* Check sanity of bound; all variables should be positive */ + if( ((tmp_store.relat == EQ) && (boundvalue < 0)) + || ((tmp_store.relat == LE) && (boundvalue < 0))) { /* Error */ + error(CRITICAL, "Error: variables must always be non-negative"); + return(FALSE); + } +#endif + +#if FALSE + if((tmp_store.relat == GE) && (boundvalue <= 0)) /* Warning */ + error(NORMAL, "Warning: useless bound; variables are always >= 0"); +#endif + + /* bound seems to be sane, add it */ + if((tmp_store.relat == GE) || (tmp_store.relat == EQ)) { + if(boundvalue > coldata[h_tab_p->index].lowbo - tol) + coldata[h_tab_p->index].lowbo = boundvalue; + else if(warn) + error(NORMAL, "Ineffective lower bound, ignored"); + } + if((tmp_store.relat == LE) || (tmp_store.relat == EQ)) { + if(boundvalue < coldata[h_tab_p->index].upbo + tol) + coldata[h_tab_p->index].upbo = boundvalue; + else if (warn) + error(NORMAL, "Ineffective upper bound, ignored"); + } + + /* check for empty range */ + if((warn) && (coldata[h_tab_p->index].upbo + tol < coldata[h_tab_p->index].lowbo)) { + error(CRITICAL, "Error: bound contradicts earlier bounds"); + return(FALSE); + } + } + else /* tmp_store.value = 0 ! */ { + char buf[256]; + + sprintf(buf, "Error, variable %s has an effective coefficient of 0 in bound", + tmp_store.name); + error(CRITICAL, buf); + return(FALSE); + } + + /* null_tmp_store(FALSE); */ + tmp_store.rhs_value = 0; + + return(TRUE); +} /* store_bounds */ + +int add_constraint_name(char *name) +{ + int row; + hashelem *hp; + + if(strlen(name) > MAXSTRL) { + char buf[256]; + + sprintf(buf, "Constraint name '%-.*s' too long, at most %d characters allowed", + MAXSTRL, name, MAXSTRL); + error(CRITICAL, buf); + return(FALSE); + } + + if((hp = findhash(name, Hash_constraints)) != NULL) { + row = hp->index; + rs = First_rside; + while ((rs != NULL) && (rs->row != row)) + rs = rs->next; + } + else { + row = Rows; + if (((hp = puthash(name, row, NULL, Hash_constraints)) == NULL) + ) return(FALSE); + rs = NULL; + } + + return(TRUE); +} + +/* + * transport the data from the intermediate structure to the sparse matrix + * and free the intermediate structure + */ +static int readinput(lprec *lp) +{ + int i, count, index; + struct column *cp, *tcp; + hashelem *hp; + struct rside *rp; + MYBOOL *negate = NULL; + REAL *row = NULL, a; + int *rowno = NULL; + + if(lp != NULL) { + if (CALLOC(negate, 1 + Rows, MYBOOL) == NULL) + return(FALSE); + + /* fill names with the rownames */ + hp = Hash_constraints->first; + while(hp != NULL) { + if (!set_row_name(lp, hp->index, hp->name)) + return(FALSE); + hp = hp->nextelem; + } + } + + for(i = Rows;i >= 0;i--) { + rp = First_rside; + if((lp != NULL) && (rp != NULL)) { + negate[i] = rp->negate; + if (rp->negate) { + switch (rp->relat) { + case LE: + rp->relat = GE; + break; + case GE: + rp->relat = LE; + break; + } + switch (rp->range_relat) { + case LE: + rp->range_relat = GE; + break; + case GE: + rp->range_relat = LE; + break; + } + rp->range_value = -rp->range_value; + rp->value = -rp->value; + } + + if((rp->range_relat >= 0) && (rp->value == lp->infinite)) { + rp->value = rp->range_value; + rp->relat = rp->range_relat; + rp->range_relat = -1; + } + else if((rp->range_relat >= 0) && (rp->value == -lp->infinite)) { + rp->value = rp->range_value; + rp->relat = rp->range_relat; + rp->range_relat = -1; + } + if ((rp->range_relat >= 0) && (rp->range_value == rp->value)) { + rp->relat = EQ; + rp->range_relat = EQ; + } + if(i) { + lp_solve_set_constr_type(lp, i, rp->relat); + relat[i] = rp->relat; + } + lp_solve_set_rh(lp, i, rp->value); + if (rp->range_relat >= 0) + /* lp->orig_upbo[i] = my_abs(rp->range_value - rp->value); */ + /* set_uprange(lp, i, my_abs(rp->range_value - rp->value)); */ + set_rh_range(lp, i, rp->range_value - rp->value); + } + if(rp != NULL) { + First_rside = rp->next; + free(rp); /* free memory when data has been read */ + } + else + First_rside = NULL; + } + + while(First_rside != NULL) { + rp = First_rside; + First_rside = rp->next; + free(rp); /* free memory when data has been read */ + } + + /* start reading the Hash_list structure */ + index = 0; + + if((lp != NULL) && + ((MALLOC(row, 1 + Rows, REAL) == NULL) || (MALLOC(rowno, 1 + Rows, int) == NULL))) { + FREE(negate); + FREE(row); + FREE(rowno); + return(FALSE); + } + + /* for(i = 0; i < Hash_tab->size; i++) { + hp = Hash_tab->table[i]; */ + hp = Hash_tab->first; + while(hp != NULL) { + count = 0; + index++; + cp = coldata[hp->index].firstcol; + while(cp != NULL) { + if(lp != NULL) { + rowno[count] = cp->row; + a = cp->value; + if (negate[cp->row]) + a = -a; + row[count++] = a; + } + tcp = cp; + /* cp = cp->next; */ + cp = cp->prev; + free(tcp); /* free memory when data has been read */ + } + + if(lp != NULL) { + add_columnex(lp, count, row, rowno); + /* check for bound */ + if(coldata[hp->index].lowbo == -DEF_INFINITE * 10.0) + /* lp->orig_lowbo[Rows+index] = 0.0; */ + lp_solve_set_lowbo(lp, index, 0); + else + /* lp->orig_lowbo[Rows+index] = coldata[hp->index].lowbo; */ + lp_solve_set_lowbo(lp, index, coldata[hp->index].lowbo); + /* lp->orig_upbo[Rows+index] = coldata[hp->index].upbo; */ + lp_solve_set_upbo(lp, index, coldata[hp->index].upbo); + + /* check if it must be an integer variable */ + if(coldata[hp->index].must_be_int) { + /* lp->must_be_int[Rows + index]=TRUE; */ + lp_solve_set_int(lp, index, TRUE); + } + if(coldata[hp->index].must_be_sec) { + set_semicont(lp, index, TRUE); + } + + /* copy name of column variable */ + if (!set_col_name(lp, index, hp->name)) { + FREE(negate); + FREE(row); + FREE(rowno); + return(FALSE); + } + + /* put matrix values in intermediate row */ + /* cp = hp->col; */ + /* cp = hp->firstcol; */ + } + + /* thp = hp; */ + /* hp = hp->next; */ + /* free(thp->name); */ + /* free(thp); */ /* free memory when data has been read */ + + hp = hp->nextelem; + + } + /* Hash_tab->table[i] = NULL; */ + + FREE(coldata); + + while(FirstSOS != NULL) + { + struct structSOSvars *SOSvars, *SOSvars1; + int *sosvars, n; + REAL *weights; + hashelem *hp; + + LastSOS = FirstSOS; + FirstSOS = FirstSOS->next; + SOSvars = LastSOS->SOSvars; + if(lp != NULL) { + MALLOC(sosvars, LastSOS->Nvars, int); + MALLOC(weights, LastSOS->Nvars, double); + } + else { + sosvars = NULL; + weights = NULL; + } + n = 0; + while(SOSvars != NULL) + { + SOSvars1 = SOSvars; + SOSvars = SOSvars->next; + if((lp != NULL) && ((hp = findhash(SOSvars1->name, lp->colname_hashtab)) != NULL)) { + sosvars[n] = hp->index; + weights[n++] = SOSvars1->weight; + } + FREE(SOSvars1->name); + FREE(SOSvars1); + } + if(lp != NULL) { + add_SOS(lp, LastSOS->name, LastSOS->type, LastSOS->weight, n, sosvars, weights); + FREE(weights); + FREE(sosvars); + } + FREE(LastSOS->name); + FREE(LastSOS); + } + + /* the following should be replaced by a call to the MPS print routine MB */ + +#if 0 + if(Verbose) { + int j; + + printf("\n"); + printf("**********Data read**********\n"); + printf("Rows : %d\n", Rows); + printf("Columns : %d\n", Columns); + printf("Nonnuls : %d\n", Non_zeros); + printf("NAME LPPROB\n"); + printf("ROWS\n"); + for(i = 0; i <= Rows; i++) { + if(relat[i] == LE) + printf(" L "); + else if(relat[i] == EQ) + printf(" E "); + else if(relat[i] == GE) + printf(" G "); + else if(relat[i] == OF) + printf(" N "); + printf("%s\n", get_row_name(lp, i)); + } + + printf("COLUMNS\n"); + j = 0; + for(i = 0; i < Non_zeros; i++) { + if(i == lp->col_end[j]) + j++; + printf(" %-8s %-8s %g\n", get_col_name(lp, j), + get_row_name(lp, lp->mat[i].row_nr), (double)lp->mat[i].value); + } + + printf("RHS\n"); + for(i = 0; i <= Rows; i++) { + printf(" RHS %-8s %g\n", get_row_name(lp, i), + (double)lp->orig_rh[i]); + } + + printf("RANGES\n"); + for(i = 1; i <= Rows; i++) + if((lp->orig_upbo[i] != lp->infinite) && (lp->orig_upbo[i] != 0)) { + printf(" RGS %-8s %g\n", get_row_name(lp, i), + (double)lp->orig_upbo[i]); + } + else if((lp->orig_lowbo[i] != 0)) { + printf(" RGS %-8s %g\n", get_row_name(lp, i), + (double)-lp->orig_lowbo[i]); + } + + printf("BOUNDS\n"); + for(i = Rows + 1; i <= Rows + Columns; i++) { + if((lp->orig_lowbo[i] != 0) && (lp->orig_upbo[i] < lp->infinite) && + (lp->orig_lowbo[i] == lp->orig_upbo[i])) { + printf(" FX BND %-8s %g\n", get_col_name(lp, i - Rows), + (double)lp->orig_upbo[i]); + } + else { + if(lp->orig_upbo[i] < lp->infinite) + printf(" UP BND %-8s %g\n", get_col_name(lp, i - Rows), + (double)lp->orig_upbo[i]); + if(lp->orig_lowbo[i] > 0) + printf(" LO BND %-8s %g\n", get_col_name(lp, i - Rows), + (double)lp->orig_lowbo[i]); + } + } + + printf("ENDATA\n"); + } +#endif + + FREE(row); + FREE(rowno); + FREE(negate); + return(TRUE); +} /* readinput */ + +lprec *yacc_read(int verbose, char *lp_name, int *_lineno, int (*parse) (void), void (*delete_allocated_memory) (void)) +{ + lprec *lp = NULL; + REAL *orig_upbo; + int stat = -1; + + lineno = _lineno; + + if(!init_read(verbose)) + error(CRITICAL, "init_read failed"); + else if (setjmp(jump_buf) == 0) + stat = parse(); + + delete_allocated_memory(); + + Rows--; + + relat = NULL; + if((stat != 0) || (CALLOC(relat, Rows + 1, short) != NULL)) { + if(stat == 0) + lp = lp_solve_make_lp(Rows, 0); + if ((stat != 0) || (lp != NULL)) { + if(lp != NULL) { + set_lp_name(lp, lp_name); + set_verbose(lp, Verbose); + } + + if (!readinput(lp)) { + if(lp != NULL) + lp_solve_delete_lp(lp); + lp = NULL; + } + + if(lp != NULL) { + if(Maximise) + lp_solve_set_maxim(lp); + + if(Rows) { + int row; + + MALLOCCPY(orig_upbo, lp->orig_upbo, 1 + Rows, REAL); + for(row = 1; row <= Rows; row++) + lp_solve_set_constr_type(lp, row, relat[row]); + + memcpy(lp->orig_upbo, orig_upbo, (1 + Rows) * sizeof(*orig_upbo)); /* restore upper bounds (range) */ + FREE(orig_upbo); + } + } + + free_hash_table(Hash_tab); + free_hash_table(Hash_constraints); + } + FREE(relat); + } + return(lp); +} Index: src/tools/solver/lp_solve/yacc_read.h =================================================================== RCS file: src/tools/solver/lp_solve/yacc_read.h diff -N src/tools/solver/lp_solve/yacc_read.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/tools/solver/lp_solve/yacc_read.h 24 Jul 2004 02:52:40 -0000 1.1 @@ -0,0 +1,23 @@ +/* prototypes of functions used in the parser */ + +#ifndef __READ_H__ +#define __READ_H__ + +void lex_fatal_error(char const *msg); +int add_constraint_name(char *name); +int store_re_op(char *yytext, int HadConstraint, int HadVar, int Had_lineair_sum); +void null_tmp_store(int init_Lin_term_count); +int store_bounds(int warn); +void storevarandweight(char *name); +int set_sos_type(int SOStype); +int set_sos_weight(int weight, int sos_decl); +int rhs_store(REAL value, int HadConstraint, int HadVar, int Had_lineair_sum); +int var_store(char *var, REAL value, int HadConstraint, int HadVar, int Had_lineair_sum); +int negate_constraint(void); +void add_row(void); +void set_obj_dir(int maximise); + +void read_error(char const *); +void check_int_sec_sos_decl(int, int, int); +lprec *yacc_read(int verbose, char *lp_name, int *_lineno, int (*parse) (void), void (*delete_allocated_memory) (void)); +#endif Index: src/widgets/ChangeLog =================================================================== RCS file: /cvs/gnome/gnumeric/src/widgets/ChangeLog,v retrieving revision 1.310 retrieving revision 1.311 diff -u -w -r1.310 -r1.311 --- src/widgets/ChangeLog 19 Jul 2004 12:40:58 -0000 1.310 +++ src/widgets/ChangeLog 28 Jul 2004 19:35:55 -0000 1.311 @@ -1,3 +1,12 @@ +2004-07-28 Morten Welinder + + * gnumeric-cell-renderer-text.c + (gnumeric_cell_renderer_text_render): Fix type of flags. + * gnumeric-cell-renderer-toggle.c + (gnumeric_cell_renderer_toggle_render): Ditto. + + * gnumeric-lazy-list.c (lazy_list_get_flags): Correct return type. + 2004-07-19 Jody Goldberg * Release 1.3.1 Index: src/widgets/gnm-dao.c =================================================================== RCS file: /cvs/gnome/gnumeric/src/widgets/gnm-dao.c,v retrieving revision 1.7 retrieving revision 1.8 diff -u -w -r1.7 -r1.8 --- src/widgets/gnm-dao.c 22 May 2004 02:41:40 -0000 1.7 +++ src/widgets/gnm-dao.c 27 Jul 2004 21:35:23 -0000 1.8 @@ -172,7 +172,7 @@ "newworkbook-button", "outputrange-button", "inplace-button", - 0 + NULL }; static void Index: src/widgets/gnumeric-cell-renderer-text.c =================================================================== RCS file: /cvs/gnome/gnumeric/src/widgets/gnumeric-cell-renderer-text.c,v retrieving revision 1.8 retrieving revision 1.9 diff -u -w -r1.8 -r1.9 --- src/widgets/gnumeric-cell-renderer-text.c 3 Jul 2004 04:43:47 -0000 1.8 +++ src/widgets/gnumeric-cell-renderer-text.c 28 Jul 2004 19:35:55 -0000 1.9 @@ -57,7 +57,7 @@ GdkRectangle *background_area, GdkRectangle *cell_area, GdkRectangle *expose_area, - guint flags) + GtkCellRendererState flags) { GtkCellRendererText *celltext = (GtkCellRendererText *) cell; @@ -126,7 +126,6 @@ parent_class = g_type_class_peek_parent (object_class); cell_class->render = gnumeric_cell_renderer_text_render; - } Index: src/widgets/gnumeric-cell-renderer-toggle.c =================================================================== RCS file: /cvs/gnome/gnumeric/src/widgets/gnumeric-cell-renderer-toggle.c,v retrieving revision 1.8 retrieving revision 1.9 diff -u -w -r1.8 -r1.9 --- src/widgets/gnumeric-cell-renderer-toggle.c 3 Jul 2004 04:43:47 -0000 1.8 +++ src/widgets/gnumeric-cell-renderer-toggle.c 28 Jul 2004 19:35:55 -0000 1.9 @@ -46,7 +46,7 @@ GdkRectangle *background_area, GdkRectangle *cell_area, GdkRectangle *expose_area, - guint flags); + GtkCellRendererState flags); static void gnumeric_cell_renderer_toggle_class_init (GnumericCellRendererToggleClass *cell_toggle_class); @@ -222,7 +222,7 @@ G_GNUC_UNUSED GdkRectangle *background_area, GdkRectangle *cell_area, G_GNUC_UNUSED GdkRectangle *expose_area, - G_GNUC_UNUSED guint flags) + G_GNUC_UNUSED GtkCellRendererState flags) { GnumericCellRendererToggle *cellpixbuf = (GnumericCellRendererToggle *) cell; GdkPixbuf *pixbuf; Index: src/widgets/gnumeric-expr-entry.c =================================================================== RCS file: /cvs/gnome/gnumeric/src/widgets/gnumeric-expr-entry.c,v retrieving revision 1.122 retrieving revision 1.123 diff -u -w -r1.122 -r1.123 --- src/widgets/gnumeric-expr-entry.c 28 May 2004 03:44:10 -0000 1.122 +++ src/widgets/gnumeric-expr-entry.c 23 Jul 2004 03:59:34 -0000 1.123 @@ -300,12 +300,13 @@ gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (gee)); return TRUE; } else - wbcg_edit_finish (wbcg, FALSE, NULL); + wbcg_edit_finish (wbcg, WBC_EDIT_REJECT, NULL); return TRUE; case GDK_KP_Enter: case GDK_Return: { SheetView *sv; + WBCEditResult result; if (gee->is_cell_renderer) return FALSE; @@ -325,21 +326,12 @@ sv = sheet_get_view (wbcg->wb_control.editing_sheet, wb_control_view (WORKBOOK_CONTROL (wbcg))); - if (state == GDK_CONTROL_MASK || - state == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) { - gboolean const is_array = (state & GDK_SHIFT_MASK); - char const *text = gtk_entry_get_text ( - wbcg_get_entry (wbcg)); - - cmd_area_set_text (WORKBOOK_CONTROL (wbcg), sv, - text, is_array); - - /* do NOT store the results If the assignment was - * successful it will have taken care of that. - */ - wbcg_edit_finish (wbcg, FALSE, NULL); - } else if (wbcg_edit_finish (wbcg, TRUE, NULL)) { - /* move the edit pos */ + if (state & GDK_CONTROL_MASK) + result = (state & GDK_SHIFT_MASK) ? WBC_EDIT_ACCEPT_ARRAY : WBC_EDIT_ACCEPT_RANGE; + else + result = WBC_EDIT_ACCEPT; + /* move the edit pos for normal entry */ + if (wbcg_edit_finish (wbcg, result, NULL) && result == WBC_EDIT_ACCEPT) { gboolean const direction = (event->state & GDK_SHIFT_MASK) ? FALSE : TRUE; sv_selection_walk_step (sv, direction, FALSE); sv_update (sv); Index: src/widgets/gnumeric-lazy-list.c =================================================================== RCS file: /cvs/gnome/gnumeric/src/widgets/gnumeric-lazy-list.c,v retrieving revision 1.4 retrieving revision 1.6 diff -u -w -r1.4 -r1.6 --- src/widgets/gnumeric-lazy-list.c 7 Jan 2004 03:21:54 -0000 1.4 +++ src/widgets/gnumeric-lazy-list.c 28 Jul 2004 19:35:55 -0000 1.6 @@ -42,7 +42,7 @@ /* Fulfill the GtkTreeModel requirements */ -static guint +static GtkTreeModelFlags lazy_list_get_flags (GtkTreeModel *tree_model) { g_return_val_if_fail (GNUMERIC_IS_LAZY_LIST (tree_model), 0); @@ -151,7 +151,7 @@ */ iter->stamp = ll->stamp; - iter->user_data = 0; + iter->user_data = NULL; return ll->rows > 0; } Index: tools/news-to-html =================================================================== RCS file: tools/news-to-html diff -N tools/news-to-html --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tools/news-to-html 6 Aug 2004 14:47:24 -0000 1.1 @@ -0,0 +1,131 @@ +#!/usr/bin/perl -w +# ----------------------------------------------------------------------------- + +# Note: this isn't perfect, but a very fine start. + +my $version = $ARGV[0]; +die "$0: must specify version number as argument.\n" unless $version; + +my $bug_prefix = "http://bugzilla.gnome.org/show_bug.cgi?id="; + +&prolog (); + +my $state = 0; +my $who = undef; +my $item = ''; +my $have_item = 0; +my $bug = undef; +open (FIL, ") { + chomp; + + if (/^Gnumeric\s*(\S*)\s*:?\s*$/) { + $state = 1 if $1 eq $version; + next; + } + + if (/^-+$/) { + $state = 0; + next; + } + + if ($state == 1) { + if (/^(.*\S)\s*:\s*$/) { + &finish_author () if $who; + $who = $1; + $have_item = 0; + next; + } + + if (s/^\s+\*\s*//) { + &finish_item () if $have_item; + $item .= "
  • "; + &add_to_item ($_); + next; + } + + if (m{^\s+http://bugzilla\.gnome\.org/show_bug\.cgi\?id=(\d+)\s*}) { + my $this_bug = $1; + &finish_item () if $have_item; + $bug = $this_bug; + next; + } + + if ($have_item && s/^\s+//) { + &add_to_item ($_); + next; + } + } +} +close (FIL); +&finish_author () if $who; + +&epilog (); + +# ----------------------------------------------------------------------------- + +sub finish_author { + &finish_item () if $have_item; + + print "
  • $who\n"; + print "
      \n"; + print $item; + print "
    \n"; + $item = ''; + $who = undef; +} + +sub finish_item { + if ($item && $item !~ /[.?!]\s*$/) { + $item .= "."; + } + if ($bug) { + $item .= " (Bug $bug.)"; + $bug = ''; + } + $item =~ s/\s+$//; + $item .= "\n
  • \n"; + $have_item = 0; +} + +sub add_to_item { + my ($txt) = @_; + + $txt =~ s{\#(\d\d\d\d\d+)}{Bug $1}; + + $item .= "\n\t$txt"; + $have_item = 1; +} + +# ----------------------------------------------------------------------------- + +sub prolog { + print "\n"; + print "\n"; + print " \n"; + print " Gnumeric $version Release Notes\n"; + print "\n"; + + print "\n"; + print "

    Gnumeric $version aka "" is now available.

    \n"; + print "
      \n"; +} + +# ----------------------------------------------------------------------------- + +sub epilog { + my $link = "http://ftp.gnome.org/pub/GNOME/sources/gnumeric/1.3/"; + + print "
    \n"; + + print "

    Availability

    \n"; + print "
    \n"; + print " $link\n"; + print "
    \n"; + + print "\n"; + print "\n"; +} + +# -----------------------------------------------------------------------------