#include #include #include #include #include "commonlib.h" #include "lp_lib.h" #include "lp_report.h" #include "ini.h" typedef int (__WINAPI fn_int_get_function)(lprec *lp); typedef long (__WINAPI fn_long_get_function)(lprec *lp); typedef MYBOOL (__WINAPI fn_MYBOOL_get_function)(lprec *lp); typedef REAL (__WINAPI fn_REAL_get_function)(lprec *lp); typedef void (__WINAPI fn_int_set_function)(lprec *lp, int value); typedef void (__WINAPI fn_long_set_function)(lprec *lp, long value); typedef void (__WINAPI fn_MYBOOL_set_function)(lprec *lp, MYBOOL value); typedef void (__WINAPI fn_REAL_set_function)(lprec *lp, REAL value); #define intfunction 1 #define longfunction 2 #define MYBOOLfunction 3 #define REALfunction 4 #define setvalues(values, basemask) values, sizeof(values) / sizeof(*values), basemask #define setNULLvalues NULL, 0, 0 #define setvalue(value) value, #value #define setintfunction(get_function, set_function) { get_function }, { set_function }, intfunction #define setlongfunction(get_function, set_function) { (fn_int_get_function *) get_function }, {(fn_int_set_function *) set_function }, longfunction #define setMYBOOLfunction(get_function, set_function) { (fn_int_get_function *) get_function }, { (fn_int_set_function *) set_function }, MYBOOLfunction #define setREALfunction(get_function, set_function) {(fn_int_get_function *) get_function }, { (fn_int_set_function *) set_function }, REALfunction #define WRITE_COMMENTED 0 #define WRITE_ACTIVE 1 struct _values { int value; char *svalue; }; struct _functions { char *par; /* name of parameter in ini file */ union { fn_int_get_function *int_get_function; /* set via setintfunction */ fn_long_get_function *long_get_function; /* set via setlongfunction */ fn_MYBOOL_get_function *MYBOOL_get_function; /* set via setMYBOOLfunction */ fn_REAL_get_function *REAL_get_function; /* set via setREALfunction */ } get_function; union { fn_int_set_function *int_set_function; /* set via setintfunction */ fn_long_set_function *long_set_function; /* set via setlongfunction */ fn_MYBOOL_set_function *MYBOOL_set_function; /* set via setMYBOOLfunction */ fn_REAL_set_function *REAL_set_function; /* set via setREALfunction */ } set_function; int type; /* set via set*function */ struct _values *values; /* set via setvalues to a structure of _values */ int elements; /* or via setNULLvalues if the value is shown as is */ unsigned int basemask; int mask; /* WRITE_ACTIVE or WRITE_COMMENTED */ }; static struct _values anti_degen[] = { { setvalue(ANTIDEGEN_NONE) }, { setvalue(ANTIDEGEN_FIXEDVARS) }, { setvalue(ANTIDEGEN_COLUMNCHECK) }, { setvalue(ANTIDEGEN_STALLING) }, { setvalue(ANTIDEGEN_NUMFAILURE) }, { setvalue(ANTIDEGEN_LOSTFEAS) }, { setvalue(ANTIDEGEN_INFEASIBLE) }, { setvalue(ANTIDEGEN_DYNAMIC) }, { setvalue(ANTIDEGEN_DURINGBB) }, { setvalue(ANTIDEGEN_RHSPERTURB) }, { setvalue(ANTIDEGEN_BOUNDFLIP) }, }; static struct _values basiscrash[] = { { setvalue(CRASH_NONE) }, /* { setvalue(CRASH_NONBASICBOUNDS) }, */ /* not yet implemented */ { setvalue(CRASH_MOSTFEASIBLE) }, { setvalue(CRASH_LEASTDEGENERATE) }, }; static struct _values bb_floorfirst[] = { { setvalue(BRANCH_CEILING) }, { setvalue(BRANCH_FLOOR) }, { setvalue(BRANCH_AUTOMATIC) }, }; static struct _values bb_rule[] = { { setvalue(NODE_FIRSTSELECT) }, { setvalue(NODE_GAPSELECT) }, { setvalue(NODE_RANGESELECT) }, { setvalue(NODE_FRACTIONSELECT) }, { setvalue(NODE_PSEUDOCOSTSELECT) }, { setvalue(NODE_PSEUDONONINTSELECT) }, { setvalue(NODE_PSEUDORATIOSELECT) }, { setvalue(NODE_USERSELECT) }, { setvalue(NODE_WEIGHTREVERSEMODE) }, { setvalue(NODE_BRANCHREVERSEMODE) }, { setvalue(NODE_GREEDYMODE) }, { setvalue(NODE_PSEUDOCOSTMODE) }, { setvalue(NODE_DEPTHFIRSTMODE) }, { setvalue(NODE_RANDOMIZEMODE) }, { setvalue(NODE_GUBMODE) }, { setvalue(NODE_DYNAMICMODE) }, { setvalue(NODE_RESTARTMODE) }, { setvalue(NODE_BREADTHFIRSTMODE) }, { setvalue(NODE_AUTOORDER) }, { setvalue(NODE_RCOSTFIXING) }, { setvalue(NODE_STRONGINIT) }, }; static struct _values improve[] = { { setvalue(IMPROVE_NONE) }, { setvalue(IMPROVE_SOLUTION) }, { setvalue(IMPROVE_DUALFEAS) }, { setvalue(IMPROVE_THETAGAP) }, { setvalue(IMPROVE_BBSIMPLEX) }, }; static REAL __WINAPI get_mip_gap_abs(lprec *lp) { return(get_mip_gap(lp, TRUE)); } static REAL __WINAPI get_mip_gap_rel(lprec *lp) { return(get_mip_gap(lp, FALSE)); } static void __WINAPI set_mip_gap_abs(lprec *lp, REAL mip_gap) { set_mip_gap(lp, TRUE, mip_gap); } static void __WINAPI set_mip_gap_rel(lprec *lp, REAL mip_gap) { set_mip_gap(lp, FALSE, mip_gap); } static struct _values pivoting[] = { { setvalue(PRICER_FIRSTINDEX) }, { setvalue(PRICER_DANTZIG) }, { setvalue(PRICER_DEVEX) }, { setvalue(PRICER_STEEPESTEDGE) }, { setvalue(PRICE_PRIMALFALLBACK) }, { setvalue(PRICE_MULTIPLE) }, { setvalue(PRICE_PARTIAL) }, { setvalue(PRICE_ADAPTIVE) }, { setvalue(PRICE_RANDOMIZE) }, { setvalue(PRICE_AUTOPARTIAL) }, { setvalue(PRICE_LOOPLEFT) }, { setvalue(PRICE_LOOPALTERNATE) }, { setvalue(PRICE_HARRISTWOPASS) }, { setvalue(PRICE_TRUENORMINIT) }, }; static struct _values presolving[] = { { setvalue(PRESOLVE_NONE) }, { setvalue(PRESOLVE_ROWS) }, { setvalue(PRESOLVE_COLS) }, { setvalue(PRESOLVE_LINDEP) }, { setvalue(PRESOLVE_AGGREGATE) }, { setvalue(PRESOLVE_SPARSER) }, { setvalue(PRESOLVE_SOS) }, { setvalue(PRESOLVE_REDUCEMIP) }, { setvalue(PRESOLVE_KNAPSACK) }, { setvalue(PRESOLVE_ELIMEQ2) }, { setvalue(PRESOLVE_IMPLIEDFREE) }, { setvalue(PRESOLVE_REDUCEGCD) }, { setvalue(PRESOLVE_PROBEFIX) }, { setvalue(PRESOLVE_PROBEREDUCE) }, { setvalue(PRESOLVE_ROWDOMINATE) }, { setvalue(PRESOLVE_COLDOMINATE) }, { setvalue(PRESOLVE_MERGEROWS) }, { setvalue(PRESOLVE_IMPLIEDSLK) }, { setvalue(PRESOLVE_COLFIXDUAL) }, { setvalue(PRESOLVE_BOUNDS) }, { setvalue(PRESOLVE_DUALS) }, { setvalue(PRESOLVE_SENSDUALS) }, }; static char *STRLWR(char *str) { char *ptr; for(ptr = str; *ptr; ptr++) *ptr = (char) tolower((unsigned char) *ptr); return(str); } static char *STRUPR(char *str) { char *ptr; for(ptr = str; *ptr; ptr++) *ptr = (char) toupper((unsigned char) *ptr); return(str); } static void __WINAPI set_presolve1(lprec *lp, int do_presolve) { set_presolve(lp, do_presolve, get_presolveloops(lp)); } static void __WINAPI set_presolve2(lprec *lp, int maxloops) { set_presolve(lp, get_presolve(lp), maxloops); } static struct _values print_sol[] = { { FALSE, "0" }, { TRUE, "1" }, { setvalue(AUTOMATIC) }, }; static struct _values scaling[] = { { setvalue(SCALE_NONE) }, { setvalue(SCALE_EXTREME) }, { setvalue(SCALE_RANGE) }, { setvalue(SCALE_MEAN) }, { setvalue(SCALE_GEOMETRIC) }, { setvalue(SCALE_CURTISREID) }, { setvalue(SCALE_QUADRATIC) }, { setvalue(SCALE_LOGARITHMIC) }, { setvalue(SCALE_USERWEIGHT) }, { setvalue(SCALE_POWER2) }, { setvalue(SCALE_EQUILIBRATE) }, { setvalue(SCALE_INTEGERS) }, { setvalue(SCALE_DYNUPDATE) }, { setvalue(SCALE_ROWSONLY) }, { setvalue(SCALE_COLSONLY) }, }; static struct _values simplextype[] = { { setvalue(SIMPLEX_PRIMAL_PRIMAL) }, { setvalue(SIMPLEX_DUAL_PRIMAL) }, { setvalue(SIMPLEX_PRIMAL_DUAL) }, { setvalue(SIMPLEX_DUAL_DUAL) }, }; static struct _values verbose[] = { { setvalue(NEUTRAL) }, { setvalue(CRITICAL) }, { setvalue(SEVERE) }, { setvalue(IMPORTANT) }, { setvalue(NORMAL) }, { setvalue(DETAILED) }, { setvalue(FULL) }, }; static struct _functions functions[] = { /* solve options */ { "ANTI_DEGEN", setintfunction(get_anti_degen, set_anti_degen), setvalues(anti_degen, ~0), WRITE_ACTIVE }, { "BASISCRASH", setintfunction(get_basiscrash, set_basiscrash), setvalues(basiscrash, ~0), WRITE_ACTIVE }, { "IMPROVE", setintfunction(get_improve, set_improve), setvalues(improve, ~0), WRITE_ACTIVE }, { "MAXPIVOT", setintfunction(get_maxpivot, set_maxpivot), setNULLvalues, WRITE_ACTIVE }, { "NEGRANGE", setREALfunction(get_negrange, set_negrange), setNULLvalues, WRITE_ACTIVE }, { "PIVOTING", setintfunction(get_pivoting, set_pivoting), setvalues(pivoting, PRICER_LASTOPTION), WRITE_ACTIVE }, { "PRESOLVE", setintfunction(get_presolve, set_presolve1), setvalues(presolving, ~0), WRITE_ACTIVE }, { "PRESOLVELOOPS", setintfunction(get_presolveloops, set_presolve2), setNULLvalues, WRITE_ACTIVE }, { "SCALELIMIT", setREALfunction(get_scalelimit, set_scalelimit), setNULLvalues, WRITE_ACTIVE }, { "SCALING", setintfunction(get_scaling, set_scaling), setvalues(scaling, SCALE_CURTISREID), WRITE_ACTIVE }, { "SIMPLEXTYPE", setintfunction(get_simplextype, set_simplextype), setvalues(simplextype, ~0), WRITE_ACTIVE }, { "OBJ_IN_BASIS", setMYBOOLfunction(is_obj_in_basis, set_obj_in_basis), setNULLvalues, WRITE_COMMENTED }, /* B&B options */ { "BB_DEPTHLIMIT", setintfunction(get_bb_depthlimit, set_bb_depthlimit), setNULLvalues, WRITE_ACTIVE }, { "BB_FLOORFIRST", setintfunction(get_bb_floorfirst, set_bb_floorfirst), setvalues(bb_floorfirst, ~0), WRITE_ACTIVE }, { "BB_RULE", setintfunction(get_bb_rule, set_bb_rule), setvalues(bb_rule, NODE_STRATEGYMASK), WRITE_ACTIVE }, { "BREAK_AT_FIRST", setMYBOOLfunction(is_break_at_first, set_break_at_first), setNULLvalues, WRITE_COMMENTED }, { "BREAK_AT_VALUE", setREALfunction(get_break_at_value, set_break_at_value), setNULLvalues, WRITE_COMMENTED }, { "MIP_GAP_ABS", setREALfunction(get_mip_gap_abs, set_mip_gap_abs), setNULLvalues, WRITE_ACTIVE }, { "MIP_GAP_REL", setREALfunction(get_mip_gap_rel, set_mip_gap_rel), setNULLvalues, WRITE_ACTIVE }, { "EPSINT", setREALfunction(get_epsint, set_epsint), setNULLvalues, WRITE_ACTIVE }, /* tolerances, values */ { "EPSB", setREALfunction(get_epsb, set_epsb), setNULLvalues, WRITE_ACTIVE }, { "EPSD", setREALfunction(get_epsd, set_epsd), setNULLvalues, WRITE_ACTIVE }, { "EPSEL", setREALfunction(get_epsel, set_epsel), setNULLvalues, WRITE_ACTIVE }, { "EPSPERTURB", setREALfunction(get_epsperturb, set_epsperturb), setNULLvalues, WRITE_ACTIVE }, { "EPSPIVOT", setREALfunction(get_epspivot, set_epspivot), setNULLvalues, WRITE_ACTIVE }, { "INFINITE", setREALfunction(get_infinite, set_infinite), setNULLvalues, WRITE_ACTIVE }, /* read-only options */ { "DEBUG", setMYBOOLfunction(is_debug, set_debug), setNULLvalues, WRITE_COMMENTED }, { "OBJ_BOUND", setREALfunction(get_obj_bound, set_obj_bound), setNULLvalues, WRITE_COMMENTED }, { "PRINT_SOL", setintfunction(get_print_sol, set_print_sol), setvalues(print_sol, ~0), WRITE_COMMENTED }, { "TIMEOUT", setlongfunction(get_timeout, set_timeout), setNULLvalues, WRITE_COMMENTED }, { "TRACE", setMYBOOLfunction(is_trace, set_trace), setNULLvalues, WRITE_COMMENTED }, { "VERBOSE", setintfunction(get_verbose, set_verbose), setvalues(verbose, ~0), WRITE_COMMENTED }, }; static void write_params1(lprec *lp, FILE *fp, char *header, int newline) { int ret = 0, ret2, i, j, k, value, value2, elements, majorversion, minorversion, release, build; unsigned int basemask; REAL a = 0; char buf[4096], par[20]; ini_writeheader(fp, header, newline); lp_solve_version(&majorversion, &minorversion, &release, &build); sprintf(buf, "lp_solve version %d.%d settings\n", majorversion, minorversion); ini_writecomment(fp, buf); for(i = 0; i < sizeof(functions) / sizeof(*functions); i++) { switch(functions[i].type) { case intfunction: if(functions[i].get_function.int_get_function == NULL) continue; ret = functions[i].get_function.int_get_function(lp); break; case longfunction: if(functions[i].get_function.long_get_function == NULL) continue; ret = functions[i].get_function.long_get_function(lp); break; case MYBOOLfunction: if(functions[i].get_function.MYBOOL_get_function == NULL) continue; ret = (int) functions[i].get_function.MYBOOL_get_function(lp); break; case REALfunction: if(functions[i].get_function.REAL_get_function == NULL) continue; a = functions[i].get_function.REAL_get_function(lp); break; } buf[0] = 0; if(functions[i].values == NULL) { switch(functions[i].type) { case intfunction: case longfunction: case MYBOOLfunction: sprintf(buf, "%d", ret); break; case REALfunction: sprintf(buf, "%g", a); break; } } else { elements = functions[i].elements; basemask = functions[i].basemask; for(j = 0; j < elements; j++) { value = functions[i].values[j].value; ret2 = ret; if(((unsigned int) value) < basemask) ret2 &= basemask; if(value == 0) { if(ret2 == 0) { if(*buf) strcat(buf, " + "); strcat(buf, functions[i].values[j].svalue); } } else if((ret2 & value) == value) { for(k = 0; k < elements; k++) { value2 = functions[i].values[k].value; if((k != j) && (value2 > value) && ((value2 & value) == value) && ((ret2 & value2) == value2)) break; } if(k == elements) { if(*buf) strcat(buf, " + "); strcat(buf, functions[i].values[j].svalue); } } } } if(functions[i].mask & WRITE_ACTIVE) par[0] = 0; else strcpy(par, ";"); strcat(par, functions[i].par); ini_writedata(fp, STRLWR(par), buf); } } static void readoptions(char *options, char **header) { char *ptr1, *ptr2; if(options != NULL) { ptr1 = options; while(*ptr1) { ptr2 = strchr(ptr1, '-'); if(ptr2 == NULL) break; ptr2++; if(tolower((unsigned char) *ptr2) == 'h') { for(++ptr2; (*ptr2) && (isspace(*ptr2)); ptr2++); for(ptr1 = ptr2; (*ptr1) && (!isspace(*ptr1)); ptr1++); *header = (char *) calloc(1 + (int) (ptr1 - ptr2), 1); memcpy(*header, ptr2, (int) (ptr1 - ptr2)); } } } if(*header == NULL) *header = strdup("Default"); } MYBOOL __WINAPI write_params(lprec *lp, char *filename, char *options) { int k, ret, params_written; FILE *fp, *fp0; int state = 0, looping, newline; char buf[4096], *filename0, *ptr1, *ptr2, *header = NULL; readoptions(options, &header); k = (int) strlen(filename); filename0 = (char *) malloc(k + 1 + 1); strcpy(filename0, filename); ptr1 = strrchr(filename0, '.'); ptr2 = strrchr(filename0, '\\'); if((ptr1 == NULL) || ((ptr2 != NULL) && (ptr1 < ptr2))) ptr1 = filename0 + k; memmove(ptr1 + 1, ptr1, k + 1 - (int) (ptr1 - filename0)); ptr1[0] = '_'; if(rename(filename, filename0)) { switch(errno) { case ENOENT: /* File or path specified by oldname not found */ FREE(filename0); filename0 = NULL; break; case EACCES: /* File or directory specified by newname already exists or could not be created (invalid path); or oldname is a directory and newname specifies a different path. */ FREE(filename0); FREE(header); return(FALSE); break; } } if((fp = ini_create(filename)) == NULL) ret = FALSE; else { params_written = FALSE; newline = TRUE; if(filename0 != NULL) { fp0 = ini_open(filename0); if(fp0 == NULL) { rename(filename0, filename); FREE(filename0); FREE(header); return(FALSE); } looping = TRUE; while(looping) { switch(ini_readdata(fp0, buf, sizeof(buf), TRUE)) { case 0: /* End of file */ looping = FALSE; break; case 1: /* header */ ptr1 = strdup(buf); STRUPR(buf); ptr2 = strdup(header); STRUPR(ptr2); if(strcmp(buf, ptr2) == 0) { write_params1(lp, fp, ptr1, newline); params_written = TRUE; newline = TRUE; state = 1; } else { state = 0; ini_writeheader(fp, ptr1, newline); newline = TRUE; } FREE(ptr2); FREE(ptr1); break; case 2: /* data */ if(state == 0) { ini_writedata(fp, NULL, buf); newline = (*buf != 0); } break; } } ini_close(fp0); } if(!params_written) write_params1(lp, fp, header, newline); ini_close(fp); ret = TRUE; } if(filename0 != NULL) { remove(filename0); FREE(filename0); } FREE(header); return( (MYBOOL) ret ); } MYBOOL __WINAPI read_params(lprec *lp, char *filename, char *options) { int ret, looping, line; FILE *fp; hashtable *hashfunctions, *hashparameters; hashelem *hp; int i, j, elements, n, intvalue, state = 0; REAL REALvalue; char buf[4096], *header = NULL, *ptr, *ptr1, *ptr2; if((fp = ini_open(filename)) == NULL) ret = FALSE; else { /* create hashtable of all callable commands to find them quickly */ hashfunctions = create_hash_table(sizeof(functions) / sizeof(*functions), 0); for (n = 0, i = 0; i < (int) (sizeof(functions)/sizeof(*functions)); i++) { puthash(functions[i].par, i, NULL, hashfunctions); if(functions[i].values != NULL) n += functions[i].elements; } /* create hashtable of all arguments to find them quickly */ hashparameters = create_hash_table(n, 0); for (n = 0, i = 0; i < (int) (sizeof(functions)/sizeof(*functions)); i++) { if(functions[i].values != NULL) { elements = functions[i].elements; for(j = 0; j < elements; j++) if((strcmp(functions[i].values[j].svalue, "0") != 0) && (strcmp(functions[i].values[j].svalue, "1") != 0)) puthash(functions[i].values[j].svalue, j, NULL, hashparameters); } } readoptions(options, &header); STRUPR(header); ret = looping = TRUE; line = 0; while((ret) && (looping)) { line++; switch(ini_readdata(fp, buf, sizeof(buf), FALSE)) { case 0: /* End of file */ looping = FALSE; break; case 1: /* header */ switch(state) { case 0: STRUPR(buf); if(strcmp(buf, header) == 0) state = 1; break; case 1: looping = FALSE; break; } break; case 2: /* data */ if(state == 1) { for(ptr = buf; (*ptr) && (isspace(*ptr)); ptr++); } else ptr = NULL; if((ptr != NULL) && (*ptr)) { STRUPR(buf); ptr = strchr(buf, '='); if(ptr == NULL) { report(lp, IMPORTANT, "read_params: No equal sign on line %d\n", line); ret = FALSE; } else { *ptr = 0; for(ptr1 = buf; isspace(*ptr1); ptr1++); for(ptr2 = ptr - 1; (ptr2 >= ptr1) && (isspace(*ptr2)); ptr2--); if(ptr2 <= ptr1) { report(lp, IMPORTANT, "read_params: No parameter name before equal sign on line %d\n", line); ret = FALSE; } else { ptr2[1] = 0; hp = findhash(ptr1, hashfunctions); if(hp == NULL) { report(lp, IMPORTANT, "read_params: Unknown parameter name (%s) before equal sign on line %d\n", ptr1, line); ret = FALSE; } else { i = hp->index; ptr1 = ++ptr; intvalue = 0; REALvalue = 0; if(functions[i].values == NULL) { switch(functions[i].type) { case intfunction: case longfunction: case MYBOOLfunction: intvalue = strtol(ptr1, &ptr2, 10); while((*ptr2) && (isspace(*ptr2))) ptr2++; if(*ptr2) { report(lp, IMPORTANT, "read_params: Invalid integer value on line %d\n", line); ret = FALSE; } break; case REALfunction: REALvalue = strtod(ptr1, &ptr2); while((*ptr2) && (isspace(*ptr2))) ptr2++; if(*ptr2) { report(lp, IMPORTANT, "read_params: Invalid real value on line %d\n", line); ret = FALSE; } break; } } else { while(ret) { ptr = strchr(ptr1, '+'); if(ptr == NULL) ptr = ptr1 + strlen(ptr1); for(; isspace(*ptr1); ptr1++); for(ptr2 = ptr - 1; (ptr2 >= ptr1) && (isspace(*ptr2)); ptr2--); if(ptr2 <= ptr1) break; else { ptr2[1] = 0; hp = findhash(ptr1, hashparameters); if (hp == NULL) { report(lp, IMPORTANT, "read_params: Invalid parameter name (%s) on line %d\n", ptr1, line); ret = FALSE; } else { j = hp->index; if((j >= functions[i].elements) || (strcmp(functions[i].values[j].svalue, ptr1))) { report(lp, IMPORTANT, "read_params: Inappropriate parameter name (%s) on line %d\n", ptr1, line); ret = FALSE; } else { intvalue += functions[i].values[j].value; } } ptr1 = ptr + 1; } } } if(ret) { switch(functions[i].type) { case intfunction: functions[i].set_function.int_set_function(lp, intvalue); break; case longfunction: functions[i].set_function.long_set_function(lp, intvalue); break; case MYBOOLfunction: functions[i].set_function.MYBOOL_set_function(lp, (MYBOOL) intvalue); break; case REALfunction: functions[i].set_function.REAL_set_function(lp, REALvalue); break; } } } } } } break; } } FREE(header); free_hash_table(hashfunctions); free_hash_table(hashparameters); ini_close(fp); } return( (MYBOOL) ret ); }