1/* BEGIN LICENSE BLOCK
2 * Version: CMPL 1.1
3 *
4 * The contents of this file are subject to the Cisco-style Mozilla Public
5 * License Version 1.1 (the "License"); you may not use this file except
6 * in compliance with the License.  You may obtain a copy of the License
7 * at www.eclipse-clp.org/license.
8 *
9 * Software distributed under the License is distributed on an "AS IS"
10 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
11 * the License for the specific language governing rights and limitations
12 * under the License.
13 *
14 * The Original Code is  The ECLiPSe Constraint Logic Programming System.
15 * The Initial Developer of the Original Code is  Cisco Systems, Inc.
16 * Portions created by the Initial Developer are
17 * Copyright (C) 1995-2006 Cisco Systems, Inc.  All Rights Reserved.
18 *
19 * Contributor(s): Joachim Schimpf, Kish Shen and Andrew Eremin, IC-Parc
20 *
21 * END LICENSE BLOCK */
22/*
23 * ECLiPSe / CPLEX interface
24 *
25 * System:	ECLiPSe Constraint Logic Programming System
26 * Author/s:	Joachim Schimpf, IC-Parc
27 *              Kish Shen,       IC-Parc
28 * Version:	$Id: eplex.c,v 1.16 2014/07/14 01:02:27 jschimpf Exp $
29 *
30 */
31
32/*#define LOG_CALLS*/
33#undef LOG_CALLS
34#ifdef LOG_CALLS
35int log_ctr = 0;
36#endif
37
38#ifndef __STDC__
39typedef unsigned size_t;
40char *getenv();
41#else
42#include <stdlib.h>
43#endif
44#include <string.h>
45
46#ifdef HAVE_UNISTD_H
47# include <unistd.h>
48#endif
49
50# include <stdio.h>
51
52#ifdef WIN32
53# include <process.h>
54# define unlink(File) _unlink(File)
55# define getpid()   _getpid()
56# define gethostid()  0
57# define putenv(s) _putenv(s)
58# define PATH_SEPARATOR ";"
59#else
60# define PATH_SEPARATOR ":"
61#endif
62
63
64/* Kish 2008-08-13:
65   Cannot define __eprintf() here for PPC Mac OS X -- at least version 10.4
66   we have access to. It is defined in stdc++, and Mac OS X does not allow
67   multiple definitions of symbols during linking (for flat_namespace).
68   versions,
69
70   Kish 2008-07-24: define __eprintf() for all cases -- Cisco lab Solaris
71   has older libraries that does not have __eprintf() defined
72
73   code modified from koders.com's definition of __eprintf(), which uses
74   fiprintf(), which is also undefined for Intel MacOSX. Here eprintf()
75   is redefined to just the abort. This should be OK, as it is used in
76   assert.h, which should only be used when debugging
77
78-- Kish Shen 2007-11-22
79
80   This is an implementation of the __eprintf function which is
81   compatible with the assert.h which is distributed with gcc.
82
83   This function is provided because in some cases libgcc.a will not
84   provide __eprintf.  This will happen if inhibit_libc is defined,
85   which is done because at the time that libgcc2.c is compiled, the
86   correct <stdio.h> may not be available.  newlib provides its own
87   copy of assert.h, which calls __assert, not __eprintf.  However, in
88   some cases you may accidentally wind up compiling with the gcc
89   assert.h.  In such a case, this __eprintf will be used if there
90   does not happen to be one in libgcc2.c.  */
91
92#if !(defined(__APPLE__) && defined(__ppc__))
93
94void
95__eprintf (format, file, line, expression)
96     const char *format;
97     const char *file;
98     unsigned int line;
99     const char *expression;
100{
101/* (void) fiprintf (stderr, format, file, line, expression);*/
102  abort ();
103 }
104
105#endif
106
107
108# include <limits.h>
109# include <math.h>
110
111#if defined(LOG_CALLS)
112# define USE_PROBLEM_ARRAY
113#endif
114
115
116/*
117 * Macros to make code more readable
118 */
119
120/* this extra step is needed to allow Call itself to be transformed */
121#define Transform_Quoted(Item) Add_Quotes(Item)
122#define Add_Quotes(Item) #Item
123
124/* Call logging macros:
125     Call(Ret, Call)   Log and call Call if LOG_CALLS, assign return value to Ret
126     CallN(Call)       Log and call Call if LOG_CALLS, return value is lost
127
128Log only macros (these should be accompanied by an actual call to the
129logged call!)
130
131     Log1(Call, A1)...Log6(Call, A1,A2,A3,A4,A5,A6)
132                       Log Call if LOG_CALLS. Call should be in printf
133                       form, with appropriate % arguments for the arguments
134*/
135#ifdef LOG_CALLS
136# define Call(Err, C) { \
137    Fprintf(log_output_,  "\n\
138	"Transform_Quoted(C)";");\
139    ec_flush(log_output_); \
140    Err = C;\
141}
142
143# define CallN(C) { \
144    Fprintf(log_output_,  "\n\
145	"Transform_Quoted(C)";");\
146    ec_flush(log_output_); \
147    C;\
148}
149
150# define Log0(C) {\
151    Fprintf(log_output_,  "\n\
152	"Transform_Quoted(C)";");\
153    ec_flush(log_output_);\
154}
155
156# define Log1(C,A1) {\
157    Fprintf(log_output_,  "\n\
158	"Transform_Quoted(C)";",A1);\
159    ec_flush(log_output_);\
160}
161
162# define Log2(C,A1,A2) {\
163    Fprintf(log_output_,  "\n\
164	"Transform_Quoted(C)";",A1,A2);\
165    ec_flush(log_output_);\
166}
167
168# define Log3(C,A1,A2,A3) {\
169    Fprintf(log_output_,  "\n\
170	"Transform_Quoted(C)";",A1,A2,A3);\
171    ec_flush(log_output_);\
172}
173
174# define Log4(C,A1,A2,A3,A4) {\
175    Fprintf(log_output_,  "\n\
176	"Transform_Quoted(C)";",A1,A2,A3,A4); \
177    ec_flush(log_output_); \
178}
179
180# define Log5(C,A1,A2,A3,A4,A5) {\
181    Fprintf(log_output_,  "\n\
182	"Transform_Quoted(C)";",A1,A2,A3,A4,A5); \
183    ec_flush(log_output_); \
184}
185
186# define Log6(C,A1,A2,A3,A4,A5,A6) {\
187    Fprintf(log_output_,  "\n\
188	"Transform_Quoted(C)";",A1,A2,A3,A4,A5,A6); \
189    ec_flush(log_output_); \
190}
191
192#else
193# define Call(Err, C) {Err = C;}
194# define CallN(C) C
195# define Log0(C)
196# define Log1(C,A1)
197# define Log2(C,A1,A2)
198# define Log3(C,A1,A2,A3)
199# define Log4(C,A1,A2,A3,A4)
200# define Log5(C,A1,A2,A3,A4,A5)
201# define Log6(C,A1,A2,A3,A4,A5,A6)
202#endif
203
204
205/*
206 * ECLiPSe declarations
207 */
208
209#include "external.h"
210
211
212#if defined(WIN32) && defined(LOG_CALLS)
213/* must be after include of external.h to avoid redefining log_output_ there
214   Windows workaround for log_output_ not exported in eclipse.def
215*/
216# define log_output_ ec_stream_id(ec_stream_nr("log_output"))
217#endif
218
219/* should be used only if v,t is a number */
220#define DoubleVal(v, t) ( IsInteger(t) ? (double) (v).nint : \
221			  IsDouble(t) ? Dbl(v) : coerce_to_double(v,t) )
222
223#define Check_Constant_Range(x) \
224	{if ((x) < -CPX_INFBOUND || (x) > CPX_INFBOUND) {Bip_Error(RANGE_ERROR);}}
225
226
227/*
228 * LpDescOnly can be used if we only want to access
229 * fields within lp_desc, not in the external solver
230 * this made a difference in old Xpress, but now there
231 * is a difference only when logging calls
232 */
233#define LpDescOnly(vlp, tlp, lpd) Get_Typed_Object(vlp, tlp, &lp_handle_tid, lpd)
234
235#ifdef LOG_CALLS
236static int next_matno = 0, current_matno = -1;
237
238# define LpDesc(vlp, tlp, lpd) {                        \
239    Get_Typed_Object(vlp, tlp, &lp_handle_tid, (lpd));  \
240    if ((lpd)->matno != current_matno)                  \
241    {                                                   \
242	Fprintf(log_output_, "\n\
243	lpd = (lp_desc *) lpdmat[%d];", (lpd)->matno);  \
244	ec_flush(log_output_);                          \
245	current_matno = (lpd)->matno;                   \
246    }                                                   \
247}
248
249#else
250# define LpDesc(vlp, tlp, lpd) LpDescOnly(vlp, tlp, lpd)
251#endif
252
253
254#define IsArray(t) IsString(t)
255#define Check_Array(t) Check_String(t)
256#define IArrayStart(pw) ((int *)BufferStart(pw))
257#define DArrayStart(pw) ((double *)BufferStart(pw))
258#define CArrayStart(pw) ((char *)BufferStart(pw))
259#define Return_Unify_Array(v,t,a) Return_Unify_String(v,t,a)
260#define DArraySize(pbuf) ((BufferSize(pbuf) - 1) / sizeof(double))
261#define IArraySize(pbuf) ((BufferSize(pbuf) - 1) / sizeof(int))
262
263static pword * _create_carray();
264static pword * _create_darray();
265static pword * _create_iarray();
266
267
268/*
269 * Solver-independent constants
270 */
271
272/* argument indices in Prolog-level prob-handle */
273#define HANDLE_CPH		1	/* C-level handle */
274#define HANDLE_STAMP		2	/* timestamp for prob-handle */
275#define HANDLE_M_METH		0	/* Outputs: offsets from meth-field */
276#define HANDLE_M_AUXMETH	1
277#define HANDLE_M_NODEMETH	2
278#define HANDLE_M_NODEAUXMETH	3
279#define HANDLE_S_SOLS		0	/* Outputs: offsets from sols-field */
280#define HANDLE_S_PIS		1
281#define HANDLE_S_SLACKS		2
282#define HANDLE_S_DJS		3
283#define HANDLE_S_CBASE		4
284#define HANDLE_S_RBASE		5
285#define HANDLE_S_CPCM		6
286#define HANDLE_S_IISR		7
287#define HANDLE_S_IISC		8
288#define HANDLE_S_IISCS		9
289
290#define COL_STAMP       1       /* timestamp for a column (in attribute) */
291
292
293#define DESCR_EMPTY		0	/* problem descriptor state */
294#define DESCR_LOADED		1
295#define DESCR_SOLVED_SOL	2
296#define DESCR_SOLVED_NOSOL	3
297#define DESCR_ABORTED_SOL	4
298#define DESCR_ABORTED_NOSOL	5
299#define DESCR_UNBOUNDED_NOSOL	6
300#define DESCR_UNKNOWN_NOSOL	7
301
302#define CSTR_TYPE_NORM          0 /* correspond to constraint_type_code/2 */
303#define CSTR_TYPE_PERMCP        1
304#define CSTR_TYPE_CONDCP        2
305
306#define CSTR_STATE_NOTADDED    -1
307#define CSTR_STATE_VIOLATED    -1
308#define CSTR_STATE_SAT         -2
309#define CSTR_STATE_BINDING     -3
310#define CSTR_STATE_INVALID     -4
311#define CSTR_STATE_INACTIVE    -5
312
313#define MIPSTART_NONE		0
314#define MIPSTART_ALL		1
315#define MIPSTART_INT		2
316
317#define CP_ACTIVE       1       /* correspond to cp_cond_code/2  */
318#define CP_ADDINIT      2
319
320#define NEWROW_INCR	60	/* default sizes for addrow arrays */
321#define NEWNZ_INCR	510
322#define NEWBD_INCR	510	/* arrays needed for changing bounds */
323#define NEWCOL_INCR	1022	/* macsz arrays growth increment */
324#define NEWSOS_INCR	32
325#define CUTPOOL_INCR    10      /* number of cutpools increment */
326
327#define RoundTo(n,unit) ((n) - ((n) - 1) % (unit) -1 + (unit))
328#define Max(x,y)	((x)>(y)?(x):(y))
329
330/* minimum number of words that Type would fit in */
331#define NumberOfWords(Type) (1+(sizeof(Type)-1)/sizeof(word))
332
333
334/*
335 * Include solver-specific declarations
336 */
337#ifdef CPLEX
338#include "eplex_cplex.h"
339#endif
340
341#ifdef GUROBI
342#include "eplex_gurobi.h"
343#endif
344
345#ifdef XPRESS
346#include "eplex_xpress.h"
347#endif
348
349#ifdef COIN /* COIN based solvers */
350#include "eplex_coin.h"
351#endif
352
353
354/*
355 * Problem handle
356 */
357
358/* Methods for lp_handle_tid */
359static void _free_lp_handle(lp_desc *lpd);
360static int _strsz_lp_handle(lp_desc *lpd, int quoted);
361static int _tostr_lp_handle(lp_desc *lpd, char *buf, int quoted);
362
363/* 2*sizeof(void *) for max. size for a printed address */
364#define STRSZ_LP_HANDLE 2*sizeof(void *)+20
365
366static int
367_strsz_lp_handle(lp_desc *lpd, int quoted)
368{
369    return STRSZ_LP_HANDLE;
370}
371
372static int
373_tostr_lp_handle(lp_desc *lpd, char *buf, int quoted)
374{
375    sprintf(buf, "'EPLEX_"Transform_Quoted(SOLVER_SHORT_NAME)"'(16'%" W_MOD "x)", (uword) lpd);
376    return strlen(buf); /* size of actual string */
377}
378
379t_ext_type lp_handle_tid = {
380    (void (*)(t_ext_ptr)) _free_lp_handle,  /* free */
381    NULL,  /* copy */
382    NULL,  /* mark_dids */
383    (int (*)(t_ext_ptr,int)) _strsz_lp_handle,  /* string_size */
384    (int (*)(t_ext_ptr,char *,int)) _tostr_lp_handle,  /* to_string */
385    NULL,  /* equal */
386    NULL,  /* remote_copy */
387    NULL,  /* get */
388    NULL   /* set */
389};
390
391
392typedef struct {
393  int              oldmar, oldmac, oldsos, oldidc;
394} untrail_data;
395
396typedef struct {
397  int              idx;
398  char             ctype;
399} untrail_ctype;
400
401typedef struct {
402    double bds[2];     /* bounds: lower, upper */
403    int    idx;       /* index of column */
404} untrail_bound;
405
406typedef struct {
407    int old_ptype;   /* old problem type */
408} untrail_ptype;
409
410
411/*
412 * Global data
413 */
414
415/* ECLiPSe streams for 4 message types (log,result,warning,error) */
416static stream_id solver_streams[4];
417
418/* Atoms used to communicate with the Prolog level */
419static dident d_le, d_ge, d_eq, d_optimizer, d_yes, d_no;
420
421/* Global solver environment (!=0 when initialised) */
422static CPXENVptr	cpx_env = (CPXENVptr) 0;
423
424
425#ifdef CPLEX
426
427static CPXCHANNELptr	cpxresults = (CPXCHANNELptr) 0;
428static CPXCHANNELptr	cpxwarning = (CPXCHANNELptr) 0;
429static CPXCHANNELptr	cpxerror = (CPXCHANNELptr) 0;
430static CPXCHANNELptr	cpxlog = (CPXCHANNELptr) 0;
431
432# if CPLEX >= 8
433static void CPXPUBLIC eclipse_out ARGS((void *nst, const char*msg));
434# else
435static void CPXPUBLIC eclipse_out ARGS((void *nst, char*msg));
436# endif
437#endif /* CPLEX */
438
439
440#ifdef XPRESS
441
442/* Type of XPRESS library */
443#define XP_OEM_UNKNOWN	-1
444#define XP_OEM_NO	0
445#define XP_OEM_YES	1
446static int oem_xpress = XP_OEM_UNKNOWN;
447
448static void XPRS_CC eclipse_out ARGS((XPRSprob prob, void *obj, const char *msg, int len, int msgtype));
449
450#endif	/* XPRESS */
451
452
453#if 0
454void *
455Malloc(size_t size)
456{
457    void *p;
458    p = malloc(size);
459    Fprintf(Current_Error, "%8x malloc(%d)\n", p, size);
460    ec_flush(Current_Error);
461    return p;
462}
463
464void *
465Realloc(void *p, size_t size)
466{
467    Fprintf(Current_Error, "%8x realloc(%d)\n", p, size);
468    ec_flush(Current_Error);
469    return realloc(p, size);
470}
471
472void
473Free(void *p)
474{
475    Fprintf(Current_Error, "%8x free\n", p);
476    ec_flush(Current_Error);
477    free(p);
478}
479#else
480#if 1
481#define Malloc(size) malloc(size)
482#define Realloc(p, size) realloc(p, size)
483#define Free(p) free(p)
484#else
485/* Eclipse's hp_alloc() can cause problems because of private heap limit */
486#define Malloc(size) hp_alloc(size)
487#define Realloc(p, size) hp_resize(p, size)
488#define Free(p) hp_free(p)
489#endif
490#endif
491
492/* free *p if it is pointing at something */
493#define TryFree(p)  if (p) { CallN(Free(p)); p = NULL; }
494
495
496static void _grow_cb_arrays(lp_desc *, int);
497static void _grow_numbers_array(lp_desc * lpd, int m);
498
499
500/*
501 * Include solver-specific code
502 */
503#ifdef CPLEX
504#include "eplex_cplex.c"
505#endif
506
507#ifdef GUROBI
508#include "eplex_gurobi.c"
509#endif
510
511#ifdef XPRESS
512#include "eplex_xpress.c"
513#endif
514
515#ifdef COIN /* COIN based solvers */
516#include "eplex_coin.c"
517#endif
518
519
520static double
521coerce_to_double(value vval, type tval)
522{
523    /* tval MUST be a number type */
524    value buffer;
525
526    tag_desc[TagType(tval)].coerce_to[TDBL](vval, &buffer);
527    return Dbl(buffer);
528}
529
530
531int
532p_cpx_cleanup(value vlp, type tlp)
533{
534    pword handle;
535    handle.val.all = vlp.all;
536    handle.tag.all = tlp.all;
537    return ec_free_handle(handle, &lp_handle_tid);
538}
539
540static void
541_free_lp_handle(lp_desc *lpd)
542{
543    int i;
544#ifdef XPRESS
545    char name[128];
546#endif
547    if (lpd->descr_state != DESCR_EMPTY)
548    {
549#ifdef CPLEX
550	if (lpd->lp)
551	    CallN(CPXfreeprob(cpx_env, &lpd->lp));
552#endif
553#ifdef XPRESS
554	strcpy(name, lpd->probname);
555	strcat(name, ".glb");
556	unlink(name);
557	if (lpd->lp) CallN(XPRSdestroyprob(lpd->lp));
558	if (lpd->lpcopy && lpd->lpcopy != lpd->lp) CallN(XPRSdestroyprob(lpd->lpcopy));
559	Mark_Copy_As_Modified(lpd);
560    	TryFree(lpd->qgtype);
561    	TryFree(lpd->mgcols);
562        TryFree(lpd->probname);
563#endif /* XPRESS */
564#ifdef COIN
565	CallN(coin_free_prob(lpd->lp));
566	/* lpd->lp allocated with new, and freed with delete by coin_free_prob()
567	   so no need to free here
568	*/
569	lpd->lp = NULL;
570#endif /* COIN */
571
572	TryFree(lpd->rhsx);
573	TryFree(lpd->senx);
574	TryFree(lpd->matbeg);
575	TryFree(lpd->matcnt);
576	TryFree(lpd->matind);
577	TryFree(lpd->matval);
578	TryFree(lpd->bdl);
579	TryFree(lpd->bdu);
580	TryFree(lpd->objx);
581	TryFree(lpd->ctype);
582	if (lpd->cb_sz)
583	{
584	    CallN(Free(lpd->cb_index));
585	    TryFree(lpd->cb_index2);
586	    CallN(Free(lpd->cb_value));
587	}
588	if (lpd->sossz)
589	{
590	    CallN(Free(lpd->sostype));
591	    CallN(Free(lpd->sosbeg));
592	    CallN(Free(lpd->sosind));
593	    CallN(Free(lpd->sosref));
594	}
595	TryFree(lpd->rngval);
596	TryFree(lpd->cname);
597	TryFree(lpd->cstore);
598	TryFree(lpd->rname);
599	TryFree(lpd->rstore);
600	TryFree(lpd->numbers);
601	TryFree(lpd->zeroes);
602	TryFree(lpd->dzeroes);
603	if (lpd->nr_sz)
604	{
605	    CallN(Free(lpd->rmatbeg));
606	    TryFree(lpd->rcompl);
607	    TryFree(lpd->rindind);
608	}
609	if (lpd->nnz_sz)
610	{
611	    CallN(Free(lpd->rmatind));
612	    CallN(Free(lpd->rmatval));
613	}
614/*
615	if (lpd->cp_nr_sz)
616	{
617	    CallN(Free(lpd->cp_rmatbeg));
618	    CallN(Free(lpd->cp_rhsx));
619	    CallN(Free(lpd->cp_senx));
620	}
621	if (lpd->cp_nz_sz)
622	{
623	    CallN(Free(lpd->cp_rmatind));
624	    CallN(Free(lpd->cp_rmatval));
625	}
626*/
627	if (lpd->cp_nr_sz2)
628	{
629	    CallN(Free(lpd->cp_rmatbeg2));
630	    CallN(Free(lpd->cp_rhsx2));
631	    CallN(Free(lpd->cp_senx2));
632	    CallN(Free(lpd->cp_active2));
633	    CallN(Free(lpd->cp_initial_add2));
634	    if (lpd->cp_nz_sz2)
635	    {
636		CallN(Free(lpd->cp_rmatind2));
637		CallN(Free(lpd->cp_rmatval2));
638	    }
639	}
640
641	for (i=0; i < lpd->cp_npools2; i++) { TryFree(lpd->cp_pools2[i]); }
642	TryFree(lpd->cp_pools2);
643	TryFree(lpd->cp_pools_max2);
644	TryFree(lpd->cp_pools_sz2);
645    }
646#ifdef CPLEX
647    CPXflushchannel (cpx_env, cpxresults);
648    CPXflushchannel (cpx_env, cpxlog);
649    CPXflushchannel (cpx_env, cpxerror);
650    CPXflushchannel (cpx_env, cpxwarning);
651#else
652    (void) ec_flush(solver_streams[LogType]);
653    (void) ec_flush(solver_streams[ResType]);
654    (void) ec_flush(solver_streams[WrnType]);
655    (void) ec_flush(solver_streams[ErrType]);
656
657#endif
658    CallN(Free(lpd));
659}
660
661
662
663
664/*
665 * Get a licence if necessary, print message and fail if impossible.
666 * If ok, do some low-level initialization
667 *
668 * As an indication that initialization was successful,
669 * cpx_env is set not non-NULL, even for XPRESS.
670 * All functions that do not use a problem handle must make sure that
671 * the system is initialised by checking whether cpx_env is non-NULL!
672 */
673
674int
675p_cpx_init(value vlicloc, type tlicloc,
676	   value vserialnum, type tserialnum,
677	   value vsubdir, type tsubdir)
678{
679    int i;
680
681    /* Initialise global variables */
682    for (i=0; i<4; i++)
683	solver_streams[i] = Current_Null;
684    d_le = ec_did("=<", 0);
685    d_ge = ec_did(">=", 0);
686    d_eq = ec_did("=:=", 0);
687    d_optimizer = ec_did(SOLVER_ATOMIC_NAME, 0);
688    d_yes = ec_did("yes", 0);
689    d_no = ec_did("no", 0);
690
691    if (!cpx_env)
692    {
693#ifdef COIN
694	CallN(coin_create_prob(&cpx_env, NULL));
695#endif
696
697# if defined(CPLEX)
698	char errmsg[512];
699	int status, dev_status;
700	char *licloc; /* environment string (CPLEX) */
701
702	Check_Integer(tserialnum);
703	Get_Name(vlicloc, tlicloc, licloc);
704	if (*licloc == '\0') licloc = NULL;
705#  if CPLEX >= 7
706	if (licloc)
707	{
708	    /* We have a CPLEX runtime key, call CPXRegisterLicense().
709	     * CAUTION: when this call fails, the process may be in a funny
710	     * state. With Cplex 7/8, the thread bindings are changed and
711	     * the process cannot use virtual timers any longer (bug 243).
712	     */
713	    Log1(CPXRegisterLicense(0, %d), (int) vserialnum.nint);
714	    if (CPXRegisterLicense(licloc, (int) vserialnum.nint))
715	    {
716		Fprintf(Current_Error, "Invalid CPLEX runtime key.\n");
717		(void) ec_flush(Current_Error);
718		Fail;
719	    }
720	}
721	/* Note CPLEX prints a banner to stderr in CPXopenCPLEX! */
722	CallN(cpx_env = CPXopenCPLEX(&dev_status));
723	if (dev_status)
724	{
725	    CPXgeterrorstring(cpx_env, dev_status, errmsg);
726	    Fprintf(Current_Error, "%s", errmsg);
727	    (void) ec_flush(Current_Error);
728	    Fail;
729	}
730#   if CPLEX >= 8
731	/* no dual reduction as suggested by manual to get firm infeasible
732           conclusion for MIP
733	*/
734	CallN(CPXsetintparam(cpx_env, CPX_PARAM_REDUCE, 1));
735#   endif
736#  else
737	int rt_status = 0;
738	CallN(cpx_env = CPXopenCPLEXdevelop(&dev_status));
739
740	if (dev_status == 32027)	/* out of licences */
741	{
742	    CPXgeterrorstring(cpx_env, dev_status, errmsg);
743	    Fprintf(Current_Error, "%s", errmsg);
744	    (void) ec_flush(Current_Error);
745	    Fail;
746	}
747	if (dev_status != 0)		/* other problem, try runtime licence */
748	{
749	    char *serialnumstring = getenv("ECLIPSECPLEXSERIALNUM");
750	    int serialnum = serialnumstring ? strtol(serialnumstring, NULL, 0) : vserialnum.nint;
751	    if (serialnum)
752	    {
753		cpx_env = CPXopenCPLEXruntime(&rt_status, serialnum, licloc);
754	    }
755	    else
756	    {
757		Fprintf(Current_Error, "Couldn't find CPLEX development licence: check setting of CPLEXLICENCE,\nCPLEXLICDIR, CPLEXLICTYPE or set ECLIPSECPLEXSERIALNUM to use a runtime licence.\n", 0);
758		(void) ec_flush(Current_Error);
759		Fail;
760	    }
761	}
762	if (dev_status && rt_status)	/* no licence could be opened */
763	{
764	    CPXgeterrorstring(cpx_env, dev_status, errmsg);
765	    Fprintf(Current_Error, "DEV: %s", errmsg);
766	    CPXgeterrorstring(cpx_env, rt_status, errmsg);
767	    Fprintf(Current_Error, "RT: %s", errmsg);
768	    (void) ec_flush(Current_Error);
769	    Fail;
770	}
771#  endif /* CPLEX < 7 */
772	status = CPXgetchannels(cpx_env, &cpxresults, &cpxwarning, &cpxerror, &cpxlog);
773	if (status)
774	{
775	    CPXgeterrorstring(cpx_env, status, errmsg);
776	    Fprintf(Current_Error, "%s", errmsg);
777	    (void) ec_flush(Current_Error);
778	    Bip_Error(EC_EXTERNAL_ERROR);
779	}
780
781# endif /* CPLEX */
782
783# ifdef XPRESS
784	int err;
785	char slicmsg[256], banner[256];
786	char *licloc, /* licence location (XPRESS) */
787	     *subdir; /* solver/platform/version-specific stuff (XPRESS 15) */
788
789	Check_Integer(tserialnum);
790	Get_Name(vlicloc, tlicloc, licloc);
791	Get_Name(vsubdir, tsubdir, subdir);
792	if (*licloc == '\0') licloc = NULL;
793
794	/* Embedded OEM licence handling */
795	if (oem_xpress == XP_OEM_YES)
796	{
797	    i = (int) vserialnum.nint;
798	    err = XPRSlicense(&i, slicmsg);	/* second call */
799	}
800#  ifdef XPRESS_OEM_ICPARC_2002
801	Handle_OEM_ICPARC
802#  endif
803#  if (XPRESS == 15)
804	    {/* Xpress 15 requires the PATH environment variable to be set
805                to where the license manager lmgrd is, as it execs an
806                unqualified lmgrd from within XPRSinit()!
807	     */
808		const char * curpaths;
809		char * newpaths;
810		curpaths = getenv("PATH");
811		newpaths = Malloc(strlen("PATH=") + strlen(curpaths)
812			+ strlen(PATH_SEPARATOR) + strlen(subdir) + 1);
813		strcpy(newpaths, "PATH=");
814		strcat(newpaths, subdir);
815		strcat(newpaths, PATH_SEPARATOR);
816		strcat(newpaths, curpaths);
817		putenv(newpaths);
818	    }
819#  endif
820
821	err = XPRSinit(licloc);
822
823#  if (XPRESS >= 20)
824	if (err != 0  &&  err != 32 /* Student mode */)
825	{
826	    char msg[512];
827	    XPRSgetlicerrmsg(msg, 512);
828	    Fprintf(Current_Error, "%s", msg);
829	    ec_flush(Current_Error);
830	    Fail;
831	}
832	XPRSgetbanner(banner);
833	Fprintf(Current_Output, "%s\n", banner);
834#  else
835	{
836	    int ndays;
837	    /* no banner printed in XPRESS 13, print it now as it may contain
838	       extra error information
839	    */
840	    XPRSgetbanner(banner);
841	    Fprintf(Current_Output, "%s\n", banner);
842	    XPRSgetdaysleft(&ndays);
843	    /* ndays == 0 if license is not time-limited */
844	    if (ndays > 0)
845	    {
846		Fprintf(Current_Output, "This XPRESS license will expire in %d days.\n\n", ndays);
847	    }
848	    else if (ndays < 0)
849	    {
850		Fprintf(Current_Error, "This XPRESS license has expired\n\n");
851		ec_flush(Current_Error);
852	    }
853	    ec_flush(Current_Output);
854	}
855	if (err != 0  &&  err != 32 /* Student mode */)
856	{
857	    if (err != 8 || oem_xpress == XP_OEM_YES)	/* suppress message for OEM library */
858	    {
859
860		Fprintf(Current_Error, "XPRESS error (probably licencing problem)\n");
861		(void) ec_flush(Current_Error);
862	    }
863	    Fail;
864	}
865	if (oem_xpress == XP_OEM_YES)		/* print the OEM message */
866	{
867	    Fprintf(Current_Output, slicmsg);
868	    (void) ec_newline(Current_Output);
869	}
870#  endif
871
872	/* use cpx_env to store the `default problem' */
873	CallN(XPRScreateprob(&cpx_env));
874
875# endif /* XPRESS */
876
877#ifdef GUROBI
878	int status;
879	char *licloc;
880
881	Get_Name(vlicloc, tlicloc, licloc);
882	if (*licloc == '\0') licloc = NULL;
883	if (licloc && !getenv("GRB_LICENSE_FILE"))
884	{
885	    char *envstring = Malloc(strlen("GRB_LICENSE_FILE=")+strlen(licloc)+1);
886	    strcat(strcpy(envstring,"GRB_LICENSE_FILE="),licloc);
887	    /* Bug: this putenv does not seem to affect GRBloadenv below (Windows) */
888	    putenv(envstring);
889	}
890
891	status = GRBloadenv(&cpx_env, NULL);
892	if (status == GRB_ERROR_NO_LICENSE)
893	{
894	    Fprintf(Current_Error, "Couldn't find Gurobi licence.\n");
895	    ec_flush(Current_Error);
896	    Fail;
897	}
898	else if (status)
899	{
900	    /* can't retrieve error messages yet */
901	    Fprintf(Current_Error, "Gurobi error %d\n", status);
902	    ec_flush(Current_Error);
903	    Bip_Error(EC_EXTERNAL_ERROR);
904	}
905	/* switch off solver's own output */
906	GRBsetintparam(cpx_env, GRB_INT_PAR_OUTPUTFLAG, 0);
907
908#endif /* GUROBI */
909    }
910# ifdef LOG_CALLS
911    Fprintf(log_output_, "\nvoid step_%d() {\n", log_ctr++);
912    ec_flush(log_output_);
913# endif
914    Succeed;
915}
916
917
918int
919p_cpx_challenge(value v, type t)
920{
921# ifdef XPRESS
922#  if defined(WIN32)
923    int nvalue;
924    char slicmsg[256];
925    /* Caution: calling optlicence() twice crashes on some non-oem XPRESSes */
926    if (oem_xpress != XP_OEM_NO)
927    {
928	if (XPRSlicense(&nvalue, slicmsg) != 8)
929	{
930	    oem_xpress = XP_OEM_YES;
931	    Return_Unify_Integer(v, t, (long) nvalue);
932	}
933    }
934#  endif
935    oem_xpress = XP_OEM_NO;
936# endif
937    Fail;
938}
939
940
941int
942p_cpx_exit()
943{
944    if (cpx_env)
945    {
946	CPXcloseCPLEX(&cpx_env);
947	cpx_env = 0;
948    }
949    Succeed;
950}
951
952
953int
954p_cpx_prob_init(value vpre, type tpre,
955		value vcpy, type tcpy,
956		value vrow, type trow,
957		value vcol, type tcol,
958		value vnz, type tnz,
959		value vdir, type tdir,
960		value vsense, type tsense,
961		value vhandle, type thandle)
962{
963    int	i;
964    lp_desc *lpd;
965
966    Check_Integer(tpre);
967    Check_Integer(tcpy);
968    Check_Integer(trow);
969    Check_Integer(tcol);
970    Check_Integer(tnz);
971    Check_Structure(thandle);
972
973    CallN(lpd = (lp_desc *) Malloc(sizeof(lp_desc)));
974    /*CallN(_clr_lp_desc(lpd));*/
975    CallN(memset(lpd, 0, sizeof(lp_desc)));
976
977
978#ifdef USE_PROBLEM_ARRAY
979    Log1(lpdmat[%d] = lpd, next_matno);
980    current_matno = next_matno;
981    lpd->matno = next_matno++;
982#endif
983
984#ifdef XPRESS
985    lpd->copystatus = (vcpy.nint == 1 ? XP_COPYINVALID : XP_COPYOFF);
986
987    {/* need unique name so that  file names created by XPRESS are unique
988        dir is a directory path (but may need to be have the directory
989        separator character (/ or \) added
990     */
991	int dirlen;
992
993#ifdef WIN32
994# define BASE_PROB_NAME \\eclipse
995#else
996# define BASE_PROB_NAME /eclipse
997#endif
998	Check_String(tdir);
999	dirlen = strlen(StringStart(vdir));
1000
1001	lpd->probname = (char *) Malloc((50+dirlen) * sizeof(char));
1002	sprintf(lpd->probname, "%s"Transform_Quoted(BASE_PROB_NAME)"%u-%u",
1003		StringStart(vdir), gethostid(), getpid());
1004	if (strlen(lpd->probname) > XP_PROBNAME_MAX)
1005	{
1006	    Fprintf(Current_Error, "Eplex error: the problem name for Xpress is too long.\n"
1007            "Change tmp_dir to a directory with shorter path length.\n");
1008	    ec_flush(Current_Error);
1009	    Bip_Error(RANGE_ERROR);
1010	}
1011    }
1012#endif
1013    lpd->prob_type = PROBLEM_LP;
1014    lpd->presolve = vpre.nint;
1015    lpd->sense = vsense.nint;
1016    lpd->mac = vcol.nint;
1017    lpd->macsz = vcol.nint;
1018    lpd->macadded = 0;
1019    lpd->nidc = 0;
1020    lpd->marsz = vrow.nint;	       /* max number of rows */
1021    lpd->mar = vrow.nint;
1022    lpd->matnz = vnz.nint;	       /* number of nonzero coefficients */
1023
1024    /* if vcol/vrow/vnz is 0, malloc arrays of at least size 1. This avoid
1025       calling malloc with size 0, and does create an array (Xpress crashes
1026       if NULL is given in place of an array address in some cases)
1027       This increment should only be done after assigning their original
1028       values to the lpd fields!
1029
1030    */
1031    if (vcol.nint == 0) vcol.nint++;
1032    if (vrow.nint == 0) vrow.nint++;
1033    if (vnz.nint == 0) vnz.nint++;
1034    lpd->rhsx = (double *) Malloc(vrow.nint * sizeof(double));
1035    lpd->senx = (char *) Malloc(vrow.nint * sizeof(char));
1036
1037    /* one extra element for matbeg, because some representations of the
1038       matrix (e.g. COIN) needs the `matbeg' for vcol+1 to be specified,
1039       so that the end of the last column can be determined without matcnt
1040    */
1041    lpd->matbeg = (int *) Malloc((1+vcol.nint) * sizeof(int));
1042    lpd->matcnt = (int *) Malloc(vcol.nint * sizeof(int));
1043    lpd->matind = (int *) Malloc(vnz.nint * sizeof(int));
1044    lpd->matval = (double *) Malloc(vnz.nint * sizeof(double));
1045
1046    lpd->bdl = (double *) Malloc(vcol.nint * sizeof(double));
1047    lpd->bdu = (double *) Malloc(vcol.nint * sizeof(double));
1048    lpd->dirtybdflag = 0;
1049    lpd->objx = (double *) Malloc(vcol.nint * sizeof(double));
1050    lpd->ctype = (char *) Malloc(vcol.nint * sizeof(char));
1051    for (i = 0; i < vcol.nint; i++)
1052    {
1053	lpd->bdl[i] = -CPX_INFBOUND;
1054	lpd->bdu[i] = CPX_INFBOUND;
1055	lpd->objx[i] = 0.0;
1056	lpd->ctype[i] = 'C';
1057    }
1058
1059    /* the cutpools fields in lpd should all be zero/NULL at this point, so
1060       they do not need to be initialised
1061    */
1062
1063    lpd->descr_state = DESCR_LOADED;
1064
1065    lpd->mipstart_dirty = 0;
1066
1067    {/* Return the cplex descriptor in argument HANDLE_CPH of the handle structure. */
1068	vhandle.ptr[HANDLE_CPH] = ec_handle(&lp_handle_tid, lpd);
1069	Make_Stamp(vhandle.ptr+HANDLE_STAMP); /* needed for other trail undos */
1070    }
1071    Succeed;
1072}
1073
1074
1075int
1076p_cpx_get_prob_param(value vlp, type tlp, value vp, type tp, value vval, type tval)
1077{
1078    lp_desc *lpd;
1079    int i;
1080    Check_Integer(tp);
1081    LpDesc(vlp, tlp, lpd);
1082    switch(vp.nint)
1083    {
1084    case 0:	i = lpd->mar; break;
1085    case 1:	i = lpd->mac; break;
1086    case 3:	i = lpd->sense; break;
1087    case 4:	i = lpd->prob_type; break;
1088    case 5:	i = lpd->optimum_ctr; break;
1089    case 6:	i = lpd->infeas_ctr; break;
1090    case 7:	i = lpd->abort_ctr; break;
1091    case 8:	i = lpd->sol_itcnt; break;
1092    case 9:	i = lpd->sol_nodnum; break;
1093    case 10:	i = lpd->descr_state; break;
1094    case 11:	i = lpd->sol_state; break;
1095    case 12:    CPXupdatemodel(lpd->lp);
1096		i = CPXgetnumnz(cpx_env, lpd->lp); break;
1097    case 13:    CPXupdatemodel(lpd->lp);
1098		i = IsMIPProb(lpd->prob_type) ?
1099		    CPXgetnumint(cpx_env, lpd->lp) + CPXgetnumbin(cpx_env, lpd->lp) : 0;
1100		break;
1101    case 14:    CPXupdatemodel(lpd->lp);
1102		i = IsQPProb(lpd->prob_type) ? CPXgetnumqpnz(cpx_env, lpd->lp) : 0;
1103		break;
1104    case 15:	i = lpd->start_mac; break;
1105/*    case 16:	i = lpd->cp_nr; break;*/
1106    case 17:	i = lpd->cp_nr2; break;
1107    case 18:	i = lpd->cp_nact2; break;
1108    default:
1109	Bip_Error(RANGE_ERROR);
1110    }
1111    Return_Unify_Integer(vval, tval, i);
1112}
1113
1114
1115int
1116p_cpx_get_param(value vlp, type tlp, value vp, type tp, value vval, type tval)
1117{
1118    double dres;
1119    int i, ires;
1120    char sres[STRBUFFERSIZE];
1121    lp_desc *lpd;
1122
1123#ifndef COIN
1124    if (!cpx_env)
1125    {
1126	Bip_Error(EC_LICENSE_ERROR);
1127    }
1128#endif
1129    /* lpd is NULL for global/default param */
1130    if (IsHandle(tlp))  {
1131	LpDesc(vlp, tlp, lpd);
1132	if (!lpd->lp) lpd = NULL;
1133    } else lpd = NULL;
1134
1135    if (IsAtom(tp))
1136    {
1137	for(i=0; i<NUMPARAMS+NUMALIASES; ++i)	/* lookup the parameter name */
1138	{
1139	    if (strcmp(params[i].name, DidName(vp.did)) == 0)
1140	    	break;
1141	}
1142	if (i==NUMPARAMS+NUMALIASES)
1143	{
1144	    Bip_Error(RANGE_ERROR);
1145	}
1146	if (params[i].type == 0  &&
1147		Get_Int_Param(cpx_env, lpd, params[i].num, &ires)
1148	    == 0)
1149	{
1150	    Return_Unify_Integer(vval, tval, ires);
1151	}
1152	if (params[i].type == 1  &&
1153		Get_Dbl_Param(cpx_env, lpd, params[i].num, &dres)
1154	    == 0)
1155	{
1156	    Return_Unify_Float(vval, tval, dres);
1157	}
1158#ifdef SOLVER_HAS_STR_PARAMS
1159	if (params[i].type == 2 &&
1160		Get_Str_Param(cpx_env, lpd, params[i].num, sres)
1161	    == 0)
1162	{
1163	  value val;
1164	  Check_Output_String(tval);
1165	  Cstring_To_Prolog(sres, val);
1166	  Return_Unify_String(vval, tval, val.ptr);
1167	}
1168#endif
1169#ifdef COIN
1170	if (params[i].type == 3 &&
1171	        coin_get_solver_intparam((lpd==NULL ? cpx_env : lpd->lp),
1172					  params[i].num, &ires)
1173	    == 0)
1174	{
1175	    Return_Unify_Integer(vval, tval, ires);
1176	}
1177	if (params[i].type == 4  &&
1178		coin_get_solver_dblparam((lpd==NULL ? cpx_env : lpd->lp),
1179					 params[i].num, &dres)
1180	    == 0)
1181	{
1182	    Return_Unify_Float(vval, tval, dres);
1183	}
1184	if (params[i].type == 6 &&
1185	        coin_get_eplex_intparam((lpd==NULL ? cpx_env : lpd->lp),
1186					  params[i].num, &ires)
1187	    == 0)
1188	{
1189	    Return_Unify_Integer(vval, tval, ires);
1190	}
1191	if (params[i].type == 8 &&
1192	        coin_get_eplex_strparam((lpd==NULL ? cpx_env : lpd->lp),
1193					  params[i].num, sres)
1194	    == 0)
1195	{
1196	  value val;
1197	  Check_Output_String(tval);
1198	  Cstring_To_Prolog(sres, val);
1199	  Return_Unify_String(vval, tval, val.ptr);
1200	}
1201#endif
1202
1203	Bip_Error(TYPE_ERROR);	/* occurs only if params[i].type is wrong */
1204    }
1205    Check_Integer(tp);
1206    switch (vp.nint)
1207    {
1208    case -1:			/* get optimizer code */
1209	Return_Unify_Atom(vval, tval, d_optimizer);
1210
1211    case -2:			/* get optimizer version */
1212#ifdef COIN
1213      {
1214	  char * ver = Malloc(32*sizeof(char));
1215	  pword pw;
1216
1217	  coin_get_solver_info(ver);
1218	  Make_String(&pw, ver);
1219	  Free(ver);
1220
1221	  Return_Unify_Pw(vval, tval, pw.val, pw.tag);
1222      }
1223#else
1224	Return_Unify_Integer(vval, tval, SOLVER_VERSION_INT);
1225#endif
1226
1227    case -3:			/* has_qp */
1228#ifdef HAS_QUADRATIC
1229	Return_Unify_Atom(vval, tval, d_yes);
1230#else
1231	Return_Unify_Atom(vval, tval, d_no);
1232#endif
1233
1234    case -4:			/* has_miqp */
1235#ifdef HAS_MIQP
1236	Return_Unify_Atom(vval, tval, d_yes);
1237#else
1238	Return_Unify_Atom(vval, tval, d_no);
1239#endif
1240
1241    case -5:			/* has_indicator_constraints */
1242#ifdef HAS_INDICATOR_CONSTRAINTS
1243	Return_Unify_Atom(vval, tval, d_yes);
1244#else
1245	Return_Unify_Atom(vval, tval, d_no);
1246#endif
1247
1248    default:
1249	Bip_Error(RANGE_ERROR);
1250    }
1251}
1252
1253
1254int
1255p_cpx_set_param(value vlp, type tlp, value vp, type tp, value vval, type tval)
1256/* fails if parameter unknown */
1257{
1258    int i;
1259    int err = 1;
1260    lp_desc *lpd;
1261
1262    Check_Atom(tp);
1263#ifndef COIN
1264    if (!cpx_env)
1265    {
1266	Bip_Error(EC_LICENSE_ERROR);
1267    }
1268#endif
1269    /* lpd is NULL for global/default param */
1270    if (IsHandle(tlp))  {
1271	LpDesc(vlp, tlp, lpd);
1272	if (!lpd->lp) lpd = NULL;
1273    } else lpd = NULL;
1274
1275#ifndef SOLVER_HAS_LOCAL_PARAMETERS
1276    if (lpd != NULL)
1277    {
1278        Fprintf(Current_Error, "Eplex error: per solver instance parameters are not supported for this solver. Use global parameters instead.\n");
1279	Bip_Error(UNIMPLEMENTED);
1280    }
1281#endif
1282    for(i=0; i<NUMPARAMS+NUMALIASES; ++i)	/* lookup the parameter name */
1283    {
1284	if (strcmp(params[i].name, DidName(vp.did)) == 0)
1285	    break;
1286    }
1287    if (i==NUMPARAMS+NUMALIASES)
1288    {
1289	Fail;
1290    }
1291    if (params[i].type == 0 && IsInteger(tval))
1292    {
1293	/* Log4 because Set_Int_Param expands into 4 arg for Xpress */
1294	Log4(Set_Int_Param(cpx_env, lpd, %d, %d), params[i].num, vval.nint,
1295			   params[i].num, vval.nint);
1296	err = Set_Int_Param(cpx_env, lpd, params[i].num, vval.nint);
1297    }
1298    else if (params[i].type == 1 && IsDouble(tval))
1299    {
1300	Log4(Set_Dbl_Param(cpx_env, lpd, %d, %f), params[i].num, Dbl(vval),
1301             params[i].num, Dbl(vval));
1302        err = Set_Dbl_Param(cpx_env, lpd, params[i].num, Dbl(vval));
1303    }
1304#ifdef SOLVER_HAS_STR_PARAMS
1305    else if (params[i].type == 2 && (IsAtom(tval) || IsString(tval)))
1306    {
1307	char *s = IsAtom(tval)? DidName(vval.did): StringStart(vval);
1308
1309	if (strlen(s) >= STRBUFFERSIZE) Bip_Error(RANGE_ERROR);/*too large*/
1310	Call(err, Set_Str_Param(cpx_env, lpd, params[i].num, s));
1311    }
1312#endif
1313#if defined(XPRESS) && defined(WIN32)
1314    else if (params[i].type == 3 && (IsAtom(tval) || IsString(tval)))
1315    {
1316	char *s = IsAtom(tval)? DidName(vval.did): StringStart(vval);
1317	err = XPRSsetlogfile(lpd->lp, s);
1318    }
1319#endif
1320#ifdef COIN
1321    /* Solver dependent parameters */
1322    else if (params[i].type == 3 && IsInteger(tval))
1323    {
1324	err = coin_set_solver_intparam((lpd == NULL ? cpx_env : lpd->lp), params[i].num, vval.nint);
1325    }
1326    else if (params[i].type == 4 && IsDouble(tval))
1327    {
1328        err = coin_set_solver_dblparam((lpd == NULL ? cpx_env : lpd->lp), params[i].num, Dbl(vval));
1329    }
1330    else if (params[i].type == 6 && IsInteger(tval))
1331    {
1332        err = coin_set_eplex_intparam((lpd == NULL ? cpx_env : lpd->lp), params[i].num, vval.nint);
1333    }
1334    /* no double params defined yet
1335    else if (params[i].type == 7 && IsDouble(tval))
1336    {
1337        err = coin_set_eplex_dblparam((lpd == NULL ? cpx_env : lpd->lp), params[i].num, Dbl(vval));
1338    }
1339    */
1340    else if (params[i].type == 8 && (IsAtom(tval) || IsString(tval)))
1341    {
1342	char *s = IsAtom(tval)? DidName(vval.did): StringStart(vval);
1343	err = coin_set_eplex_strparam((lpd == NULL ? cpx_env : lpd->lp), params[i].num, s);
1344    }
1345#endif
1346    if (err) {
1347	Bip_Error(TYPE_ERROR);
1348    }
1349    Succeed;
1350}
1351
1352
1353/*----------------------------------------------------------------------*
1354 * Message and error output
1355 *----------------------------------------------------------------------*/
1356
1357#ifdef CPLEX
1358
1359static void CPXPUBLIC
1360# if CPLEX >= 8
1361eclipse_out(void * nst, const char * msg)
1362# else
1363eclipse_out(void * nst, char * msg)
1364# endif
1365{
1366    (void) ec_outf((stream_id) nst, msg, strlen(msg));
1367    (void) ec_flush((stream_id) nst);
1368}
1369
1370
1371int
1372p_cpx_output_stream(value vc, type tc, value vwhat, type twhat, value vs, type ts)
1373{
1374    stream_id nst;
1375    CPXCHANNELptr ch;
1376    pword pw;
1377    int err;
1378
1379    if (!cpx_env)
1380    {
1381	Bip_Error(EC_LICENSE_ERROR);
1382    }
1383    pw.val = vs; pw.tag = ts;
1384    err = ec_get_stream(pw, &nst);
1385    if (err) { Bip_Error(err); }
1386    Check_Integer(tc);
1387    switch (vc.nint) {
1388    case 0: ch = cpxresults; break;
1389    case 1: ch = cpxerror; break;
1390    case 2: ch = cpxwarning; break;
1391    case 3: ch = cpxlog; break;
1392    default: Bip_Error(RANGE_ERROR);
1393    }
1394    Check_Integer(twhat);
1395    if (vwhat.nint == 0)
1396    {
1397	CPXdelfuncdest(cpx_env, ch, (void *) nst, eclipse_out);
1398    } else {
1399	/* raise error only if adding a stream */
1400	if (CPXaddfuncdest(cpx_env, ch, (void *) nst, eclipse_out))
1401	    { Bip_Error(EC_EXTERNAL_ERROR); }
1402    }
1403    Succeed;
1404}
1405
1406#else
1407
1408# ifdef XPRESS
1409static void XPRS_CC
1410eclipse_out(XPRSprob lp, void * obj, const char * msg, int len, int msgtype)
1411{
1412    if (msgtype >= 1 && msgtype <= 4)
1413    {
1414	/* filter out and not print unwanted messages  */
1415	if (msgtype == 3)
1416	{
1417	    /* it seems that there are no other way of filtering out
1418               warning messages than to check the msg string
1419	    */
1420	    if (strncmp(msg, "?140 ", 5) == 0) return; /* basis lost */
1421	    if (strncmp(msg, "?359 ", 5) == 0) return; /* illegal int bounds */
1422	}
1423	/* Fprintf(solver_streams[msgtype-1], "*%d*", msgtype); */
1424	(void) ec_outf(solver_streams[msgtype-1], (char*) msg, len);
1425	(void) ec_newline(solver_streams[msgtype-1]);
1426    }
1427    /* flushing is done in cleanup */
1428}
1429
1430# endif
1431# ifdef COIN
1432
1433void
1434eclipse_out(int msgtype, const char* message)
1435{
1436
1437    (void) ec_outf(solver_streams[msgtype], message, strlen(message));
1438    (void) ec_newline(solver_streams[msgtype]);
1439
1440}
1441
1442# endif
1443
1444int
1445p_cpx_output_stream(value vc, type tc, value vwhat, type twhat, value vs, type ts)
1446{
1447    stream_id nst;
1448    stream_id *solver_stream;
1449    pword pw;
1450    int err;
1451
1452# ifdef XPRESS
1453    if (!cpx_env)
1454    {
1455	Bip_Error(EC_LICENSE_ERROR);
1456    }
1457# endif
1458    pw.val = vs; pw.tag = ts;
1459    err = ec_get_stream(pw, &nst);
1460    if (err) { Bip_Error(err); }
1461    Check_Integer(tc);
1462    Check_Integer(twhat);
1463    switch (vc.nint) {
1464	case 0: solver_stream = &solver_streams[ResType]; break;
1465	case 1: solver_stream = &solver_streams[ErrType]; break;
1466	case 2: solver_stream = &solver_streams[WrnType]; break;
1467	case 3: solver_stream = &solver_streams[LogType]; break;
1468	default: Bip_Error(RANGE_ERROR);
1469    }
1470    if (vwhat.nint == 1)
1471	*solver_stream = nst;
1472    else if (*solver_stream == nst)
1473	*solver_stream = Current_Null;
1474    Succeed;
1475}
1476
1477#endif
1478
1479/*----------------------------------------------------------------------*
1480 * Initial setup
1481 *----------------------------------------------------------------------*/
1482
1483int
1484p_cpx_get_rhs(value vlp, type tlp, value vpool, type tpool, value vi, type ti,
1485	      value vsense, type tsense, value vval, type tval)
1486{
1487    lp_desc *lpd;
1488    Prepare_Requests
1489    double rhs[1];
1490    char sen[1];
1491    dident sense;
1492
1493    LpDesc(vlp, tlp, lpd);
1494    switch (vpool.nint)
1495    {
1496    case CSTR_TYPE_NORM:
1497	SetPreSolve(lpd->presolve);
1498	CPXupdatemodel(lpd->lp);	/* before CPXget... */
1499	if (CPXgetrhs(cpx_env, lpd->lp, rhs, (int) vi.nint, (int) vi.nint))
1500	   { Bip_Error(EC_EXTERNAL_ERROR); }
1501	if (CPXgetsense(cpx_env, lpd->lp, sen, (int) vi.nint, (int) vi.nint))
1502	   { Bip_Error(EC_EXTERNAL_ERROR); }
1503	break;
1504/*
1505    case CSTR_TYPE_PERMCP:
1506	sen[0] = lpd->cp_senx[vi.nint];
1507	rhs[0] = lpd->cp_rhsx[vi.nint];
1508	break;
1509*/
1510    case CSTR_TYPE_CONDCP:
1511	sen[0] = lpd->cp_senx2[vi.nint];
1512	rhs[0] = lpd->cp_rhsx2[vi.nint];
1513	break;
1514    default:
1515	Bip_Error(RANGE_ERROR);
1516	break;
1517    }
1518
1519    sense = sen[0] == SOLVER_SENSE_LE ? d_le :
1520	    sen[0] == SOLVER_SENSE_GE ? d_ge : d_eq;
1521    Request_Unify_Atom(vsense, tsense, sense);
1522    Request_Unify_Float(vval, tval, rhs[0]);
1523
1524    Return_Unify;
1525}
1526
1527int
1528p_cpx_set_rhs_coeff(value vlp, type tlp, value vi, type ti, value vsense, type tsense, value vval, type tval)
1529{
1530    lp_desc *lpd;
1531    LpDescOnly(vlp, tlp, lpd);
1532    Check_Integer(ti);
1533    Check_Number(tval);
1534    if (vi.nint >= lpd->marsz) { Bip_Error(RANGE_ERROR); }
1535    Check_Atom(tsense);
1536    if (vsense.did == d_le) lpd->senx[vi.nint] = SOLVER_SENSE_LE;
1537    else if (vsense.did == d_ge) lpd->senx[vi.nint] = SOLVER_SENSE_GE;
1538    else if (vsense.did == d_eq) lpd->senx[vi.nint] = SOLVER_SENSE_EQ;
1539    else { Bip_Error(RANGE_ERROR); }
1540    lpd->rhsx[vi.nint] = DoubleVal(vval, tval);
1541    Check_Constant_Range(lpd->rhsx[vi.nint]);
1542    Succeed;
1543}
1544
1545int
1546p_cpx_set_obj_coeff(value vlp, type tlp, value vj, type tj, value vval, type tval)
1547{
1548    lp_desc *lpd;
1549    int j;
1550
1551    LpDescOnly(vlp, tlp, lpd);
1552    Check_Integer(tj);
1553    Check_Number(tval);
1554    j = vj.nint;
1555
1556    if (j >= lpd->mac) { Bip_Error(RANGE_ERROR); }
1557    if (j >= lpd->macadded) j -= lpd->macadded; /* added col */
1558
1559    lpd->objx[j] = DoubleVal(vval, tval);
1560    Check_Constant_Range(lpd->objx[j]);
1561    Succeed;
1562}
1563
1564#if 0
1565int
1566p_cpx_get_obj_coeff(value vlp, type tlp, value vj, type tj, value vval, type tval)
1567{
1568    lp_desc *lpd;
1569    LpDescOnly(vlp, tlp, lpd);
1570    Check_Integer(tj);
1571    if (vj.nint >= lpd->mac) { Bip_Error(RANGE_ERROR); }
1572    Return_Unify_Float(vval, tval, lpd->objx[vj.nint]);
1573}
1574#endif
1575
1576int
1577p_cpx_set_qobj_coeff(value vlp, type tlp, value vi, type ti, value vj, type tj, value vval, type tval)
1578{
1579    lp_desc *lpd;
1580    double coef;
1581    int i;
1582    LpDescOnly(vlp, tlp, lpd);
1583    Check_Integer(ti);
1584    Check_Integer(tj);
1585    Check_Number(tval);
1586    if (IsInteger(tval)) {
1587	coef = (double) vval.nint;
1588    } else {
1589	Check_Float(tval);
1590	coef = DoubleVal(vval, tval);
1591	Check_Constant_Range(coef);
1592    }
1593    if (vj.nint >= lpd->mac || vi.nint >= lpd->mac) { Bip_Error(RANGE_ERROR); }
1594
1595    if (lpd->cb_cnt == 0)		/* first quadratic coefficient */
1596    {
1597	/* change the problem type to quadratic if linear */
1598	switch (lpd->prob_type)
1599	{
1600	case PROBLEM_LP:
1601	    lpd->prob_type = PROBLEM_QP;
1602	    break;
1603	case PROBLEM_MIP:
1604	    lpd->prob_type = PROBLEM_MIQP;
1605	    break;
1606	case PROBLEM_QP:
1607	case PROBLEM_MIQP:
1608	    /* nothing to be done if already quadratic */
1609	    break;
1610	default:
1611	    Fprintf(Current_Error,
1612	       "Eplex error: quadratic objective coefficients cannot be added to problem type %d\n.",
1613	    lpd->prob_type);
1614	    Bip_Error(RANGE_ERROR);
1615	}
1616    }
1617    if (lpd->cb_cnt >= lpd->cb_sz)	/* grow arrays if necessary */
1618	_grow_cb_arrays(lpd, 1);
1619    i = lpd->cb_cnt++;
1620    lpd->cb_index[i] = vi.nint;
1621    lpd->cb_index2[i] = vj.nint;
1622    lpd->cb_value[i] = vi.nint==vj.nint ? 2*coef : coef;
1623    Succeed;
1624}
1625
1626
1627int
1628p_cpx_load_varname(value vlp, type tlp, value vj, type tj, value vname, type tname)
1629{
1630    lp_desc *lpd;
1631
1632
1633#ifdef XPRESS
1634      int maxlen = 128;
1635      size_t namelen;
1636      char buffer[128];
1637#endif
1638
1639    Check_Integer(tj);
1640    Check_String(tname);
1641    LpDesc(vlp, tlp, lpd);
1642    if (vj.nint >= lpd->mac) { Bip_Error(RANGE_ERROR); }
1643
1644#ifdef XPRESS
1645    /* need to use temp. buffer in XPRESS as string is length limited */
1646    namelen = strlen(StringStart(vname));
1647    if (maxlen >= (int) namelen) {
1648    /* just in case the call fails, assign some maxlen in that case */
1649    if (XPRSgetintcontrol(lpd->lp, XPRS_MPSNAMELENGTH, &maxlen) != 0) maxlen = 8;
1650      strcpy(buffer, StringStart(vname));
1651    } else {
1652      strncpy(buffer, StringStart(vname), (size_t) maxlen);
1653      buffer[maxlen] = '\0';
1654    }
1655    Log5({
1656	char buffer[%d];
1657	strncpy(buffer, "%s", %d);
1658	XPRSaddnames(lpd->lp, 2, buffer, %d, %d);
1659    }, maxlen, buffer, maxlen, vj.nint, vj.nint);
1660    XPRSaddnames(lpd->lp, 2, buffer, vj.nint, vj.nint);
1661
1662#else
1663    /* this assumes that vname.str will be copied upto first \0 */
1664    /* we use CPXchgname() as this is in version 4 */
1665    Log2(CPXchgname(cpx_env, lpd->lp, 'c', %d, %s), vj.nint, StringStart(vname));
1666    CPXchgname(cpx_env, lpd->lp, 'c', vj.nint, StringStart(vname));
1667#endif
1668    Succeed;
1669}
1670
1671
1672/*----------------------------------------------------------------------*
1673 * Changing rhs
1674 * cplex_change_rhs(++CPH, ++Size, ++RowIdxs, ++RhsCoeffs)
1675 *   Changes the rhs coefficients. RowIdxs and RhsCoeffs are lists of length
1676 *   Size. There is no provision for backtracking: the changes should be
1677 *   undone with another call to cplex_change_rhs
1678 */
1679int
1680p_cpx_change_rhs(value vlp, type tlp, value vsize, type tsize,
1681	      value vidxs, type tidxs, value vvals, type tvals)
1682{
1683    lp_desc *lpd;
1684    int i, err;
1685
1686    LpDesc(vlp, tlp, lpd);
1687    Check_Integer(tsize);
1688
1689    if (vsize.nint > 0)
1690    {
1691	double *rhs = (double *) Malloc(vsize.nint * sizeof(double));
1692	int *idxs = (int *) Malloc(vsize.nint * sizeof(int));
1693
1694	for (i=0; i<vsize.nint; ++i)
1695	{
1696	    if (IsList(tidxs) && IsList(tvals))
1697	    {
1698		pword *ihead = vidxs.ptr;
1699		pword *itail = ihead + 1;
1700		pword *vhead = vvals.ptr;
1701		pword *vtail = vhead + 1;
1702
1703		Dereference_(ihead);
1704		Dereference_(vhead);
1705		if (!IsInteger(ihead->tag) || !IsNumber(vhead->tag))
1706		{
1707		    Free(idxs);
1708		    Free(rhs);
1709		    Bip_Error(TYPE_ERROR);
1710		}
1711		idxs[i] = ihead->val.nint;
1712		rhs[i] = DoubleVal(vhead->val, vhead->tag);
1713		/* check that the row index and rhs value are in range... */
1714		if (idxs[i] < 0 || idxs[i] >= lpd->mar ||
1715		    rhs[i] < -CPX_INFBOUND || rhs[i] > CPX_INFBOUND)
1716		{
1717		    Free(idxs);
1718		    Free(rhs);
1719		    Bip_Error(RANGE_ERROR);
1720		}
1721
1722		Dereference_(itail);
1723		Dereference_(vtail);
1724		tidxs = itail->tag;
1725		vidxs = itail->val;
1726		tvals = vtail->tag;
1727		vvals = vtail->val;
1728	    }
1729	    else
1730	    {
1731		Free(idxs);
1732		Free(rhs);
1733		Bip_Error(TYPE_ERROR);
1734	    }
1735	}
1736
1737	Call(err, CPXchgrhs(cpx_env, lpd->lp, vsize.nint, idxs, rhs));
1738
1739	Free(idxs);
1740	Free(rhs);
1741	if (err) { Bip_Error(EC_EXTERNAL_ERROR); }
1742    }
1743
1744    Succeed;
1745}
1746
1747/*----------------------------------------------------------------------*
1748 * Changing column bounds
1749 * cplex_change_cols_bounds(++CPH, ++Size, ++Idxs, ++Los, ++His)
1750 *   Changes the lower and upper bounds of columns. Idxs, Los, His are
1751 *   lists of length Size. There is no provision for backtracking: the
1752 *   changes should be  undone with another call to cplex_change_cols_bounds
1753 *
1754 */
1755int
1756p_cpx_change_cols_bounds(value vlp, type tlp, value vsize, type tsize,
1757      value vidxs, type tidxs, value vlos, type tlos, value vhis, type this)
1758{
1759    lp_desc *lpd;
1760    int i, err, size;
1761
1762    LpDesc(vlp, tlp, lpd);
1763    Check_Integer(tsize);
1764
1765    size = vsize.nint*2;
1766    if (size > 0)
1767    {
1768	double *bds = (double *) Malloc(size * sizeof(double));
1769	int *idxs = (int *) Malloc(size * sizeof(double));
1770	char *types = (char *) Malloc(size * sizeof(char));
1771
1772	for (i=0; i<size; )
1773	{
1774	    if (IsList(tidxs) && IsList(tlos) && IsList(this))
1775	    {
1776		pword *ihead = vidxs.ptr;
1777		pword *itail = ihead + 1;
1778		pword *lohead = vlos.ptr;
1779		pword *lotail = lohead + 1;
1780		pword *uphead = vhis.ptr;
1781		pword *uptail = uphead + 1;
1782
1783		Dereference_(ihead);
1784		Dereference_(lohead);
1785		Dereference_(uphead);
1786		if (!IsInteger(ihead->tag) || !IsNumber(lohead->tag) ||
1787		    !IsNumber(uphead->tag))
1788		{
1789		    Free(bds);
1790		    Free(idxs);
1791		    Free(types);
1792		    Bip_Error(TYPE_ERROR);
1793		}
1794		idxs[i] = ihead->val.nint;
1795		if (idxs[i] < 0 || idxs[i] >= lpd->mac)
1796		{
1797		    Free(bds);
1798		    Free(idxs);
1799		    Free(types);
1800		    Bip_Error(RANGE_ERROR);
1801		}
1802		bds[i] = DoubleVal(lohead->val, lohead->tag);
1803		if (bds[i] <= -CPX_INFBOUND) bds[i] = -CPX_INFBOUND;
1804		types[i++] = 'L';
1805
1806		idxs[i] = ihead->val.nint;
1807		bds[i] = DoubleVal(uphead->val, uphead->tag);
1808		if (bds[i] >= CPX_INFBOUND) bds[i] = CPX_INFBOUND;
1809		if (bds[i] < bds[i-1])
1810		{
1811		    Free(bds);
1812		    Free(idxs);
1813		    Free(types);
1814		    Fail;
1815		}
1816		types[i++] = 'U';
1817
1818		Dereference_(itail);
1819		Dereference_(lotail);
1820		Dereference_(uptail);
1821		tidxs = itail->tag;
1822		vidxs = itail->val;
1823		tlos = lotail->tag;
1824		vlos = lotail->val;
1825		this = uptail->tag;
1826		vhis = uptail->val;
1827	    } else
1828	    {
1829		Free(bds);
1830		Free(idxs);
1831		Free(types);
1832		Bip_Error(TYPE_ERROR);
1833	    }
1834	}
1835
1836	Call(err, CPXchgbds(cpx_env, lpd->lp, size, idxs, types, bds));
1837	Free(bds);
1838	Free(idxs);
1839	Free(types);
1840
1841	if (err) { Bip_Error(EC_EXTERNAL_ERROR); }
1842    }
1843
1844    Succeed;
1845}
1846
1847int
1848p_cpx_lo_hi(value vlo, type tlo, value vhi, type thi)
1849{
1850    Prepare_Requests;
1851    Request_Unify_Float(vlo, tlo, -CPX_INFBOUND);
1852    Request_Unify_Float(vhi, thi, CPX_INFBOUND);
1853    Return_Unify;
1854}
1855
1856/*----------------------------------------------------------------------*
1857 * Changing problem type
1858 */
1859
1860static void _cpx_reset_probtype ARGS((pword*,word*,int,int));
1861
1862static void
1863_cpx_reset_probtype(pword * pw, word * pdata, int size, int flags)
1864{
1865    int err;
1866    lp_desc *lpd = ExternalData(pw[HANDLE_CPH].val.ptr);
1867
1868    if (!lpd)
1869	return; /* stale handle */
1870
1871    if (lpd->descr_state != DESCR_EMPTY)
1872    {
1873#ifdef CPLEX
1874	switch  (((untrail_ptype*) pdata)->old_ptype)
1875	{
1876	case PROBLEM_LP:
1877	    Call(err, CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_LP));
1878	    break;
1879	case PROBLEM_QP:
1880	    Call(err, CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_QP));
1881	    break;
1882	default:
1883	    Fprintf(Current_Error, "Eplex problem: Trying to reset an Eplex problem to a unsupported type: %i.\n",
1884		    ((untrail_ptype*) pdata)->old_ptype);
1885	    ec_flush(Current_Error);
1886	    return;
1887
1888	}
1889#endif
1890	lpd->prob_type = ((untrail_ptype*) pdata)->old_ptype;
1891#ifdef SOLVE_MIP_COPY
1892	if (lpd->copystatus != XP_COPYOFF && lpd->lp != lpd->lpcopy)
1893	{
1894	    CallN(XPRSdestroyprob(lpd->lpcopy));
1895	    CallN(lpd->lpcopy = lpd->lp);
1896	    Mark_Copy_As_Modified(lpd);
1897	}
1898#endif
1899    }
1900}
1901
1902int
1903p_cpx_change_lp_to_mip(value vhandle, type thandle)
1904{
1905    lp_desc *lpd;
1906    int err;
1907    untrail_ptype pdata;
1908
1909    LpDesc(vhandle.ptr[HANDLE_CPH].val, vhandle.ptr[HANDLE_CPH].tag, lpd);
1910    pdata.old_ptype = lpd->prob_type;
1911
1912    /* all columns are assumed to be type 'C' by changing to MIP. Any
1913       integer columns will be set explicitly later
1914    */
1915    switch (pdata.old_ptype)
1916    {
1917    case PROBLEM_LP:
1918	lpd->prob_type = PROBLEM_MIP;
1919	Call(err, CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_MILP));
1920	break;
1921    case PROBLEM_QP:
1922#ifdef HAS_MIQP
1923	lpd->prob_type = PROBLEM_MIQP;
1924	Call(err, CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_MIQP));
1925#else
1926	Fprintf(Current_Error, "Eplex error: this solver does not support solving of quadratic MIP problems.\n");
1927	ec_flush(Current_Error);
1928	Bip_Error(UNIMPLEMENTED);
1929#endif
1930	break;
1931    case PROBLEM_MIQP:
1932    case PROBLEM_MIP:
1933	return PSUCCEED;
1934    default:
1935	Fprintf(Current_Error, "Eplex error: trying to change problem to mixed integer from an unexpected state.\n");
1936	ec_flush(Current_Error);
1937	Bip_Error(RANGE_ERROR);
1938	break;
1939    }
1940
1941    if (err) { Bip_Error(EC_EXTERNAL_ERROR); }
1942
1943#ifdef SOLVE_MIP_COPY
1944    if (lpd->copystatus != XP_COPYOFF)
1945    {
1946	Call(err, XPRScreateprob(&lpd->lpcopy));
1947	if (err) { Bip_Error(EC_EXTERNAL_ERROR); }
1948	Mark_Copy_As_Modified(lpd);
1949    }
1950#endif
1951    ec_trail_undo(_cpx_reset_probtype, vhandle.ptr, NULL, (word*)&pdata, NumberOfWords(untrail_ptype), TRAILED_WORD32);
1952    return PSUCCEED;
1953}
1954
1955/* cplex_set_problem_type(CPH, ProbType, SetSolverType)
1956
1957   changes the problem type to ProbType. Used to set and reset the problem
1958   type during probing, when the problem type might be changed temporarily.
1959   SetSolverType applies to external solvers which has its own problem type
1960   (currently CPLEX): it specifies if the external solver's problem type
1961   should be set as well (1 = yes, 0 = no). CPLEX's problem type cannot
1962   always be changed when setting a problem type before a probe, e.g.
1963   CPXPROB_FIXEDMILP/MIQP can only be set if there is already a MILP/MIQP
1964   solution. Therefore, in these cases, the solver's problem type is only
1965   changed during the solving of the problem in p_cpx_optimise().
1966*/
1967int
1968p_cpx_set_problem_type(value vlp, type tlp, value vtype, type ttype,
1969		       value vsetsolver, type tsetsolver)
1970{
1971    lp_desc *lpd;
1972    LpDescOnly(vlp, tlp, lpd);
1973
1974    Check_Integer(ttype);
1975    Check_Integer(tsetsolver);
1976#ifdef CPLEX
1977    if (vsetsolver.nint == 1)
1978    {
1979	int err;
1980	switch (vtype.nint)
1981	{
1982	case PROBLEM_LP:
1983	    Call(err,CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_LP));
1984	    break;
1985	case PROBLEM_MIP:
1986# ifndef HAS_RELAXEDLP
1987	    if (CPXgetprobtype(cpx_env, lpd->lp) == CPXPROB_LP &&
1988		lpd->ctype != NULL)
1989	    {
1990		/* this assumes that we have copied the ctype information
1991                   of original MIP problem to lpd->ctype
1992		*/
1993		Call(err, CPXcopyctype(cpx_env, lpd->lp, lpd->ctype));
1994		TryFree(lpd->ctype);
1995	    }
1996	    else
1997# endif
1998	    {
1999		Call(err,CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_MILP));
2000	    }
2001	    break;
2002	case PROBLEM_QP:
2003	    Call(err,CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_QP));
2004	    break;
2005# ifdef HAS_MIQP
2006	case PROBLEM_MIQP:
2007	    if (CPXgetprobtype(cpx_env, lpd->lp) == CPXPROB_QP &&
2008		lpd->ctype != NULL)
2009	    {
2010		/* this assumes that we have copied the ctype information
2011                   of original MIQP problem to lpd->ctype
2012		*/
2013		Call(err, CPXcopyctype(cpx_env, lpd->lp, lpd->ctype));
2014		TryFree(lpd->ctype);
2015	    }
2016	    else
2017	    {
2018		Call(err,CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_MILP));
2019	    }
2020	    break;
2021	case PROBLEM_FIXEDQ:
2022	    Call(err, CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_FIXEDMIQP));
2023	    break;
2024# endif
2025	case PROBLEM_FIXEDL:
2026	    Call(err,CPXchgprobtype(cpx_env, lpd->lp, CPXPROB_FIXEDMILP));
2027	    break;
2028	default:
2029	    Bip_Error(RANGE_ERROR);
2030	}
2031	if (err) Bip_Error(EC_EXTERNAL_ERROR);
2032    }
2033#endif
2034
2035    lpd->prob_type = vtype.nint;
2036    Succeed;
2037}
2038
2039/*----------------------------------------------------------------------*
2040 * Changing column type
2041 */
2042
2043#define Get_Col_Bounds(j, lo0,hi0) {                    \
2044        if (CPXgetlb(cpx_env, lpd->lp, &lo0, j,  j))	\
2045	    { Bip_Error(EC_EXTERNAL_ERROR); }		\
2046        if (CPXgetub(cpx_env, lpd->lp, &hi0, j, j))	\
2047            { Bip_Error(EC_EXTERNAL_ERROR); }		\
2048}
2049
2050#define Change_Col_Bound(j, Which, oldlo, oldhi, newbd, Stamp, changed) {\
2051    untrail_bound udata;					       \
2052    udata.bds[0] = oldlo;                                              \
2053    udata.bds[1] = oldhi;                                              \
2054    udata.idx = j;                                                     \
2055                                                                       \
2056    Log3(                                                              \
2057	 {\n\
2058            int myj      = %d;\n\
2059	    double bd  = %.15e;\n\
2060	    CPXchgbds(cpx_env, lpd->lp, 1, &myj, "%s", &bd);\n\
2061         }, j, newbd, Which                                            \
2062    );                                                                 \
2063                                                                       \
2064    CPXchgbds(cpx_env, lpd->lp, 1, &j, Which, &newbd);                 \
2065    changed = 1;                                                       \
2066    ec_trail_undo(_cpx_restore_bounds, vhandle.ptr,                    \
2067                  Stamp, (word *) &udata,                              \
2068		  NumberOfWords(untrail_bound), TRAILED_WORD32);       \
2069}
2070
2071
2072static void _cpx_restore_bounds ARGS((pword*,word*,int,int));
2073
2074static void _cpx_reset_col_type ARGS((pword*,word*,int,int));
2075
2076int
2077p_cpx_change_col_type(value vhandle, type thandle,
2078		      value vj, type tj,
2079		      value vtype, type ttype)
2080{
2081    int idx[1], res;
2082    char ctype[1];
2083    untrail_ctype udata;
2084    lp_desc *lpd;
2085#if defined(HAS_NARROW_INT_RANGE) || defined(HAS_INTLB_BUG)
2086    double lo0, hi0;
2087#endif
2088
2089    Check_Structure(thandle);
2090    LpDesc(vhandle.ptr[HANDLE_CPH].val, vhandle.ptr[HANDLE_CPH].tag, lpd);
2091    Check_Integer(tj);
2092
2093    if (vj.nint >= lpd->mac || vj.nint < 0) { Bip_Error(RANGE_ERROR); }
2094
2095    SetPreSolve(lpd->presolve);
2096    Mark_Copy_As_Modified(lpd);
2097    idx[0] = vj.nint;
2098    CPXupdatemodel(lpd->lp);	/* before CPXget... */
2099    res = CPXgetctype(cpx_env, lpd->lp, ctype, vj.nint, vj.nint);
2100
2101    if (res != 0) { Bip_Error(EC_EXTERNAL_ERROR); }
2102
2103    if ((char) vtype.nint != ctype[0]) {
2104	/* only need to change if new column type different */
2105	udata.idx = vj.nint;
2106	udata.ctype = ctype[0];
2107
2108	/* if change is from B->I, just ignore it (user could have posted
2109           extra integer constraints)
2110	*/
2111	if (!((char) vtype.nint == 'I' && (char) ctype[0] == 'B'))
2112	{
2113	    ctype[0] = (char) vtype.nint;
2114#if defined(HAS_NARROW_INT_RANGE) || defined(HAS_INTLB_BUG)
2115            {/* Xpress 14 likes its integers to have smaller bounds */
2116		Get_Col_Bounds(idx[0], lo0, hi0);
2117# ifdef HAS_NARROW_INT_RANGE
2118                /* no timestamp on these column bound changes: they will always
2119		   be untrailed. This is because the type changes may be
2120                   applied to multiple merged columns, and we need to
2121                   ensure that the bound change is always undone
2122		*/
2123		if (lo0 < -XPRS_MAXINT)
2124		{
2125		    double intbound = -XPRS_MAXINT;
2126		    int changed; /* dummy here for the macro */
2127		    Change_Col_Bound(idx[0], "L", lo0, hi0,
2128				     intbound, NULL, changed);
2129		    lo0 = intbound; /* for HAS_INTLB_BUG below */
2130		}
2131		if (hi0 > XPRS_MAXINT)
2132		{
2133		    double intbound = XPRS_MAXINT;
2134		    int changed; /* dummy here for the macro */
2135		    Change_Col_Bound(idx[0], "U", lo0, hi0,
2136				     intbound, NULL, changed);
2137		}
2138# endif
2139	    }
2140#endif
2141	    res = CPXchgctype(cpx_env, lpd->lp, 1, idx, ctype);
2142	    Log2(
2143	    {int  idx[1];
2144	    char ctype[1];
2145	    idx[0] = %d;
2146	    ctype[0] = '%c';
2147	    CPXchgctype(cpx_env,lpd->lp,1, idx, ctype);
2148	    }, idx[0], ctype[0]);
2149#ifdef HAS_INTLB_BUG
2150	    /* After changing a column type to integer, the lower bound is
2151               lost (set to 0) if it was negative (Xpress 13-15)
2152	       Reported to Dash 2004-10-19
2153	    */
2154	    {
2155		char btype[1];
2156		double lo1[1];
2157
2158		btype[0] = 'L';
2159		lo1[0] = lo0;
2160
2161		XPRSchgbounds(lpd->lp, 1, idx, btype, lo1);
2162	    }
2163	    Log2({\n\
2164		int myj = %d;\n\
2165		double bd = %.15e;\n\
2166		XPRSchgbounds(lpd->lp, 1, &myj, "L", &bd);\n\
2167	    }, idx[0], lo0);
2168#endif
2169
2170	    if (res != 0) { Bip_Error(EC_EXTERNAL_ERROR); }
2171	    ec_trail_undo(_cpx_reset_col_type, vhandle.ptr, NULL, (word*)&udata, NumberOfWords(untrail_ctype), TRAILED_WORD32);
2172	}
2173    }
2174    Succeed;
2175}
2176
2177
2178static void _cpx_reset_col_type(pword * phandle, word * udata, int size, int flags)
2179
2180{
2181    int idx[1];
2182    char octype[1];
2183
2184    lp_desc *lpd = ExternalData(phandle[HANDLE_CPH].val.ptr);
2185
2186    if (!lpd)
2187	return; /* stale handle */
2188
2189    idx[0] = ((untrail_ctype*) udata)->idx;
2190        octype[0] = ((untrail_ctype*) udata)->ctype;
2191
2192#if 0
2193    Fprintf(Current_Error, "Resetting col %d to %c, in gc:%d\n",
2194	idx[0], octype[0], ec_.m.vm_flags & NO_EXIT);
2195    ec_flush(Current_Error);
2196#endif
2197
2198    if (lpd->descr_state != DESCR_EMPTY)
2199    {
2200	Log2(
2201	{int  idx[1];\n\
2202	char octype[1];\n\
2203	idx[0] = %d;\n\
2204	octype[0] = '%c';\n\
2205	CPXchgctype(cpx_env,lpd->lp,1, idx, octype);\n\
2206	}, idx[0], octype[0]);
2207
2208	if (CPXchgctype(cpx_env, lpd->lp, 1, idx, octype))
2209	{
2210	    Fprintf(Current_Error, "Error in Changing column %d to type %c\n",
2211		    idx[0], octype[0]);
2212	    ec_flush(Current_Error);
2213	    return;
2214	}
2215      Mark_Copy_As_Modified(lpd);
2216    }
2217}
2218
2219
2220/*----------------------------------------------------------------------*
2221 * Adding constraints
2222 *----------------------------------------------------------------------*/
2223/*
2224 * We first collect the new row/col data in (growable) arrays using
2225 * p_cpx_set_matbeg(), p_cpx_set_matval() and p_cpx_set_obj_coeff() [cols]
2226 * p_cpx_new_row() and p_cpx_add_coeff() [rows].
2227 * Then, the information is transferred to the solver (and trailed)
2228 * by calling p_cpx_flush_new_rowcols().
2229 * On failure, the constraints get removed by _cpx_del_rowcols().
2230 *
2231 * added by AE 25/10/02
2232 * this is for adding rows whose index in the external
2233 * solver we want to know for sure - when we get duals
2234 * in colgen we really have to know we are getting the right
2235 * ones associated with the sp cost function vars
2236 * this requires all variables to have their index already in the attribute
2237 */
2238
2239#define New_Row(nrs, nr_sz, senx, rhsx, rmatbeg, nnz, sense, vrhs, trhs, ExtraAlloc) {\
2240    if (nrs+1 >= nr_sz)	/* allocate/grow arrays */\
2241    {\
2242	CallN(nr_sz += NEWROW_INCR);\
2243	CallN(senx = (char *) Realloc(senx, nr_sz*sizeof(char)));\
2244	CallN(rhsx = (double *) Realloc(rhsx, nr_sz*sizeof(double)));\
2245	CallN(rmatbeg = (int *) Realloc(rmatbeg, nr_sz*sizeof(int)));\
2246        ExtraAlloc; \
2247    }\
2248    senx[nrs] = (char) sense;\
2249    rhsx[nrs] = DoubleVal(vrhs, trhs);\
2250    Check_Constant_Range(rhsx[nrs]);\
2251    rmatbeg[nrs] = nnz;\
2252    ++nrs;\
2253}
2254
2255int
2256p_cpx_new_row(value vlp, type tlp, value vsense, type tsense,
2257	      value vrhs, type trhs, value vgtype, type tgtype,
2258	      value vidx, type tidx)
2259{
2260    lp_desc *lpd;
2261    int    idx, sense;
2262
2263    LpDescOnly(vlp, tlp, lpd);
2264    Check_Number(trhs);
2265    Check_Integer(tgtype);
2266    Check_Atom(tsense);
2267    if (vsense.did == d_le) sense = SOLVER_SENSE_LE;
2268    else if (vsense.did == d_ge) sense = SOLVER_SENSE_GE;
2269    else if (vsense.did == d_eq) sense = SOLVER_SENSE_EQ;
2270    else { Bip_Error(RANGE_ERROR); }
2271
2272    switch (vgtype.nint)
2273    {
2274    case CSTR_TYPE_NORM:
2275	idx = lpd->mar+lpd->nr;
2276	New_Row(lpd->nr, lpd->nr_sz, lpd->senx, lpd->rhsx, lpd->rmatbeg,
2277		lpd->nnz, sense, vrhs, trhs, {});
2278	break;
2279/*
2280    case CSTR_TYPE_PERMCP:
2281	idx = lpd->cp_nr;
2282	New_Row(lpd->cp_nr, lpd->cp_nr_sz, lpd->cp_senx, lpd->cp_rhsx,
2283		lpd->cp_rmatbeg, lpd->cp_nnz, sense, vrhs, trhs, {});
2284	break;
2285*/
2286    case CSTR_TYPE_CONDCP:
2287	idx = lpd->cp_nr2;
2288	New_Row(lpd->cp_nr2, lpd->cp_nr_sz2, lpd->cp_senx2, lpd->cp_rhsx2,
2289		lpd->cp_rmatbeg2, lpd->cp_nnz2, sense, vrhs, trhs,
2290		{CallN(lpd->cp_active2 = (char *) Realloc(lpd->cp_active2, lpd->cp_nr_sz2*sizeof(char)));
2291		 CallN(lpd->cp_initial_add2 = (char *) Realloc(lpd->cp_initial_add2, lpd->cp_nr_sz2*sizeof(char)));
2292		});
2293	break;
2294    default:
2295	Bip_Error(RANGE_ERROR);
2296	break;
2297    }
2298    Return_Unify_Integer(vidx, tidx, idx);
2299}
2300
2301
2302int
2303p_cpx_new_row_idc(value vlp, type tlp, value vsense, type tsense,
2304	  value vrhs, type trhs, value vcompl, type tcompl,
2305	  value vindind, type tindind)
2306{
2307    int sense;
2308    lp_desc *lpd;
2309
2310    LpDescOnly(vlp, tlp, lpd);
2311    Check_Number(trhs);
2312    Check_Integer(tcompl);
2313    Check_Integer(tindind);
2314    Check_Atom(tsense);
2315    if (vsense.did == d_le) sense = SOLVER_SENSE_LE;
2316    else if (vsense.did == d_ge) sense = SOLVER_SENSE_GE;
2317    else if (vsense.did == d_eq) sense = SOLVER_SENSE_EQ;
2318    else { Bip_Error(RANGE_ERROR); }
2319
2320    if (lpd->nr+1 >= lpd->nr_sz)	/* allocate/grow arrays */
2321    {
2322	CallN(lpd->nr_sz += NEWROW_INCR);
2323	CallN(lpd->senx = (char *) Realloc(lpd->senx, lpd->nr_sz*sizeof(char)));
2324	CallN(lpd->rhsx = (double *) Realloc(lpd->rhsx, lpd->nr_sz*sizeof(double)));
2325	CallN(lpd->rmatbeg = (int *) Realloc(lpd->rmatbeg, lpd->nr_sz*sizeof(int)));
2326	CallN(lpd->rcompl = (char *) Realloc(lpd->rcompl, lpd->nr_sz*sizeof(char)));
2327	CallN(lpd->rindind = (int *) Realloc(lpd->rindind, lpd->nr_sz*sizeof(int)));
2328    } else if (!lpd->rcompl) {
2329	/* Only used for IDC, may not be allocated yet */
2330	CallN(lpd->rcompl = (char *) Malloc(lpd->nr_sz*sizeof(char)));
2331	CallN(lpd->rindind = (int *) Malloc(lpd->nr_sz*sizeof(int)));
2332    }
2333    lpd->senx[lpd->nr] = (char) sense;
2334    lpd->rhsx[lpd->nr] = DoubleVal(vrhs, trhs);
2335    Check_Constant_Range(lpd->rhsx[lpd->nr]);
2336    lpd->rmatbeg[lpd->nr] = lpd->nnz;
2337    lpd->rcompl[lpd->nr] = vcompl.nint;	/* complement flag */
2338    lpd->rindind[lpd->nr] = vindind.nint;	/* indicator variable index */
2339    ++lpd->nr;
2340
2341    Succeed;
2342}
2343
2344
2345
2346#define Add_Row_Coeff(nnz_sz, nnzs, rmatind, rmatval, idxj, val, tag) {\
2347    if (nnzs >= nnz_sz)	/* allocate/grow arrays */\
2348    {\
2349        CallN(nnz_sz += NEWNZ_INCR);\
2350	CallN(rmatind = (int *) Realloc(rmatind, nnz_sz*sizeof(int)));\
2351	CallN(rmatval = (double *) Realloc(rmatval, nnz_sz*sizeof(double)));\
2352    }\
2353    if (idxj >= lpd->mac) { Bip_Error(RANGE_ERROR); }\
2354    rmatind[nnzs] = idxj;\
2355    rmatval[nnzs] = DoubleVal(val, tag);\
2356    Check_Constant_Range(rmatval[nnzs]);\
2357    ++nnzs;\
2358}
2359
2360int
2361p_cpx_add_coeff(value vlp, type tlp, value vj, type tj, value v, type t, value vpool, type tpool)
2362{
2363    lp_desc *lpd;
2364
2365    LpDescOnly(vlp, tlp, lpd);
2366    Check_Integer(tj);
2367    Check_Number(t);
2368    Check_Integer(tpool);
2369
2370    switch (vpool.nint)
2371    {
2372    case CSTR_TYPE_NORM:
2373	Add_Row_Coeff(lpd->nnz_sz, lpd->nnz, lpd->rmatind, lpd->rmatval, vj.nint, v, t);
2374	break;
2375/*
2376    case CSTR_TYPE_PERMCP:
2377	Add_Row_Coeff(lpd->cp_nz_sz, lpd->cp_nnz, lpd->cp_rmatind, lpd->cp_rmatval, vj.nint, v, t);
2378	break;
2379*/
2380    case CSTR_TYPE_CONDCP:
2381	Add_Row_Coeff(lpd->cp_nz_sz2, lpd->cp_nnz2, lpd->cp_rmatind2, lpd->cp_rmatval2, vj.nint, v, t);
2382	break;
2383    default:
2384	Bip_Error(RANGE_ERROR);
2385	break;
2386    }
2387    Succeed;
2388}
2389
2390static void
2391_grow_numbers_array(lp_desc * lpd, int m)   /* make sure array contains 0..m-1 */
2392{
2393    if (m > lpd->numsz)	/* grow auxiliary array if necessary */
2394    {
2395	int i = lpd->numsz;
2396	m = Max(m, i+NEWCOL_INCR);
2397	lpd->numsz = m;
2398# ifdef LOG_CALLS
2399	if (i == 0)
2400	    Fprintf(log_output_, "\n\
2401	    	lpd->numbers = (int *) malloc(%d*sizeof(int));\n\
2402	    	lpd->zeroes = (int *) malloc(%d*sizeof(int));\n\
2403	        lpd->dzeroes = (double *) malloc(%d*sizeof(double));", m, m, m);
2404	else
2405	    Fprintf(log_output_, "\n\
2406		lpd->numbers = (int *) realloc(lpd->numbers, %d*sizeof(int));\n\
2407		lpd->zeroes = (int *) realloc(lpd->zeroes, %d*sizeof(int));\n\
2408	        lpd->dzeroes = (int *) realloc(lpd->dzeroes, %d*sizeof(double));", m, m, m);
2409	Fprintf(log_output_, "\n\
2410	    { int i; for (i=%d; i<%d; i++)\n\
2411		{ lpd->numbers[i] = i; lpd->zeroes[i] = 0; lpd->dzeroes[i] = 0.0; } }", i, m);
2412# endif
2413	if (i == 0) {
2414	    lpd->numbers = (int *) Malloc(m*sizeof(int));
2415	    lpd->zeroes = (int *) Malloc(m*sizeof(int));
2416	    lpd->dzeroes = (double *) Malloc(m*sizeof(double));
2417	} else {
2418	    lpd->numbers = (int *) Realloc(lpd->numbers, m*sizeof(int));
2419	    lpd->zeroes = (int *) Realloc(lpd->zeroes, m*sizeof(int));
2420	    lpd->dzeroes = (double *) Realloc(lpd->dzeroes, m*sizeof(double));
2421	}
2422	for (; i < m; i++) {
2423	    lpd->numbers[i] = i;
2424	    lpd->zeroes[i] = 0;
2425	    lpd->dzeroes[i] = 0.0;
2426	}
2427    }
2428}
2429
2430
2431static void _cpx_restore_bounds(pword * phandle, word * udata, int size, int undo_context)
2432{
2433    lp_desc *lpd = ExternalData(phandle[HANDLE_CPH].val.ptr);
2434
2435    if (!lpd)
2436	return; /* stale handle */
2437
2438    if (lpd->descr_state != DESCR_EMPTY)
2439    {
2440	/* lp has not been cleaned up */
2441
2442	int idx[2];
2443	int res;
2444	untrail_bound adata; /* needed to ensure proper alignment */
2445
2446	memcpy(&adata, udata, sizeof(untrail_bound));
2447
2448	idx[0] = adata.idx;
2449	idx[1] = adata.idx;
2450
2451	Log4(
2452	      {
2453               int idx[2];
2454	       double bds[2];
2455               idx[0] = %d;
2456               idx[1] = %d;
2457	       bds[0] = %.15e;
2458	       bds[1] = %.15e;
2459	       CPXchgbds(cpx_env, lpd->lp, 2, idx, "LU", bds);
2460              }, idx[0], idx[1], adata.bds[0], adata.bds[1]
2461	);
2462
2463	res = CPXchgbds(cpx_env, lpd->lp, 2, idx, "LU", adata.bds);
2464	if (res != 0)
2465	{
2466	    Fprintf(Current_Error, "Eplex external solver error while trying to restore bounds.to column %d\n", idx[0]);
2467	    ec_flush(Current_Error);
2468
2469	}
2470    }
2471}
2472
2473
2474static void
2475reset_sos(lp_desc * lpd, int oldsos)
2476{
2477    if (lpd->nsos_added > oldsos)
2478    {
2479	if (cpx_delsos(lpd, oldsos, lpd->nsos_added))
2480	{
2481	    Fprintf(Current_Error, "Error in deleting SOSs %d..%d\n",
2482		oldsos, lpd->nsos_added);
2483	    ec_flush(Current_Error);
2484	}
2485	lpd->nsos = lpd->nsos_added = oldsos;
2486    }
2487}
2488
2489
2490static void
2491reset_idc(lp_desc * lpd, int oldidc)
2492{
2493#ifdef HAS_INDICATOR_CONSTRAINTS
2494    if (lpd->nidc > oldidc)
2495    {
2496	if (CPXdelindconstrs(cpx_env, lpd->lp, oldidc, lpd->nidc-1))
2497	{
2498	    Fprintf(Current_Error, "Error in deleting indicator constraints %d..%d\n",
2499		oldidc, lpd->nidc-1);
2500	    ec_flush(Current_Error);
2501	}
2502	lpd->nidc = oldidc;
2503    }
2504#endif
2505}
2506
2507
2508static void
2509reset_rowcols(lp_desc * lpd, int oldmar, int oldmac)
2510{
2511#ifdef CPLEX
2512	if (lpd->mar > oldmar)
2513	{
2514	    Log2(CPXdelrows(cpx_env, lpd->lp, %d, %d), oldmar, lpd->mar-1);
2515	    if (CPXdelrows(cpx_env, lpd->lp, oldmar, lpd->mar-1))
2516	    {
2517		Fprintf(Current_Error, "Error in CPXdelrows(%d..%d)\n",
2518			oldmar, lpd->mar-1);
2519		ec_flush(Current_Error);
2520	    }
2521	    lpd->mar = oldmar;
2522	}
2523	if (lpd->macadded > oldmac)
2524	{
2525	    Log2(CPXdelcols(cpx_env, lpd->lp, %d, %d), oldmac, lpd->macadded-1);
2526	    if (CPXdelcols(cpx_env, lpd->lp, oldmac, lpd->macadded-1))
2527	    {
2528		Fprintf(Current_Error, "Error in CPXdelcols(%d..%d)\n",
2529		    oldmac, lpd->macadded-1);
2530		ec_flush(Current_Error);
2531	    }
2532	    lpd->macadded = oldmac;
2533	}
2534#else
2535	int ndr = lpd->mar - oldmar;
2536	int ndc = lpd->macadded - oldmac;
2537	int m = Max(lpd->mar,lpd->macadded);
2538
2539	_grow_numbers_array(lpd, m);	/* if necessary */
2540
2541	if (ndr > 0)
2542	{
2543	    Log2(XPRSdelrows(lpd->lp,%d, &lpd->numbers[%d]), ndr,oldmar);
2544	    if (XPRSdelrows(lpd->lp, ndr, &lpd->numbers[oldmar]))
2545	    {
2546		Fprintf(Current_Error, "Error in deleting rows %d..%d\n",
2547			oldmar, oldmar+ndr-1);
2548		ec_flush(Current_Error);
2549	    }
2550	    lpd->mar = oldmar;
2551	}
2552	if (ndc > 0)
2553	{
2554	    Log2(XPRSdelcols(lpd->lp,%d, &lpd->numbers[%d]), ndc,oldmac);
2555	    if (XPRSdelcols(lpd->lp, ndc, &lpd->numbers[oldmac]))
2556	    {
2557		Fprintf(Current_Error, "Error in deleting cols %d..%d\n",
2558		    oldmac, oldmac+ndc-1);
2559		ec_flush(Current_Error);
2560	    }
2561	    lpd->macadded = oldmac;
2562	}
2563#endif
2564	lpd->mac = lpd->macadded;
2565	Mark_Copy_As_Modified(lpd);
2566}
2567
2568
2569static void _cpx_del_rowcols(pword * phandle,word * udata, int size, int flags)
2570{
2571    lp_desc *lpd = ExternalData(phandle[HANDLE_CPH].val.ptr);
2572
2573    int oldmar = ((untrail_data*) udata)->oldmar,
2574        oldmac = ((untrail_data*) udata)->oldmac,
2575        oldsos = ((untrail_data*) udata)->oldsos,
2576        oldidc = ((untrail_data*) udata)->oldidc;
2577
2578    if (lpd  &&  lpd->descr_state != DESCR_EMPTY)
2579    {
2580#if 0
2581	Fprintf(Current_Error,
2582	    "Removing rows %d..%d, cols %d..%d, soss %d..%d, idcs %d..%d, in gc:%d\n",
2583	    oldmar, lpd->mar-1, oldmac, lpd->macadded-1,
2584	    oldsos, lpd->nsos_added, oldidc, lpd->nidc,
2585	    ec_.m.vm_flags & NO_EXIT);
2586	ec_flush(Current_Error);
2587#endif
2588	reset_idc(lpd, oldidc);
2589	reset_sos(lpd, oldsos);
2590	reset_rowcols(lpd, oldmar, oldmac);
2591    }
2592}
2593
2594
2595/*
2596 * flush_new_rowcols(+Handle, +HasObjCoeffs) expects the following input:
2597 *
2598 *	lpd->mac	new column count (>= macadded, those already added)
2599 *	lpd->objx	objective coefficients for new vars only (if HasObjCoeffs)
2600 *	lpd->matnz	number of nonzero coefficients for new vars in old constraints
2601 *	lpd->matxxx	those nonzero coefficients
2602 *	lpd->ctype	types for new vars only
2603 *
2604 *	lpd->nr		number of rows to add
2605 *	lpd->senx	row senses
2606 *	lpd->rhsx	row RHSs
2607 *	lpd->nnz	number of nonzero coefficients to add
2608 *	lpd->rmatxxx	those nonzero coefficients
2609 *
2610 * We may add only columns or only rows.
2611 */
2612/* newcolobjs == 1  if non-zero objective coeffs are to be added */
2613int
2614p_cpx_flush_new_rowcols(value vhandle, type thandle, value vnewcolobjs, type tnewcolobjs)
2615{
2616    lp_desc *lpd;
2617    int res, coladded, rowadded, nzadded;
2618
2619    Check_Structure(thandle);
2620    LpDesc(vhandle.ptr[HANDLE_CPH].val, vhandle.ptr[HANDLE_CPH].tag, lpd);
2621    Check_Integer(tnewcolobjs);
2622    coladded = lpd->mac - lpd->macadded;
2623    rowadded = lpd->nr;
2624    nzadded = lpd->nnz;
2625
2626    /***
2627    Fprintf(Current_Error, "Adding rows %d..%d, cols %d..%d\n",
2628    	lpd->mar, lpd->mar+rowadded-1,
2629    	lpd->mac, lpd->mac+coladded-1);
2630    ec_flush(Current_Error);
2631    ***/
2632
2633    SetPreSolve(lpd->presolve);
2634    Mark_Copy_As_Modified(lpd);
2635
2636#ifdef LOG_CALLS
2637    {
2638	int i;
2639	Fprintf(log_output_, "\n\
2640		lpd->nr = %d;", lpd->nr);
2641	Fprintf(log_output_, "\n\
2642                lpd->matnz = %d;", lpd->matnz);
2643	/* needed by OSI */
2644	Fprintf(log_output_, "\n\
2645                lpd->mac = %d;", lpd->mac);
2646	if (vnewcolobjs.nint)
2647	{
2648	    for (i=0; i<coladded; ++i)
2649	    {
2650		Fprintf(log_output_, "\n\
2651                lpd->objx[%d] = %.15e;", i, lpd->objx[i]);
2652	    }
2653	}
2654	for (i=0; i<coladded; ++i)
2655	{
2656	    Fprintf(log_output_, "\n\
2657                lpd->bdl[%d] = %.15e;\n\
2658                lpd->bdu[%d] = %.15e;", i, lpd->bdl[i], i, lpd->bdu[i]);
2659	}
2660	for (i=0; i<lpd->matnz; ++i)
2661	{
2662	    Fprintf(log_output_, "\n\
2663                lpd->matind[%d] = %d;\n\
2664                lpd->matval[%d] = %.15e;", i, lpd->matind[i], i, lpd->matval[i]);
2665	}
2666	if (lpd->matnz > 0)
2667	{
2668	    for (i=0; i<coladded; ++i)
2669	    {
2670		Fprintf(log_output_, "\n\
2671                lpd->matbeg[%d] = %d;", i, lpd->matbeg[i]);
2672	    }
2673	}
2674
2675	for (i=0; i<lpd->nr; ++i)
2676	{
2677	    Fprintf(log_output_, "\n\
2678		lpd->senx[%d] = '%c';\n\
2679		lpd->rhsx[%d] = %.15e;\n\
2680		lpd->rmatbeg[%d] = %d;",
2681	    	i, lpd->senx[i], i, lpd->rhsx[i], i, lpd->rmatbeg[i]);
2682	}
2683	Fprintf(log_output_, "\n\
2684		lpd->nnz = %d;", lpd->nnz);
2685	for (i=0; i<lpd->nnz; ++i)
2686	{
2687	    Fprintf(log_output_, "\n\
2688		lpd->rmatind[%d] = %d;\n\
2689		lpd->rmatval[%d] = %.15e;",
2690	    	i, lpd->rmatind[i], i, lpd->rmatval[i]);
2691	}
2692    }
2693#endif
2694
2695    if (coladded)
2696    {
2697	_grow_numbers_array(lpd, coladded+1);	/* for zeroes[] */
2698	Log2(CPXaddcols(cpx_env, lpd->lp, %d, lpd->matnz,
2699			%s, (lpd->matnz ? lpd->matbeg : lpd->zeroes),
2700		       lpd->matind, lpd->matval, lpd->bdl, lpd->bdu, NULL),
2701	               coladded, (vnewcolobjs.nint ? "lpd->objx" : "lpd->dzeroes"));
2702
2703	res = CPXaddcols(cpx_env, lpd->lp, coladded, lpd->matnz,
2704			 (vnewcolobjs.nint ? lpd->objx : lpd->dzeroes),
2705			 (lpd->matnz ? lpd->matbeg : lpd->zeroes),
2706			 lpd->matind, lpd->matval, lpd->bdl, lpd->bdu, NULL);
2707	TryFree(lpd->objx);
2708	TryFree(lpd->matbeg);
2709	TryFree(lpd->matind);
2710	TryFree(lpd->matval);
2711	lpd->matnz = 0;
2712	if (lpd->dirtybdflag & 3)
2713	{
2714	    if (lpd->dirtybdflag & 1) TryFree(lpd->bdl);
2715	    if (lpd->dirtybdflag & 2) TryFree(lpd->bdu);
2716	    lpd->dirtybdflag = 0;
2717	}
2718
2719	if (IsMIPProb(lpd->prob_type) && res == 0)
2720	{
2721	    int i, colidx;
2722	    if (lpd->qgtype) CallN(Free(lpd->qgtype));
2723	    lpd->qgtype = NULL;
2724	    if (lpd->mgcols) CallN(Free(lpd->mgcols));
2725	    lpd->mgcols = Malloc(coladded*sizeof(int));
2726	    Log1(lpd->mgcols = (int *) malloc(%d*sizeof(int)), coladded);
2727	    /* we reuse mgcols for the index of the added columns. We set the
2728               type for all columns, as Dash does not specify what types
2729               columns are set to in XPRSaddcols().
2730	    */
2731	    for ((i=0, colidx=lpd->macadded); i < coladded; (i++, colidx++))
2732	    {
2733		lpd->mgcols[i] = colidx;
2734		Log2(lpd->mgcols[%d] = %d, i, colidx);
2735		Log2(lpd->ctype[%d] = %d, i, lpd->ctype[i]);
2736	    }
2737	    CPXupdatemodel(lpd->lp);	/* columns must be added before type can be set */
2738	    Log1(CPXchgctype(cpx_env, lpd->lp, %d, lpd->mgcols, lpd->ctype),
2739		 coladded);
2740	    res = CPXchgctype(cpx_env, lpd->lp, coladded,
2741				  lpd->mgcols, lpd->ctype);
2742	}
2743
2744	if (res != 0) { Bip_Error(EC_EXTERNAL_ERROR); }
2745    }
2746    CPXupdatemodel(lpd->lp);	/* columns must be added before rows can be created */
2747    if (rowadded)
2748    {
2749	Log2(CPXaddrows(cpx_env, lpd->lp, 0, %d, %d, lpd->rhsx, lpd->senx,
2750			lpd->rmatbeg, lpd->rmatind, lpd->rmatval, NULL, NULL),
2751	     rowadded, nzadded);
2752	res = CPXaddrows(cpx_env, lpd->lp, 0, rowadded, nzadded,
2753			 lpd->rhsx, lpd->senx, lpd->rmatbeg, lpd->rmatind,
2754			 lpd->rmatval, NULL, NULL);
2755	if (res != 0) { Bip_Error(EC_EXTERNAL_ERROR); }
2756    }
2757
2758    if (coladded || rowadded)
2759    {
2760      untrail_data udata;
2761      udata.oldmac = lpd->macadded;
2762      udata.oldmar = lpd->mar;
2763      udata.oldsos = lpd->nsos_added;
2764      udata.oldidc = lpd->nidc;
2765      ec_trail_undo(_cpx_del_rowcols, vhandle.ptr, vhandle.ptr+HANDLE_STAMP, (word*) &udata, NumberOfWords(untrail_data), TRAILED_WORD32);
2766    }
2767
2768    lpd->macadded = lpd->mac;	/* remember what we added */
2769    lpd->mar += rowadded;
2770
2771    lpd->nr = lpd->nnz = 0;	/* maybe shrink arrays here */
2772    Succeed;
2773}
2774
2775
2776/*
2777 * Add Indicator Constraints from descriptor arrays to solver
2778 * Input:
2779 *	lpd->nr		number of indicator rows to add
2780 *	lpd->senx	row senses
2781 *	lpd->rhsx	row RHSs
2782 *	lpd->nnz	number of nonzero coefficients to add
2783 *	lpd->rmatxxx	those nonzero coefficients
2784 *	lpd->rcompl	the complement flags
2785 *	lpd->rindind	the indicator variable indexes
2786 */
2787
2788int
2789p_cpx_flush_idcs(value vhandle, type thandle)
2790{
2791#ifdef HAS_INDICATOR_CONSTRAINTS
2792    int i;
2793    lp_desc *lpd;
2794    untrail_data udata;
2795
2796    Check_Structure(thandle);
2797    LpDesc(vhandle.ptr[HANDLE_CPH].val, vhandle.ptr[HANDLE_CPH].tag, lpd);
2798
2799    if (lpd->nr == 0)
2800    	Succeed;
2801
2802    /* trail first, in case we abort during adding */
2803    udata.oldmac = lpd->macadded;
2804    udata.oldmar = lpd->mar;
2805    udata.oldsos = lpd->nsos_added;
2806    udata.oldidc = lpd->nidc;
2807    ec_trail_undo(_cpx_del_rowcols, vhandle.ptr, vhandle.ptr+HANDLE_STAMP, (word*) &udata, NumberOfWords(untrail_data), TRAILED_WORD32);
2808
2809    lpd->rmatbeg[lpd->nr] = lpd->nnz;
2810    for(i=0; lpd->nr>0; --lpd->nr,++i)
2811    {
2812#if 0
2813	int k;
2814	Fprintf(Current_Error, "CPXaddindconstr(%d,%d,%d,%f,%d,...,...)\n",
2815		lpd->rindind[i], lpd->rcompl[i],
2816		 lpd->rmatbeg[i+1]-lpd->rmatbeg[i],	/* nzcnt */
2817		 lpd->rhsx[i], lpd->senx[i]);
2818	for(k=lpd->rmatbeg[i];k<lpd->rmatbeg[i+1];++k) {
2819	    Fprintf(Current_Error, "%d:%f, ", lpd->rmatind[k], lpd->rmatval[k]);
2820	}
2821	(void) ec_newline(Current_Error);
2822	(void) ec_flush(Current_Error);
2823#endif
2824
2825	if (CPXaddindconstr(cpx_env, lpd->lp, lpd->rindind[i], lpd->rcompl[i],
2826		 lpd->rmatbeg[i+1]-lpd->rmatbeg[i],	/* nzcnt */
2827		 lpd->rhsx[i], lpd->senx[i],
2828		 lpd->rmatind+lpd->rmatbeg[i], lpd->rmatval+lpd->rmatbeg[i],
2829		 NULL))
2830	{
2831	    Bip_Error(EC_EXTERNAL_ERROR);
2832	}
2833	++lpd->nidc;
2834    }
2835    /* could free/resize arrays here */
2836    Succeed;
2837#else
2838    Bip_Error(UNIMPLEMENTED);
2839#endif
2840}
2841
2842
2843/*----------------------------------------------------------------------*
2844 * Updating variable bounds
2845 *----------------------------------------------------------------------*/
2846
2847/* the *impose* procedures are for columns that have been added to the
2848   external solver already.
2849*/
2850int
2851p_cpx_impose_col_lwb(value vhandle, type thandle,
2852		     value vatt, type tatt,
2853		     value vj, type tj,
2854		     value vlo, type tlo,
2855		     value vchanged, type tchanged)
2856{
2857    lp_desc *lpd;
2858    double lo0, hi0, newlo;
2859    int j, changed = 0;
2860    Check_Integer(tj);
2861    Check_Float(tlo);
2862    LpDesc(vhandle.ptr[HANDLE_CPH].val, vhandle.ptr[HANDLE_CPH].tag, lpd);
2863
2864    if (lpd->descr_state == DESCR_EMPTY)
2865    {
2866	Fprintf(Current_Error, "Eplex error: empty handle\n");
2867	(void) ec_flush(Current_Error);
2868	Bip_Error(EC_EXTERNAL_ERROR);
2869    }
2870
2871    j = (int) vj.nint;
2872    if (j >= lpd->macadded) { Bip_Error(RANGE_ERROR); }
2873    if ((newlo = Dbl(vlo)) < -CPX_INFBOUND) newlo = -CPX_INFBOUND;
2874
2875    CPXupdatemodel(lpd->lp);	/* make sure bounds are up-to-date */
2876    Get_Col_Bounds(j, lo0, hi0);
2877    if (newlo > hi0)
2878    {
2879	double ftol;
2880
2881	Get_Feasibility_Tolerance(cpx_env, lpd, &ftol);
2882	if (newlo <= hi0 + ftol) newlo = hi0;
2883	else { Fail; }
2884    }
2885
2886    if (lo0 < newlo)
2887    {
2888	Change_Col_Bound(j, "L", lo0, hi0, newlo, vatt.ptr+COL_STAMP, changed);
2889    }
2890
2891    Return_Unify_Integer(vchanged, tchanged, changed);
2892}
2893
2894int
2895p_cpx_impose_col_upb(value vhandle, type thandle,
2896		     value vatt, type tatt,
2897		     value vj, type tj,
2898		     value vhi, type thi,
2899		     value vchanged, type tchanged)
2900{
2901    lp_desc *lpd;
2902    double lo0, hi0, newhi;
2903    int j, changed = 0;
2904
2905    LpDesc(vhandle.ptr[HANDLE_CPH].val, vhandle.ptr[HANDLE_CPH].tag, lpd);
2906    Check_Integer(tj);
2907    Check_Float(thi);
2908
2909    if (lpd->descr_state == DESCR_EMPTY)
2910    {
2911	Fprintf(Current_Error, "Eplex error: empty handle\n");
2912	(void) ec_flush(Current_Error);
2913	Bip_Error(EC_EXTERNAL_ERROR);
2914    }
2915
2916    j = (int) vj.nint;
2917    if (j >= lpd->macadded) { Bip_Error(RANGE_ERROR); }
2918    if ((newhi = Dbl(vhi)) > CPX_INFBOUND) newhi = CPX_INFBOUND;
2919
2920    CPXupdatemodel(lpd->lp);	/* make sure bounds are up-to-date */
2921    Get_Col_Bounds(j, lo0, hi0);
2922    if (newhi < lo0)
2923    {
2924	double ftol;
2925
2926	Get_Feasibility_Tolerance(cpx_env, lpd, &ftol);
2927	if (newhi >= lo0 - ftol) newhi = lo0;
2928	else { Fail; }
2929    }
2930
2931    if (hi0 > newhi)
2932    {
2933	Change_Col_Bound(j, "U", lo0, hi0, newhi, vatt.ptr+COL_STAMP, changed);
2934    }
2935
2936    Return_Unify_Integer(vchanged, tchanged, changed);
2937}
2938
2939int
2940p_cpx_impose_col_bounds(value vhandle, type thandle,
2941			value vatt, type tatt,
2942			value vj, type tj,
2943			value vflag, type tflag,
2944			value vlo, type tlo,
2945			value vhi, type thi,
2946			value vchanged, type tchanged)
2947{
2948    lp_desc *lpd;
2949    double  lo0, hi0, newlo, newhi;
2950    int     res, j, flag, changed = 0;
2951    char    ctype[1];
2952
2953    Check_Integer(tj);
2954    Check_Integer(tflag);
2955    Check_Float(thi);
2956    Check_Float(tlo);
2957    LpDesc(vhandle.ptr[HANDLE_CPH].val, vhandle.ptr[HANDLE_CPH].tag, lpd);
2958
2959    if (lpd->descr_state == DESCR_EMPTY)
2960    {
2961	Fprintf(Current_Error, "Eplex error: empty handle\n");
2962	(void) ec_flush(Current_Error);
2963	Bip_Error(EC_EXTERNAL_ERROR);
2964    }
2965
2966    CPXupdatemodel(lpd->lp);	/* make sure types and bounds are up-to-date */
2967
2968    j = (int) vj.nint;
2969    if (j >= lpd->macadded || j < 0) { Bip_Error(RANGE_ERROR); }
2970    if ((newhi = Dbl(vhi)) > CPX_INFBOUND) newhi = CPX_INFBOUND;
2971    if ((newlo = Dbl(vlo)) < -CPX_INFBOUND) newlo = -CPX_INFBOUND;
2972
2973    flag = (int) vflag.nint;
2974    if (flag == 0) {
2975	/* flag == 0 ==> we can widen the bound. Check to make sure that the new
2976           bounds are not too wide, i.e. invalid for the column type
2977	*/
2978        switch (lpd->prob_type)
2979	{
2980	case PROBLEM_MIP:
2981	case PROBLEM_MIQP:
2982	    res = CPXgetctype(cpx_env, lpd->lp, ctype, vj.nint, vj.nint);
2983	    if (res != 0) { Bip_Error(EC_EXTERNAL_ERROR); }
2984
2985	    if (ctype[0] == 'B' && (newhi > 1 || newhi < 0 || newlo > 1 || newlo < 0))
2986	        { Bip_Error(RANGE_ERROR); }
2987#ifdef HAS_NARROW_INT_RANGE
2988	    if (ctype[0] == 'I' && (newhi > XPRS_MAXINT || newhi < -XPRS_MAXINT || newlo > XPRS_MAXINT || newlo < -XPRS_MAXINT))
2989	        { Bip_Error(RANGE_ERROR); }
2990#endif
2991	default:
2992	    break;
2993	}
2994    }
2995
2996    Get_Col_Bounds(j, lo0, hi0);
2997    if (newhi < newlo) { Fail; }
2998    if (flag && (newhi < lo0))
2999    {
3000	double ftol;
3001
3002	/* if new bound outside but within tolerance of old bound, ignore change */
3003	Get_Feasibility_Tolerance(cpx_env, lpd, &ftol);
3004	if (newhi >= lo0 - ftol)
3005	{
3006	    newhi = lo0;
3007	    if (newlo > newhi) newlo = lo0; /* make sure other bound is consistent! */
3008	}
3009	else
3010	{
3011	    Fail;
3012	}
3013    }
3014
3015    if (flag && (newlo > hi0))
3016    {
3017	double ftol;
3018
3019	Get_Feasibility_Tolerance(cpx_env, lpd, &ftol);
3020	if (newlo <= hi0 + ftol)
3021	{
3022	    newlo = hi0;
3023	    if (newhi < newlo) newhi = hi0;
3024	} else
3025	{
3026	    Fail;
3027	}
3028    }
3029
3030    if (newhi == newlo)
3031    {
3032	if (newhi != hi0 || newhi != lo0)
3033	    Change_Col_Bound(j, "B", lo0, hi0, newlo, vatt.ptr+COL_STAMP,
3034			     changed);
3035    }
3036    else
3037    {
3038	if (newhi < hi0 || (!flag && newhi > hi0)) Change_Col_Bound(j, "U", lo0, hi0, newhi,
3039								    vatt.ptr+COL_STAMP, changed);
3040	if (newlo > lo0 || (!flag && newlo < lo0)) Change_Col_Bound(j, "L", lo0, hi0, newlo,
3041								    vatt.ptr+COL_STAMP, changed);
3042    }
3043
3044    Return_Unify_Integer(vchanged, tchanged, changed);
3045}
3046
3047int
3048p_cpx_get_col_bounds(value vlp, type tlp,
3049		     value vj, type tj,
3050		     value vlo, type tlo,
3051		     value vhi, type thi)
3052{
3053    Prepare_Requests
3054    lp_desc *lpd;
3055    double lo, hi;
3056    int j;
3057
3058    LpDesc(vlp, tlp, lpd);
3059    Check_Integer(tj);
3060    j = vj.nint;
3061
3062    if (lpd->descr_state == DESCR_EMPTY)
3063    {
3064	Fprintf(Current_Error, "Eplex error: empty handle\n");
3065	(void) ec_flush(Current_Error);
3066	Bip_Error(EC_EXTERNAL_ERROR);
3067    }
3068
3069    if (lpd->lp == NULL)
3070    {/* solver not created for problem yet, get bounds from arrays */
3071	Request_Unify_Float(vhi, thi, lpd->bdu[j]);
3072	Request_Unify_Float(vlo, tlo, lpd->bdl[j]);
3073	Return_Unify;
3074    }
3075    else if (j >= lpd->mac || j < 0)
3076    {/* invalid column index */
3077	Bip_Error(RANGE_ERROR);
3078    }
3079    else if (j >= lpd->macadded)
3080    {/* column not yet added to solver, get bounds from arrays */
3081	j -= lpd->macadded; /* arrays are for new added columns only */
3082	Request_Unify_Float(vhi, thi, lpd->bdu[j]);
3083	Request_Unify_Float(vlo, tlo, lpd->bdl[j]);
3084	Return_Unify;
3085    }
3086    else
3087    {
3088	CPXupdatemodel(lpd->lp);	/* before CPXget... */
3089	if (CPXgetub(cpx_env, lpd->lp, &hi, (int) vj.nint, (int) vj.nint))
3090	{
3091	    Bip_Error(EC_EXTERNAL_ERROR);
3092	}
3093	if (CPXgetlb(cpx_env, lpd->lp, &lo, (int) vj.nint, (int) vj.nint))
3094	{
3095	    Bip_Error(EC_EXTERNAL_ERROR);
3096	}
3097	Request_Unify_Float(vhi, thi, hi);
3098	Request_Unify_Float(vlo, tlo, lo);
3099	Return_Unify;
3100    }
3101}
3102
3103
3104/*
3105 * cplex_set_new_cols(CPH, AddedCols, NewObjCoeffs, NewLos, NewHis, NonZeros)
3106 *
3107 * Sets the following fields:
3108 *	lpd->mac	+AddedCold
3109 *	lpd->matnz	+NonZeros
3110 *	lpd->matxxx	get resized for AddedCols and NonZeros
3111 *	lpd->ctype	resized to AddedCols only and initialised to 'C'
3112 *	lpd->bdl/bdu	resized to AddedCols or maczs?? and filled
3113 *	lpd->objx	resized to AddedCols, if NewObjCoeffs given
3114 */
3115
3116int
3117p_cpx_set_new_cols(value vlp, type tlp, value vadded, type tadded, value vobjs, type tobjs, value vlos, type tlos, value vhis, type this, value vnzs, type tnzs)
3118{
3119    /* the column `buffer arrays' are needed by CPLEX and Xpress's interface
3120       to pass information for the columns. Except for ctype
3121       and possibly bdl, bdu, we simpy pass
3122       default values to the solver on setup/adding columns
3123    */
3124    lp_desc *lpd;
3125    int i;
3126
3127    LpDescOnly(vlp, tlp, lpd);
3128    Check_Integer(tadded);
3129    Check_Integer(tnzs);
3130    if (vadded.nint == 0) { Succeed; } /* no added columns, return now! */
3131
3132    lpd->mac += vadded.nint;
3133    lpd->matnz = vnzs.nint;
3134    TryFree(lpd->matind);
3135    TryFree(lpd->matval);
3136    if (vnzs.nint > 0)
3137    {
3138	lpd->matind = (int *) Malloc((size_t) vnzs.nint*sizeof(int));
3139	lpd->matval = (double *) Malloc((size_t) vnzs.nint*sizeof(double));
3140	Log1(lpd->matind = (int *) malloc((size_t) %d*sizeof(int)), vnzs.nint);
3141	Log1(lpd->matval = (double *) Malloc((size_t) %d*sizeof(double)), vnzs.nint);
3142    }
3143    TryFree(lpd->ctype);
3144    TryFree(lpd->matbeg);
3145    TryFree(lpd->matcnt);
3146    lpd->ctype = (char *) Malloc((size_t) vadded.nint*sizeof(char));
3147    /* +1 for matbeg required by COIN */
3148    lpd->matbeg = (int *) Malloc((size_t) (vadded.nint+1)*sizeof(int));
3149    lpd->matcnt = (int *) Malloc((size_t) vadded.nint*sizeof(int));
3150    Log3({\n\
3151	lpd->ctype = (char *) Malloc((size_t) %d*sizeof(char));\n\
3152	lpd->matbeg = (int *) Malloc((size_t) %d*sizeof(int));\n\
3153	lpd->matcnt = (int *) Malloc((size_t) %d*sizeof(int));\n\
3154    }, vadded.nint, vadded.nint, vadded.nint);
3155
3156    for (i=0; i<vadded.nint; i++) lpd->ctype[i] = 'C';
3157
3158    /* treatment of bounds arrays lpd->bdl, lpd->bdu:
3159
3160       if columns with non-default bounds were added immediately previously
3161       the bounds arrays will have been freed in p_cpx_flush_new_rowcols so
3162       any existing bounds arrays contain default values and are length lpd->macsz
3163
3164       1) if vadded.nint > lpd->macsz any existing arrays are too small:
3165          a) if we have non-default bounds to apply we free any existing array
3166	     and Malloc a new one of correct size vadded.nint since Realloc may
3167	     have to copy the contents of the existing array and free it and we
3168	     need to overwrite the entries again anyway
3169	  b) if we have default bounds to apply we just expand the existing array
3170	     with Realloc and initialize the new positions
3171       2) otherwise  any existing arrays are big enough:
3172          a) if we have non-default bounds to apply we overwrite the necessary entries
3173	  b) if we have default bounds to apply we have nothing to do
3174
3175       if we have non-default bounds we set the appropriate bit of the lpd->dirtybdflag
3176       so that the arrays can be freed in p_cpx_flush_new_rowcols
3177    */
3178    if (vadded.nint > lpd->macsz) /* any existing bound arrays are too small */
3179    {
3180        if (IsList(tlos)) /* non-default lower bounds */
3181	{
3182	    /* since Realloc may copy and free and we need to overwrite entries
3183	       anyway it is probably better to free and Malloc */
3184	    TryFree(lpd->bdl);
3185	    lpd->bdl = (double *) Malloc((size_t) vadded.nint*sizeof(double));
3186	    Log1(lpd->bdl = (double *) malloc(%d*sizeof(double)), vadded.nint);
3187	    if (IsList(this)) /* non-default upper bounds */
3188	    {
3189	        /* both bounds arrays are non-default and will be freed
3190		   immediately after flushing: no need to increase lpd->macsz since
3191		   we will need to Malloc new bounds arrays of correct size next
3192		   time around anyway */
3193	        TryFree(lpd->bdu);
3194		lpd->bdu = (double *) Malloc((size_t) vadded.nint*sizeof(double));
3195		Log1(lpd->bdu = (double *) malloc(%d*sizeof(double)), vadded.nint);
3196		/* fill the bounds arrays with explicit bounds */
3197		for (i = 0; (IsList(tlos) && IsList(this)); ++i)
3198		{
3199		    pword *lhead = vlos.ptr;
3200		    pword *ltail = lhead + 1;
3201		    pword *hhead = vhis.ptr;
3202		    pword *htail = hhead + 1;
3203		    double lo, hi;
3204
3205		    Dereference_(lhead);
3206		    if (IsInteger(lhead->tag)) {
3207		      lo = (double) lhead->val.nint;
3208		    } else {
3209		      Check_Float(lhead->tag);
3210		      lo = Dbl(lhead->val);
3211		    }
3212		    lpd->bdl[i] = (lo < -CPX_INFBOUND ? -CPX_INFBOUND : lo);
3213		    Dereference_(hhead);
3214		    if (IsInteger(hhead->tag)) {
3215		      hi = (double) hhead->val.nint;
3216		    } else {
3217		      Check_Float(hhead->tag);
3218		      hi = Dbl(hhead->val);
3219		    }
3220		    lpd->bdu[i] = (hi > CPX_INFBOUND ? CPX_INFBOUND : hi);
3221		    Dereference_(ltail);
3222		    tlos = ltail->tag;
3223		    vlos = ltail->val;
3224		    Dereference_(htail);
3225		    this = htail->tag;
3226		    vhis = htail->val;
3227		}
3228		/* check that there are the right number of bds */
3229		if (i != vadded.nint) { Bip_Error(RANGE_ERROR) }
3230		/* mark both arrays as "dirty" */
3231		lpd->dirtybdflag |= 3;
3232	    }
3233	    else /* default upper bounds */
3234	    {
3235	        int newcsz;
3236
3237		newcsz = Max(vadded.nint, lpd->macsz+NEWCOL_INCR);
3238		if (lpd->bdu == NULL) /* upper bounds array freed */
3239		{
3240		    lpd->bdu = (double *) Malloc((size_t) newcsz*sizeof(double));
3241		    Log1(lpd->bdu = (double *) malloc(%d*sizeof(double)), newcsz);
3242
3243		    for (i=0; i<newcsz; i++)
3244		    {
3245		      lpd->bdu[i]  = CPX_INFBOUND;
3246		    }
3247		}
3248		else
3249		{
3250		    lpd->bdu = (double *) Realloc(lpd->bdu, (size_t) newcsz*sizeof(double));
3251		    Log1(lpd->bdu = (double *) realloc(%d*sizeof(double)), newcsz);
3252		    for (i=lpd->macsz; i<newcsz; i++)
3253		    {
3254		      lpd->bdu[i]  = CPX_INFBOUND;
3255		    }
3256		}
3257		lpd->macsz = newcsz;
3258		/* fill the lower bounds array with explicit bounds */
3259		for (i = 0; (IsList(tlos)); ++i)
3260		{
3261		    pword *lhead = vlos.ptr;
3262		    pword *ltail = lhead + 1;
3263		    double lo;
3264
3265		    Dereference_(lhead);
3266		    if (IsInteger(lhead->tag)) {
3267		      lo = (double) lhead->val.nint;
3268		    } else {
3269		      Check_Float(lhead->tag);
3270		      lo = Dbl(lhead->val);
3271		    }
3272		    lpd->bdl[i] = (lo < -CPX_INFBOUND ? -CPX_INFBOUND : lo);
3273		    Dereference_(ltail);
3274		    tlos = ltail->tag;
3275		    vlos = ltail->val;
3276		}
3277		/* check that there are the right number of bds */
3278		if (i != vadded.nint) { Bip_Error(RANGE_ERROR) }
3279		/* mark lower bounds array as "dirty" */
3280		lpd->dirtybdflag |= 1;
3281	    }
3282	}
3283	else if (IsList(this)) /* default lower bounds, non-default upper bounds */
3284	{
3285	    int newcsz;
3286
3287	    newcsz = Max(vadded.nint, lpd->macsz+NEWCOL_INCR);
3288	    if (lpd->bdl == NULL) /* lower bounds array freed */
3289	    {
3290	        lpd->bdl = (double *) Malloc((size_t) newcsz*sizeof(double));
3291		Log1(lpd->bdl = (double *) malloc(%d*sizeof(double)), newcsz);
3292		for (i=0; i<newcsz; i++)
3293		{
3294		  lpd->bdl[i]  = -CPX_INFBOUND;
3295		}
3296	    }
3297	    else
3298	    {
3299	        lpd->bdl = (double *) Realloc(lpd->bdl, (size_t) newcsz*sizeof(double));
3300		Log1(lpd->bdl = (double *) realloc(%d*sizeof(double)), newcsz);
3301		for (i=lpd->macsz; i<newcsz; i++)
3302		{
3303		  lpd->bdl[i]  = -CPX_INFBOUND;
3304		}
3305	    }
3306	    lpd->macsz = newcsz;
3307	    TryFree(lpd->bdu);
3308	    lpd->bdu = (double *) Malloc((size_t) vadded.nint*sizeof(double));
3309	    Log1(lpd->bdu = (double *) malloc(%d*sizeof(double)), vadded.nint);
3310	    /* fill the upper bounds array with explicit bounds */
3311	    for (i = 0; (IsList(this)); ++i)
3312	    {
3313	        pword *hhead = vhis.ptr;
3314		pword *htail = hhead + 1;
3315		double hi;
3316
3317		Dereference_(hhead);
3318		if (IsInteger(hhead->tag)) {
3319		  hi = (double) hhead->val.nint;
3320		} else {
3321		  Check_Float(hhead->tag);
3322		  hi = Dbl(hhead->val);
3323		}
3324		lpd->bdu[i] = (hi > CPX_INFBOUND ? CPX_INFBOUND : hi);
3325		Dereference_(htail);
3326		this = htail->tag;
3327		vhis = htail->val;
3328	    }
3329	    /* check that there are the right number of bds */
3330	    if (i != vadded.nint) { Bip_Error(RANGE_ERROR) }
3331	    /* mark upper bounds array as "dirty" */
3332	    lpd->dirtybdflag |= 2;
3333	}
3334	else /* default bounds */
3335	{
3336	    int newcsz;
3337
3338	    newcsz = Max(vadded.nint, lpd->macsz+NEWCOL_INCR);
3339	    if (lpd->bdl == NULL) /* lower bounds array freed */
3340	    {
3341	        lpd->bdl = (double *) Malloc((size_t) newcsz*sizeof(double));
3342		Log1(lpd->bdl = (double *) malloc(%d*sizeof(double)), newcsz);
3343		for (i=0; i<newcsz; i++)
3344		{
3345		  lpd->bdl[i]  = -CPX_INFBOUND;
3346		}
3347	    }
3348	    else
3349	    {
3350	        lpd->bdl = (double *) Realloc(lpd->bdl, (size_t) newcsz*sizeof(double));
3351		Log1(lpd->bdl = (double *) realloc(%d*sizeof(double)), newcsz);
3352		for (i=lpd->macsz; i<newcsz; i++)
3353		{
3354		  lpd->bdl[i]  = -CPX_INFBOUND;
3355		}
3356	    }
3357	    if (lpd->bdu == NULL) /* upper bounds array freed */
3358	    {
3359	        lpd->bdu = (double *) Malloc((size_t) newcsz*sizeof(double));
3360		Log1(lpd->bdu = (double *) malloc(%d*sizeof(double)), newcsz);
3361		for (i=0; i<newcsz; i++)
3362		{
3363		  lpd->bdu[i]  = CPX_INFBOUND;
3364		}
3365	    }
3366	    else
3367	    {
3368	        lpd->bdu = (double *) Realloc(lpd->bdu, (size_t) newcsz*sizeof(double));
3369		Log1(lpd->bdu = (double *) malloc(%d*sizeof(double)), newcsz);
3370		for (i=lpd->macsz; i<newcsz; i++)
3371		{
3372		  lpd->bdu[i]  = CPX_INFBOUND;
3373		}
3374	    }
3375	    lpd->macsz = newcsz;
3376	}
3377    }
3378    else /* any existing bound arrays are big enough */
3379    {
3380        if (IsList(tlos)) /* non-default lower bounds */
3381	{
3382	    if (lpd->bdl == NULL)
3383	    {
3384		lpd->bdl = (double *) Malloc((size_t) vadded.nint*sizeof(double));
3385		Log1(lpd->bdl = (double *) malloc(%d*sizeof(double)), vadded.nint);
3386	    }
3387	    if (IsList(this)) /* non-default upper bounds */
3388	    {
3389	        if (lpd->bdu == NULL)
3390                {
3391		    lpd->bdu = (double *) Malloc((size_t) vadded.nint*sizeof(double));
3392		    Log1(lpd->bdu = (double *) malloc(%d*sizeof(double)), vadded.nint);
3393		}
3394		/* fill the bounds arrays with explicit bounds */
3395		for (i = 0; (IsList(tlos) && IsList(this)); ++i)
3396		{
3397		    pword *lhead = vlos.ptr;
3398		    pword *ltail = lhead + 1;
3399		    pword *hhead = vhis.ptr;
3400		    pword *htail = hhead + 1;
3401		    double lo, hi;
3402
3403		    Dereference_(lhead);
3404		    if (IsInteger(lhead->tag)) {
3405		      lo = (double) lhead->val.nint;
3406		    } else {
3407		      Check_Float(lhead->tag);
3408		      lo = Dbl(lhead->val);
3409		    }
3410		    lpd->bdl[i] = (lo < -CPX_INFBOUND ? -CPX_INFBOUND : lo);
3411		    Dereference_(hhead);
3412		    if (IsInteger(hhead->tag)) {
3413		      hi = (double) hhead->val.nint;
3414		    } else {
3415		      Check_Float(hhead->tag);
3416		      hi = Dbl(hhead->val);
3417		    }
3418		    lpd->bdu[i] = (hi > CPX_INFBOUND ? CPX_INFBOUND : hi);
3419		    Dereference_(ltail);
3420		    tlos = ltail->tag;
3421		    vlos = ltail->val;
3422		    Dereference_(htail);
3423		    this = htail->tag;
3424		    vhis = htail->val;
3425		}
3426		/* check that there are the right number of bds */
3427		if (i != vadded.nint) { Bip_Error(RANGE_ERROR) }
3428		/* mark both arrays as "dirty" */
3429		lpd->dirtybdflag |= 3;
3430	    }
3431	    else /* default upper bounds */
3432	    {
3433	        if (lpd->bdu == NULL) /* upper bounds array freed */
3434		{
3435		    lpd->bdu = (double *) Malloc((size_t) lpd->macsz*sizeof(double));
3436		    Log1(lpd->bdu = (double *) malloc(%d*sizeof(double)), lpd->macsz);
3437		    for (i=0; i<lpd->macsz; i++)
3438		    {
3439		      lpd->bdu[i]  = CPX_INFBOUND;
3440		    }
3441		}
3442		/* fill the lower bounds array with explicit bounds */
3443		for (i = 0; (IsList(tlos)); ++i)
3444		{
3445		    pword *lhead = vlos.ptr;
3446		    pword *ltail = lhead + 1;
3447		    double lo;
3448
3449		    Dereference_(lhead);
3450		    if (IsInteger(lhead->tag)) {
3451		      lo = (double) lhead->val.nint;
3452		    } else {
3453		      Check_Float(lhead->tag);
3454		      lo = Dbl(lhead->val);
3455		    }
3456		    lpd->bdl[i] = (lo < -CPX_INFBOUND ? -CPX_INFBOUND : lo);
3457		    Dereference_(ltail);
3458		    tlos = ltail->tag;
3459		    vlos = ltail->val;
3460		}
3461		/* check that there are the right number of bds */
3462		if (i != vadded.nint) { Bip_Error(RANGE_ERROR) }
3463		/* mark lower bounds array as "dirty" */
3464		lpd->dirtybdflag |= 1;
3465	    }
3466	}
3467	else if (IsList(this)) /* default lower bounds, non-default upper bounds */
3468	{
3469	    if (lpd->bdl == NULL) /* lower bounds array freed */
3470	    {
3471	        lpd->bdl = (double *) Malloc((size_t) lpd->macsz*sizeof(double));
3472		Log1(lpd->bdl = (double *) malloc(%d*sizeof(double)), lpd->macsz);
3473		for (i=0; i<lpd->macsz; i++)
3474		{
3475		  lpd->bdl[i]  = -CPX_INFBOUND;
3476		}
3477	    }
3478	    /* fill the upper bounds array with explicit bounds */
3479	    for (i = 0; (IsList(this)); ++i)
3480	    {
3481	        pword *hhead = vhis.ptr;
3482		pword *htail = hhead + 1;
3483		double hi;
3484
3485		Dereference_(hhead);
3486		if (IsInteger(hhead->tag)) {
3487		  hi = (double) hhead->val.nint;
3488		} else {
3489		  Check_Float(hhead->tag);
3490		  hi = Dbl(hhead->val);
3491		}
3492		lpd->bdu[i] = (hi > CPX_INFBOUND ? CPX_INFBOUND : hi);
3493		Dereference_(htail);
3494		this = htail->tag;
3495		vhis = htail->val;
3496	    }
3497	    /* check that there are the right number of bds */
3498	    if (i != vadded.nint) { Bip_Error(RANGE_ERROR) }
3499	    /* mark upper bounds array as "dirty" */
3500	    lpd->dirtybdflag |= 2;
3501	}
3502	else /* default bounds */
3503	{
3504	    if (lpd->bdl == NULL) /* lower bounds array freed */
3505	    {
3506	        lpd->bdl = (double *) Malloc((size_t) lpd->macsz*sizeof(double));
3507		Log1(lpd->bdl = (double *) malloc(%d*sizeof(double)), lpd->macsz);
3508		for (i=0; i<lpd->macsz; i++)
3509		{
3510		  lpd->bdl[i]  = -CPX_INFBOUND;
3511		}
3512	    }
3513	    if (lpd->bdu == NULL) /* upper bounds array freed */
3514	    {
3515	        lpd->bdu = (double *) Malloc((size_t) lpd->macsz*sizeof(double));
3516		Log1(lpd->bdu = (double *) malloc(%d*sizeof(double)), lpd->macsz);
3517		for (i=0; i<lpd->macsz; i++)
3518		{
3519		  lpd->bdu[i]  = CPX_INFBOUND;
3520		}
3521	    }
3522	}
3523    }
3524
3525    /* fill the objective coefficients array if specified */
3526    if (IsList(tobjs))
3527    {
3528
3529	if (IsList(tobjs)) /* only if there are objective coefficients */
3530	{
3531	    TryFree(lpd->objx);
3532	    Log1(lpd->objx = (double *) malloc((size_t) %d*sizeof(double)), vadded.nint);
3533	    lpd->objx = (double *) Malloc((size_t) vadded.nint*sizeof(double));
3534	}
3535
3536	for (i = 0; IsList(tobjs); ++i)
3537	{
3538	    pword *head = vobjs.ptr;
3539	    pword *tail = head + 1;
3540	    double coeff;
3541
3542	    Dereference_(head);
3543	    if (IsInteger(head->tag)) {
3544		coeff = (double) head->val.nint;
3545	    } else {
3546		Check_Float(head->tag);
3547		coeff = Dbl(head->val);
3548		Check_Constant_Range(coeff);
3549	    }
3550	    lpd->objx[i] = coeff;
3551	    Dereference_(tail);
3552	    tobjs = tail->tag;
3553	    vobjs = tail->val;
3554	}
3555	/* check that there are the right number of objs */
3556	if (i != vadded.nint) { Bip_Error(RANGE_ERROR) }
3557    }
3558
3559    Succeed;
3560}
3561
3562
3563int
3564p_cpx_init_type(value vlp, type tlp, value vj, type tj, value vtype, type ttype)
3565{
3566    lp_desc *lpd;
3567    int j;
3568
3569    LpDescOnly(vlp, tlp, lpd);
3570    Check_Integer(tj);
3571    j = vj.nint;
3572
3573    if (j >= lpd->mac) { Bip_Error(RANGE_ERROR); }
3574    if (j >= lpd->macadded) j -= lpd->macadded; /* added col */
3575    lpd->ctype[j] = (char) vtype.nint;
3576    if (vtype.nint != 'C')
3577    {
3578	switch (lpd->prob_type)
3579	{
3580	case PROBLEM_LP:
3581	    lpd->prob_type = PROBLEM_MIP;
3582	    break;
3583#ifdef HAS_MIQP
3584	case PROBLEM_QP:
3585	    lpd->prob_type = PROBLEM_MIQP;
3586	    break;
3587	case PROBLEM_MIQP:
3588#endif
3589	case PROBLEM_MIP:
3590	    break;
3591	default:
3592	    Fprintf(Current_Error, "Eplex error: this solver does not support solving of quadratic MIP problems.\n");
3593	    ec_flush(Current_Error);
3594	    Bip_Error(EC_EXTERNAL_ERROR);
3595	    break;
3596	}
3597
3598#ifdef XPRESS
3599    	++lpd->ngents;
3600#endif
3601    }
3602    Succeed;
3603}
3604
3605
3606/* Set bounds for new variables in buffer arrays.
3607 * Used for initial setup and for adding variables.
3608 */
3609int
3610p_cpx_init_bound(value vlp, type tlp, value vj, type tj, value vwhich, type twhich, value vval, type tval)
3611{
3612    lp_desc *lpd;
3613    int j;
3614    double bd;
3615
3616    LpDescOnly(vlp, tlp, lpd);
3617    Check_Integer(tj);
3618    Check_Atom(twhich);
3619    Check_Float(tval);
3620    bd = Dbl(vval);
3621
3622    j = vj.nint;
3623    if (j >= lpd->mac) { Bip_Error(RANGE_ERROR); }
3624    if (j >= lpd->macadded) j -= lpd->macadded; /* added col */
3625
3626    if (vwhich.did == d_le) {		/* upper bound */
3627	if (bd < lpd->bdl[j]) { Fail; }
3628	if (bd < lpd->bdu[j]) { lpd->bdu[j] = bd; lpd->dirtybdflag |= 2; }
3629
3630    } else if (vwhich.did == d_ge) {	/* lower bound */
3631	if (bd > lpd->bdu[j]) { Fail; }
3632	if (bd > lpd->bdl[j]) { lpd->bdl[j] = bd; lpd->dirtybdflag |= 1; }
3633
3634    } else if (vwhich.did == d_eq) {	/* both bounds */
3635	if (bd < lpd->bdl[j]  ||  lpd->bdu[j] < bd) { Fail; }
3636	lpd->bdl[j] = lpd->bdu[j] = bd;
3637	lpd->dirtybdflag |= 3;
3638
3639    } else {
3640	Bip_Error(RANGE_ERROR);
3641    }
3642    Succeed;
3643}
3644
3645
3646/*----------------------------------------------------------------------*
3647 * Retrieving variable type and bounds
3648 *----------------------------------------------------------------------*/
3649
3650
3651int
3652p_cpx_get_col_type(value vlp, type tlp, value vj, type tj, value vtype, type ttype)
3653{
3654    lp_desc *lpd;
3655    char ctype[1];
3656
3657    LpDesc(vlp, tlp, lpd);
3658    Check_Integer(tj);
3659    if (vj.nint >= lpd->mac || vj.nint < 0) { Bip_Error(RANGE_ERROR); }
3660    SetPreSolve(lpd->presolve);
3661    if (IsMIPProb(lpd->prob_type))
3662    {
3663	CPXupdatemodel(lpd->lp);	/* before CPXget... */
3664	if (CPXgetctype(cpx_env, lpd->lp, ctype, (int) vj.nint, (int) vj.nint))
3665	{
3666	    Bip_Error(EC_EXTERNAL_ERROR);
3667	}
3668    }
3669    else
3670    {
3671	ctype[0] = 'C';
3672    }
3673    Return_Unify_Integer(vtype, ttype, (int) ctype[0]);
3674}
3675
3676/*----------------------------------------------------------------------*
3677 * Updating objective
3678 *----------------------------------------------------------------------*/
3679
3680static void
3681_grow_cb_arrays(lp_desc * lpd, int with_index2)
3682{
3683    if (lpd->cb_sz == 0)
3684    {
3685#ifdef LOG_CALLS
3686    Fprintf(log_output_, "\n\
3687	lpd->cb_sz = %d;\n\
3688	lpd->cb_index = (int *) malloc(lpd->cb_sz*sizeof(int));\n\
3689	lpd->cb_value = (double *) malloc(lpd->cb_sz*sizeof(double));",
3690	    NEWBD_INCR);
3691    if (with_index2)
3692	Fprintf(log_output_, "\n\
3693	    lpd->cb_index2 = (int *) malloc(lpd->cb_sz*sizeof(int));");
3694    ec_flush(log_output_);
3695#endif
3696	lpd->cb_sz = NEWBD_INCR;
3697	lpd->cb_index = (int *) Malloc(NEWBD_INCR*sizeof(int));
3698	if (with_index2)
3699	    lpd->cb_index2 = (int *) Malloc(NEWBD_INCR*sizeof(int));
3700	lpd->cb_value = (double *) Malloc(NEWBD_INCR*sizeof(double));
3701    }
3702    else
3703    {
3704#ifdef LOG_CALLS
3705    Fprintf(log_output_, "\n\
3706	lpd->cb_sz += %d;\n\
3707	lpd->cb_index = (int *) realloc(lpd->cb_index, lpd->cb_sz*sizeof(int));\n\
3708	lpd->cb_value = (double *) realloc(lpd->cb_value, lpd->cb_sz*sizeof(double));",
3709	    NEWBD_INCR);
3710    if (with_index2)
3711    {
3712	Fprintf(log_output_, "\n\
3713	    lpd->cb_index2 = lpd->cb_index2\n\
3714	    	? (int *) realloc(lpd->cb_index2, lpd->cb_sz*sizeof(int))\n\
3715		: (int *) malloc(lpd->cb_sz*sizeof(int));");
3716    }
3717    else if (lpd->cb_index2)
3718    {
3719	Fprintf(log_output_, "\n\
3720		free(lpd->cb_index2);\n\
3721		lpd->cb_index2 = 0;");
3722    }
3723    ec_flush(log_output_);
3724#endif
3725	lpd->cb_sz += NEWBD_INCR;
3726	lpd->cb_index = (int *) Realloc(lpd->cb_index, lpd->cb_sz*sizeof(int));
3727	lpd->cb_value = (double *) Realloc(lpd->cb_value, lpd->cb_sz*sizeof(double));
3728	if (with_index2)
3729	{
3730	    lpd->cb_index2 = lpd->cb_index2
3731	    	? (int *) Realloc(lpd->cb_index2, lpd->cb_sz*sizeof(int))
3732		: (int *) Malloc(lpd->cb_sz*sizeof(int));
3733	}
3734	else if (lpd->cb_index2)
3735	{
3736	    Free(lpd->cb_index2);
3737	    lpd->cb_index2 = 0;
3738	}
3739    }
3740}
3741
3742
3743int
3744p_cpx_new_obj_coeff(value vlp, type tlp, value vj, type tj, value vcoeff, type tcoeff)
3745{
3746    lp_desc *lpd;
3747    double coeff;
3748    int i;
3749
3750    LpDescOnly(vlp, tlp, lpd);
3751    Check_Integer(tj);
3752    if (vj.nint >= lpd->mac)
3753       { Bip_Error(RANGE_ERROR); }
3754
3755    if (IsInteger(tcoeff)) {
3756	coeff = (double) vcoeff.nint;
3757    } else {
3758	Check_Float(tcoeff);
3759	coeff = Dbl(vcoeff);
3760	Check_Constant_Range(coeff);
3761    }
3762    if (lpd->cb_cnt >= lpd->cb_sz)	/* grow arrays if necessary */
3763    {
3764	_grow_cb_arrays(lpd, 0);
3765    }
3766    i = lpd->cb_cnt++;
3767    lpd->cb_index[i] = vj.nint;
3768    lpd->cb_value[i] = coeff;
3769    Succeed;
3770}
3771
3772int
3773p_cpx_flush_obj(value vlp, type tlp)
3774{
3775    lp_desc *lpd;
3776    LpDesc(vlp, tlp, lpd);
3777    if (lpd->cb_cnt == 0)
3778    {
3779    	Succeed;
3780    }
3781    SetPreSolve(lpd->presolve);
3782    Mark_Copy_As_Modified(lpd);
3783#ifdef LOG_CALLS
3784    {
3785	int i;
3786	for (i=0; i<lpd->cb_cnt; ++i)
3787	{
3788	Fprintf(log_output_, "\n\
3789	    lpd->cb_index[%d] = %d;\n\
3790	    lpd->cb_value[%d] = %.15e;",
3791	    	i, lpd->cb_index[i], i, lpd->cb_value[i]);
3792	}
3793	Log1(CPXchgobj(cpx_env, lpd->lp, %d, lpd->cb_index, lpd->cb_value), lpd->cb_cnt);
3794    }
3795#endif
3796    if (CPXchgobj(cpx_env, lpd->lp, lpd->cb_cnt, lpd->cb_index, lpd->cb_value))
3797    {
3798	Bip_Error(EC_EXTERNAL_ERROR);
3799    }
3800    lpd->cb_cnt = 0;
3801    Succeed;
3802}
3803
3804int
3805p_cpx_new_qobj_coeff(value vlp, type tlp,
3806		     value vi, type ti,
3807		     value vj, type tj,
3808		     value vcoeff, type tcoeff)
3809{
3810    lp_desc *lpd;
3811    double coeff;
3812
3813    Check_Integer(ti);
3814    Check_Integer(tj);
3815    LpDesc(vlp, tlp, lpd);
3816    if (vj.nint >= lpd->mac || vi.nint >= lpd->mac)
3817       { Bip_Error(RANGE_ERROR); }
3818
3819    if (IsInteger(tcoeff)) {
3820	coeff = (double) vcoeff.nint;
3821    } else {
3822	Check_Float(tcoeff);
3823	coeff = Dbl(vcoeff);
3824	Check_Constant_Range(coeff);
3825    }
3826    if (vi.nint==vj.nint)
3827    	coeff *= 2;
3828    SetPreSolve(lpd->presolve);
3829    Log3(CPXchgqpcoef(cpx_env, lpd->lp, %d,%d,%.15e), vi.nint, vj.nint, coeff);
3830    if (CPXchgqpcoef(cpx_env, lpd->lp, vi.nint, vj.nint, coeff))
3831    {
3832	Bip_Error(EC_EXTERNAL_ERROR);
3833    }
3834    Succeed;
3835}
3836
3837
3838int
3839p_cpx_change_obj_sense(value vlp, type tlp, value vsense, type tsense)
3840{
3841    lp_desc *lpd;
3842
3843    Check_Integer(tsense);
3844    LpDesc(vlp, tlp, lpd);
3845
3846    SetPreSolve(lpd->presolve);
3847    lpd->sense = vsense.nint;
3848    CPXchgobjsen(cpx_env, lpd->lp, vsense.nint);
3849#ifdef SOLVE_MIP_COPY
3850    if (lpd->copystatus != XP_COPYOFF) Mark_Copy_As_Modified(lpd);
3851#endif
3852    Succeed;
3853}
3854
3855/*----------------------------------------------------------------------*
3856 * Initial matrix setup
3857 *----------------------------------------------------------------------*/
3858
3859int
3860p_cpx_set_matbeg(value vlp, type tlp,
3861		 value vj, type tj,
3862		 value vk, type tk,
3863		 value vk1, type tk1)
3864{
3865    lp_desc *lpd;
3866    int j;
3867
3868    Check_Integer(tk);
3869    Check_Integer(tk1);
3870    Check_Integer(tj);
3871    j = vj.nint;
3872    LpDescOnly(vlp, tlp, lpd);
3873
3874    if (j >= lpd->mac || j < 0) { Bip_Error(RANGE_ERROR); }
3875    if (j >= lpd->macadded) j -= lpd->macadded; /* added col */
3876    lpd->matbeg[j] = vk.nint;
3877    lpd->matcnt[j] = vk1.nint - vk.nint;
3878    Succeed;
3879}
3880
3881int
3882p_cpx_set_matval(value vlp, type tlp,
3883		 value vk, type tk,
3884		 value vi, type ti,
3885		 value vval, type tval)
3886{
3887    lp_desc *lpd;
3888    Check_Integer(tk);
3889    Check_Integer(ti);
3890    Check_Number(tval);
3891    LpDescOnly(vlp, tlp, lpd);
3892
3893    if (vk.nint >= lpd->matnz || vk.nint < 0 ||
3894	vi.nint >= lpd->mar || vi.nint < SOLVER_MAT_BASE)
3895    	{ Bip_Error(RANGE_ERROR); }
3896    lpd->matind[vk.nint] = vi.nint;
3897    lpd->matval[vk.nint] = DoubleVal(vval, tval);
3898    Check_Constant_Range(lpd->matval[vk.nint]);
3899    Succeed;
3900}
3901
3902int
3903p_cpx_loadprob(value vlp, type tlp)
3904{
3905    int err;
3906    lp_desc *lpd;
3907    LpDescOnly(vlp, tlp, lpd);
3908
3909    SetPreSolve(lpd->presolve);
3910    lpd->start_mac = lpd->mac;
3911
3912    if (lpd->nsos) {
3913	if (lpd->prob_type == PROBLEM_QP)
3914	    lpd->prob_type = PROBLEM_MIQP;
3915	else
3916	    lpd->prob_type = PROBLEM_MIP;
3917    }
3918
3919#ifndef HAS_MIQP
3920    if (lpd->prob_type == PROBLEM_MIQP)
3921    {
3922	Fprintf(Current_Error, "Eplex error: this solver does not support solving of quadratic MIP problems.\n");
3923	ec_flush(Current_Error);
3924	Bip_Error(UNIMPLEMENTED);
3925    }
3926#endif
3927#ifdef LOG_CALLS
3928    {
3929    int i;
3930# ifndef DUMPMAT
3931#  ifdef XPRESS
3932    Fprintf(log_output_, "\n\
3933	lpd->probname = (char *) malloc(16*sizeof(char));\n\
3934	strcpy(lpd->probname, \"eclipse\");"
3935    );
3936#  endif
3937#  ifdef CPLEX
3938    Log1(lpd->sense = %d, lpd->sense);
3939#  endif
3940    Fprintf(log_output_, "\n\
3941	lpd->sense = %d;\n\
3942    	lpd->macsz = %d;\n\
3943    	lpd->marsz = %d;\n\
3944    	lpd->matnz = %d;\n\
3945    	lpd->mac = %d;\n\
3946    	lpd->mar = %d;\n\
3947	lpd->rhsx = (double *) malloc(lpd->marsz * sizeof(double));\n\
3948	lpd->senx = (char *) malloc(lpd->marsz * sizeof(char));\n\
3949	lpd->matbeg = (int *) malloc((lpd->macsz+1) * sizeof(int));\n\
3950	lpd->matcnt = (int *) malloc(lpd->macsz * sizeof(int));\n\
3951	lpd->matind = (int *) malloc(lpd->matnz * sizeof(int));\n\
3952	lpd->matval = (double *) malloc(lpd->matnz * sizeof(double));\n\
3953	lpd->bdl = (double *) malloc(lpd->macsz * sizeof(double));\n\
3954	lpd->bdu = (double *) malloc(lpd->macsz * sizeof(double));\n\
3955	lpd->objx = (double *) malloc(lpd->macsz * sizeof(double));\n\
3956	lpd->ctype = (char *) malloc(lpd->macsz * sizeof(char));",
3957	lpd->sense,(lpd->macsz ? lpd->macsz : 1), (lpd->marsz ? lpd->marsz: 1), (lpd->matnz? lpd->matnz : 1),
3958	lpd->mac, lpd->mar);
3959
3960    for (i=0; i<lpd->mac; ++i)
3961    {
3962	Fprintf(log_output_, "\n\tlpd->objx[%d] = %.15e;", i, lpd->objx[i]);
3963	Fprintf(log_output_, "\n\tlpd->bdl[%d] = %.15e;", i, lpd->bdl[i]);
3964	Fprintf(log_output_, "\n\tlpd->bdu[%d] = %.15e;", i, lpd->bdu[i]);
3965	Fprintf(log_output_, "\n\tlpd->matbeg[%d] = %d;", i, lpd->matbeg[i]);
3966	Fprintf(log_output_, "\n\tlpd->matcnt[%d] = %d;", i, lpd->matcnt[i]);
3967    }
3968    for (i=0; i<lpd->mar; ++i)
3969    {
3970	Fprintf(log_output_, "\n\tlpd->rhsx[%d] = %.15e;", i, lpd->rhsx[i]);
3971	Fprintf(log_output_, "\n\tlpd->senx[%d] = '%c';", i, lpd->senx[i]);
3972    }
3973    for (i=0; i<lpd->matnz; ++i)
3974    {
3975	Fprintf(log_output_, "\n\tlpd->matind[%d] = %d;", i, lpd->matind[i]);
3976	Fprintf(log_output_, "\n\tlpd->matval[%d] = %.15e;", i, lpd->matval[i]);
3977    }
3978# else  /* DUMPMAT */
3979    dump_problem(lpd);
3980# endif
3981    }
3982#endif /* LOG_CALLS */
3983
3984    lpd->lp = NULL;
3985
3986#ifdef GUROBI
3987    if (cpx_loadprob(lpd))
3988    {
3989	Bip_Error(EC_EXTERNAL_ERROR);
3990    }
3991#endif
3992
3993#ifdef CPLEX
3994    CallN(lpd->lp = CPXcreateprob(cpx_env, &err, "eclipse"));
3995
3996    if (lpd->lp == NULL)
3997    {
3998	if (err == CPXERR_NO_ENVIRONMENT) {
3999	    Fprintf(Current_Error, "Unable to create problem in CPLEX: licensing problem?\n");
4000	    ec_flush(Current_Error);
4001	}
4002	Bip_Error(EC_EXTERNAL_ERROR);
4003    }
4004#endif
4005#ifdef COIN
4006    CallN(coin_create_prob(&(lpd->lp), cpx_env));
4007#endif
4008#if defined(CPLEX) || defined(COIN)
4009    CallN(lpd->lpcopy = lpd->lp);  /* no need for a copy in CPLEX */
4010    Call(err, CPXcopylp(cpx_env, lpd->lp, lpd->mac, lpd->mar,
4011    		lpd->sense, lpd->objx, lpd->rhsx, lpd->senx,
4012		lpd->matbeg, lpd->matcnt, lpd->matind, lpd->matval,
4013		lpd->bdl, lpd->bdu, NULL));
4014    if (err)
4015	{ Bip_Error(EC_EXTERNAL_ERROR); }
4016
4017    if (IsMIPProb(lpd->prob_type))
4018    {
4019# if defined(LOG_CALLS)
4020/* no need to log for XPRESS as ctype array not used directly */
4021	{ int i;
4022	    for (i=0; i<lpd->mac; ++i)
4023	    {
4024		Fprintf(log_output_, "\n\tlpd->ctype[%d] = '%c';", i, lpd->ctype[i]);
4025	    }
4026	}
4027# endif
4028	Call(err, CPXcopyctype(cpx_env, lpd->lp, lpd->ctype));
4029	if (err)
4030	    { Bip_Error(EC_EXTERNAL_ERROR); }
4031    }
4032
4033    if (lpd->nsos)
4034    {
4035#if defined(CPLEX) && CPLEX < 10
4036	if (CPXaddsos(cpx_env, lpd->lp, lpd->nsos, lpd->nsosnz, lpd->sostype,
4037		NULL, lpd->sosbeg, lpd->sosind, lpd->sosref))
4038#else
4039	if (CPXaddsos(cpx_env, lpd->lp, lpd->nsos, lpd->nsosnz, lpd->sostype,
4040		lpd->sosbeg, lpd->sosind, lpd->sosref, NULL))
4041#endif
4042	    { Bip_Error(EC_EXTERNAL_ERROR); }
4043	lpd->nsos_added = lpd->nsos;
4044    }
4045    if IsQPProb(lpd->prob_type)
4046    {
4047# ifdef HAS_QUADRATIC
4048	int i;
4049#  ifdef HAS_MIQP
4050	int ptype = (lpd->prob_type == PROBLEM_QP ? CPXPROB_QP : CPXPROB_MIQP);
4051#  else
4052	int ptype = CPXPROB_QP;
4053#  endif
4054	if (CPXgetprobtype(cpx_env, lpd->lp) != ptype)
4055	{
4056	    Call(err, CPXchgprobtype(cpx_env, lpd->lp, ptype));
4057	    if (err != 0)
4058	    { Bip_Error(EC_EXTERNAL_ERROR); }
4059	}
4060# ifdef CPLEX
4061	for (i=0; i<lpd->cb_cnt; ++i)
4062	{
4063	    Log3(CPXchgqpcoef(cpx_env, lpd->lp, %d, %d, %f),
4064		lpd->cb_index[i], lpd->cb_index2[i], lpd->cb_value[i]);
4065
4066	    if (CPXchgqpcoef(cpx_env, lpd->lp, lpd->cb_index[i],
4067				    lpd->cb_index2[i], lpd->cb_value[i]))
4068		{ Bip_Error(EC_EXTERNAL_ERROR); }
4069	}
4070	lpd->cb_cnt = 0;
4071#  elif defined(COIN)
4072	coin_set_qobj(lpd->lp, lpd->mac, lpd->cb_cnt, lpd->cb_index, lpd->cb_index2, lpd->cb_value);
4073#  endif
4074
4075# else /* !HAS_QUADRATIC */
4076	Fprintf(Current_Error, "Eplex error: Quadratic problems not supported for this solver!\n");
4077	ec_flush(Current_Error);
4078	Bip_Error(EC_EXTERNAL_ERROR);
4079# endif
4080    }
4081
4082#endif /* CPLEX || COIN */
4083#ifdef XPRESS
4084
4085   Call(err, XPRScreateprob(&lpd->lp));
4086   if (lpd->copystatus != XP_COPYOFF)
4087   {
4088       Mark_Copy_As_Modified(lpd);
4089       if (IsMIPProb(lpd->prob_type))
4090       {
4091	   if (err == 0) { Call(err, XPRScreateprob(&lpd->lpcopy)); }
4092       }
4093       else
4094	   CallN(lpd->lpcopy = lpd->lp);
4095   }
4096   else
4097       CallN(lpd->lpcopy = lpd->lp);
4098
4099    if (err && (err != 32/*student version*/))
4100    {
4101	char errmsg[256];
4102        XPRSgetlasterror(lpd->lp, errmsg);
4103	Fprintf(Current_Error, "Eplex error: %s\n", errmsg);
4104	ec_flush(Current_Error);
4105	Bip_Error(EC_EXTERNAL_ERROR);
4106    }
4107
4108    CallN(XPRScopycontrols(lpd->lp, cpx_env));
4109    /* Switch presolve off if requested, otherwise leave cpx_env's defaults */
4110    if (lpd->presolve == 0)
4111    {
4112	CallN(XPRSsetintcontrol(lpd->lp, XPRS_PRESOLVE, 0));
4113	CallN(XPRSsetintcontrol(lpd->lp, XPRS_MIPPRESOLVE, 0));
4114    }
4115
4116    /* this call back was done globally before version 13 */
4117    XPRSsetcbmessage(lpd->lp, eclipse_out, NULL);
4118
4119    /* the problem is now always loaded with XPRSloadglobal()
4120       as suggested by David Nielsen @ Dash 2004-09-28
4121       For a quadratic problem, the quadratic terms are then added
4122     */
4123    if (lpd->ngents)    /* has integers */
4124    {
4125	int i,j;
4126	/* don't know whether these arrays can be temporary */
4127	Log1({
4128        lpd->ngents = %i;\n\
4129	lpd->nsos = 0;\n\
4130	lpd->sossz = 0;\n\
4131	lpd->sostype = NULL;\n\
4132	lpd->sosbeg = NULL;\n\
4133	lpd->sosind = NULL;\n\
4134	lpd->sosref = NULL;\n\
4135	}, lpd->ngents);
4136	CallN(lpd->qgtype = (char *)Malloc(lpd->ngents*sizeof(char)));
4137	CallN(lpd->mgcols = (int *)Malloc(lpd->ngents*sizeof(int)));
4138	for (i=0,j=0; i < lpd->mac; i++)
4139	{
4140	    if (lpd->ctype[i] != 'C')
4141	    {
4142		Log4({\n\
4143		lpd->qgtype[%i] = '%c';\n\
4144		lpd->mgcols[%i] = %i;\n\
4145		}, j, lpd->ctype[i], j, i);
4146
4147		lpd->qgtype[j] = lpd->ctype[i];	/* 'B' or 'I' */
4148		lpd->mgcols[j++] = i;
4149	    }
4150	}
4151	/* correct the count, in case there were duplicates
4152	 * in the integer list (yes it happened...) */
4153	lpd->ngents = j;
4154	Log1(lpd->ngents = %i, j);
4155    }
4156    else
4157    {
4158	lpd->qgtype = NULL;
4159	lpd->mgcols = NULL;
4160    }
4161
4162    Call(err, XPRSloadglobal(lpd->lp, lpd->probname,
4163	             lpd->mac, lpd->mar, lpd->senx, lpd->rhsx, NULL, lpd->objx,
4164		     lpd->matbeg, lpd->matcnt, lpd->matind, lpd->matval,
4165		     lpd->bdl, lpd->bdu,
4166		     lpd->ngents, lpd->nsos, lpd->qgtype, lpd->mgcols, NULL,
4167		     lpd->sostype, lpd->sosbeg, lpd->sosind, lpd->sosref));
4168
4169    if (err) { Bip_Error(EC_EXTERNAL_ERROR); }
4170
4171    if (lpd->cb_cnt) /* has quadratic objective terms */
4172    {
4173# ifdef LOG_CALLS
4174	int i;
4175	Fprintf(log_output_, "\n\tlpd->cb_cnt = %d;", lpd->cb_cnt);
4176	for(i=0; i< lpd->cb_cnt; ++i)
4177	{
4178	    Fprintf(log_output_, "\n\tlpd->cb_index[%d] = %d;", i, lpd->cb_index[i]);
4179	    Fprintf(log_output_, "\n\tlpd->cb_index2[%d] = %d;", i, lpd->cb_index2[i]);
4180	    Fprintf(log_output_, "\n\tlpd->cb_value[%d] = %.15e;", i, lpd->cb_value[i]);
4181	}
4182# endif
4183	Call(err, XPRSchgmqobj(lpd->lp, lpd->cb_cnt,
4184	    lpd->cb_index, lpd->cb_index2, lpd->cb_value));
4185
4186	lpd->cb_cnt = 0;
4187	if (err) { Bip_Error(EC_EXTERNAL_ERROR); }
4188    }
4189
4190
4191
4192#endif /* XPRESS */
4193
4194    /* free our copy of the problem */
4195    Free(lpd->rhsx); lpd->rhsx = NULL;
4196    Free(lpd->senx); lpd->senx = NULL;
4197    Free(lpd->matbeg); lpd->matbeg = NULL;
4198    Free(lpd->matcnt); lpd->matcnt = NULL;
4199    Free(lpd->matind); lpd->matind = NULL;
4200    Free(lpd->matval); lpd->matval = NULL;
4201    Free(lpd->bdl); lpd->bdl = NULL;
4202    Free(lpd->bdu); lpd->bdu = NULL;
4203    Free(lpd->ctype); lpd->ctype = NULL;
4204    Free(lpd->objx); lpd->objx = NULL;
4205    lpd->matnz = 0;
4206    lpd->macsz = 0;
4207
4208    if (lpd->nsos)
4209    {
4210	lpd->nsos_added = lpd->nsos;
4211	Free(lpd->sosbeg); lpd->sosbeg = NULL;
4212	Free(lpd->sosind); lpd->sosind = NULL;
4213	Free(lpd->sosref); lpd->sosref = NULL;
4214	lpd->nsosnz = 0;
4215    }
4216
4217    lpd->macadded = lpd->mac;
4218    Succeed;
4219}
4220
4221/* add initial cutpool constraints to problem */
4222static int
4223_setup_initial_cp_constraints(lp_desc * lpd, int add_all, int * unadded_cntp,
4224			      int * cp_unadded, int * cp_map2)
4225{
4226    double * rhs, * rmatval;
4227    int * rmatbeg, * rmatind, i, offset, first = -1,
4228	cp_rcnt2 = 0, rcnt = 0;
4229    char * senx;
4230
4231
4232    rmatbeg = (int *)Malloc((lpd->cp_nr2+1)*sizeof(int));
4233
4234    for (i=0; i < lpd->cp_nr2; i++)
4235    {
4236	if (lpd->cp_active2[i] == 1)
4237	{
4238	    if (lpd->cp_initial_add2[i] == 1 || add_all)
4239	    {/* active, added initially (or add all active constraints) */
4240		if (first == -1)
4241		{
4242		    first = i;
4243		    offset = lpd->cp_rmatbeg2[first];
4244		}
4245		/* rmatbeg need to be offset from the start of array */
4246		rmatbeg[cp_rcnt2] = lpd->cp_rmatbeg2[i] - offset;
4247		cp_map2[i] = cp_rcnt2++;
4248		rcnt++;
4249		continue;
4250	    } else
4251	    { /* active, but not added initially */
4252		cp_map2[i] = CSTR_STATE_NOTADDED; /* not added yet */
4253		cp_unadded[(*unadded_cntp)++] = i;
4254	    }
4255	} else
4256	{/* not active */
4257	    cp_map2[i] = CSTR_STATE_INACTIVE; /* not active */
4258	}
4259
4260	if (rcnt > 0)
4261	{/* there are some rows to add... */
4262	    rhs = &lpd->cp_rhsx2[first];
4263	    rmatind = &lpd->cp_rmatind2[offset];
4264	    rmatval = &lpd->cp_rmatval2[offset];
4265	    senx   =  &lpd->cp_senx2[first];
4266	    CPXaddrows(cpx_env, lpd->lp, 0, rcnt,
4267		       (lpd->cp_rmatbeg2[i] - offset),
4268		       rhs, senx, rmatbeg, rmatind, rmatval, NULL, NULL);
4269	    rcnt = 0;
4270	    first = -1;
4271	}
4272
4273    }
4274
4275    if (rcnt > 0)
4276    {/* there are some rows to add... */
4277	rhs = &lpd->cp_rhsx2[first];
4278	rmatind = &lpd->cp_rmatind2[offset];
4279	rmatval = &lpd->cp_rmatval2[offset];
4280	senx   =  &lpd->cp_senx2[first];
4281	CPXaddrows(cpx_env, lpd->lp, 0, rcnt,
4282		   (lpd->cp_nnz2 - offset),
4283		   rhs, senx, rmatbeg, rmatind, rmatval, NULL, NULL);
4284	rcnt = 0;
4285	first = -1;
4286    }
4287
4288    lpd->mar += cp_rcnt2;
4289    lpd->cp_nact2 = cp_rcnt2;
4290    if (cp_rcnt2 > 0) { Mark_Copy_As_Modified(lpd); }
4291    Free(rmatbeg);
4292
4293    return 0;
4294}
4295
4296/*----------------------------------------------------------------------*
4297 * Read/write
4298 *----------------------------------------------------------------------*/
4299
4300int
4301p_cpx_lpwrite(value vfile, type tfile, value vformat, type tformat,
4302	      value vlp, type tlp)
4303{
4304    lp_desc *lpd;
4305    char has_cp = 0, *file, *format;
4306    int oldmar, res;
4307    Get_Name(vformat, tformat, format);
4308    Get_Name(vfile, tfile, file);
4309    LpDesc(vlp, tlp, lpd);
4310
4311    oldmar = lpd->mar;
4312
4313    SetPreSolve(lpd->presolve);
4314    if (lpd->cp_nr2 > 0)
4315    {
4316	int unadded_cnt = 0, * cp_unadded, * cp_map2;
4317
4318	cp_unadded = (int *)Malloc(lpd->cp_nr2*sizeof(int));
4319	cp_map2 = (int *)Malloc(lpd->cp_nr2*sizeof(int));
4320	if (_setup_initial_cp_constraints(lpd, 1, &unadded_cnt, cp_unadded, cp_map2) == -1)
4321	{
4322	    reset_rowcols(lpd, oldmar, lpd->mac);
4323	    Bip_Error(RANGE_ERROR);
4324	}
4325    }
4326    res = cpx_write(lpd, file, format);
4327    reset_rowcols(lpd, oldmar, lpd->mac);
4328    if (res == 0)
4329    {
4330	Succeed;
4331    } else
4332    {
4333	Bip_Error(EC_EXTERNAL_ERROR);
4334    }
4335}
4336
4337
4338int
4339p_cpx_lpread(value vfile, type tfile,
4340	     value vformat, type tformat,
4341	     value vpresolve, type tpresolve,
4342	     value vhandle, type thandle)
4343{
4344    lp_desc *lpd;
4345    char *file, *format;
4346    int res;
4347#if defined(CPLEX) || defined(XPRESS)
4348    if (!cpx_env)
4349    {
4350	Bip_Error(EC_LICENSE_ERROR);
4351    }
4352#endif
4353
4354    Get_Name(vfile, tfile, file);
4355    Get_Name(vformat, tformat, format);
4356    Check_Structure(thandle);
4357    Check_Integer(tpresolve);
4358
4359    CallN((lpd =  (lp_desc *) Malloc(sizeof(lp_desc))));
4360    /*CallN(_clr_lp_desc(lpd));*/
4361    CallN(memset(lpd, 0, sizeof(lp_desc)));
4362    /* the logged code needs to be hand-adjusted to put file in scope */
4363    Log1({char *file = "%s";}, file);
4364    lpd->presolve = vpresolve.nint;
4365
4366#ifdef USE_PROBLEM_ARRAY
4367    Log1(lpdmat[%d] = lpd, next_matno);
4368    current_matno = next_matno;
4369    lpd->matno = next_matno++;
4370#endif
4371
4372    if (cpx_read(lpd, file, format))
4373    {
4374	Bip_Error(EC_EXTERNAL_ERROR);
4375    }
4376
4377    lpd->start_mac = lpd->macadded = lpd->mac;
4378    lpd->descr_state = DESCR_LOADED;
4379
4380    {/* Return the cplex descriptor in argument HANDLE_CPH of the handle structure. */
4381	vhandle.ptr[HANDLE_CPH] = ec_handle(&lp_handle_tid, lpd);
4382	Make_Stamp(vhandle.ptr+HANDLE_STAMP); /* needed for other trail undos */
4383    }
4384    Succeed;
4385}
4386
4387
4388void
4389_create_result_darray(value vhandle, int pos, int size, pword* pw, double** start)
4390{
4391    pword *argp = &vhandle.ptr[pos];
4392
4393    Dereference_(argp);
4394    if (IsRef(argp->tag))
4395	*start = NULL;
4396    else
4397    {
4398	pw->tag.kernel = TSTRG;
4399	pw->val.ptr = _create_darray(size);
4400	*start = DArrayStart(pw->val.ptr);
4401    }
4402}
4403
4404void
4405_create_result_iarray(value vhandle, int pos, int size, pword *pw, int** start)
4406{
4407    pword *argp = &vhandle.ptr[pos];
4408
4409    Dereference_(argp);
4410    if (IsRef(argp->tag))
4411	*start = NULL;
4412    else
4413    {
4414	pw->tag.kernel = TSTRG;
4415	pw->val.ptr = _create_iarray(size);
4416	*start = IArrayStart(pw->val.ptr);
4417    }
4418}
4419
4420/*----------------------------------------------------------------------*
4421 * Accessing Infeasible information
4422 *----------------------------------------------------------------------*/
4423
4424#ifdef SUPPORT_IIS
4425static int
4426_get_iis(lp_desc * lpd, int * nrowsp, int * ncolsp, int * rowidxs, int * colidxs, char * colstats)
4427{
4428    int status;
4429    int i;
4430# ifdef CPLEX
4431    int * rowstatbuf, * colstatbuf;
4432
4433    rowstatbuf = Malloc(sizeof(int) * *nrowsp);
4434    colstatbuf = Malloc(sizeof(int) * *ncolsp);
4435
4436# endif
4437
4438    Get_Conflict(lpd->lp, status, rowidxs, rowstatbuf, nrowsp, colidxs, colstatbuf, ncolsp);
4439
4440# ifdef CPLEX
4441    switch (status)
4442    {
4443    case CPX_STAT_CONFLICT_MINIMAL:
4444	for(i=0;i<*ncolsp;i++)
4445	{
4446	    switch (colstatbuf[i])
4447	    {
4448# ifdef HAS_GENERAL_CONFLICT_REFINER
4449	    case CPX_CONFLICT_MEMBER:
4450		colstats[i] = 'b';
4451		break;
4452#  endif
4453	    case CPX_CONFLICT_LB:
4454		colstats[i] = 'l';
4455		break;
4456	    case CPX_CONFLICT_UB:
4457		colstats[i] = 'u';
4458		break;
4459	    default:
4460		colstats[i] = 'x';
4461		break;
4462	    }
4463	}
4464	Free(rowstatbuf);
4465	Free(colstatbuf);
4466	break;
4467    default:
4468	Free(rowstatbuf);
4469	Free(colstatbuf);
4470	return -1;
4471	break;
4472#  ifdef HAS_GENERAL_CONFLICT_REFINER
4473    case CPX_STAT_CONFLICT_FEASIBLE:
4474	/* An infeaible problem can return CONFLICT_FEASIBLE, with no conflict set, probably because
4475	   problem is near feasible.
4476	*/
4477	*nrowsp = 0;
4478	*ncolsp = 0;
4479	Free(rowstatbuf);
4480	Free(colstatbuf);
4481	return 1;
4482	break;
4483#  endif
4484    }
4485# endif
4486# ifdef XPRESS
4487    if (!status) {
4488	for(i=0;i<*ncolsp;i++) colstats[i] = 'x';
4489    } else {
4490	return -1;
4491    }
4492# endif
4493
4494    return 0;
4495}
4496
4497#endif /* SUPPORT_IIS */
4498
4499
4500/*----------------------------------------------------------------------*
4501 * Solve
4502 *----------------------------------------------------------------------*/
4503
4504
4505static int
4506_cstr_state(lp_desc * lpd, int row, char add_cp_cstr, double * sols, double tol)
4507{
4508    int lastarg, argpos;
4509    double lhs = 0.0, slack;
4510
4511    /* add_cp_cstr == 2 if unbounded result -- simply add all constraints
4512       in this case by returning violated state
4513    */
4514    if (add_cp_cstr == 2) return CSTR_STATE_VIOLATED;
4515    lastarg = (row < lpd->cp_nr2 - 1 ? lpd->cp_rmatbeg2[row+1] : lpd->cp_nnz2);
4516    for (argpos = lpd->cp_rmatbeg2[row] ; argpos < lastarg ; argpos++)
4517    {
4518	lhs += sols[lpd->cp_rmatind2[argpos]] * lpd->cp_rmatval2[argpos];
4519    }
4520    /* definition of slack for all row types except ranged rows, which we
4521       don't use
4522    */
4523    slack = lpd->cp_rhsx2[row] - lhs;
4524    switch (lpd->cp_senx2[row])
4525    {
4526    case SOLVER_SENSE_LE:
4527	return (slack<-tol ? CSTR_STATE_VIOLATED
4528		: (slack<=tol ? CSTR_STATE_BINDING : CSTR_STATE_SAT));
4529	break;
4530    case SOLVER_SENSE_GE:
4531	return  (slack > tol ? CSTR_STATE_VIOLATED
4532		 : (slack >= -tol ? CSTR_STATE_BINDING : CSTR_STATE_SAT));
4533	break;
4534    case SOLVER_SENSE_EQ:
4535	return (slack <= tol && slack >= -tol ? CSTR_STATE_BINDING
4536		: CSTR_STATE_VIOLATED);
4537	break;
4538    default:
4539	/* constraint type out of range */
4540	return CSTR_STATE_INVALID;
4541	break;
4542    }
4543}
4544
4545
4546/* cplex_optimise(Handle, SolveMethods, TimeOut, WriteBefore, MipStart,
4547		OutputPos, OptResult, OptStatus, WorstBound, BestBound)
4548
4549   optimises problem in Handle. Handle is needed to access the result
4550   arrays located in Handle by the OutputPos arguments.
4551   OptResult is the resulting status after the optimisation, OptStatus is
4552   the optimiser-dependent status returned by the optimiser. Worst and
4553   Best bounds are the bounds on the optimal solution determined by the
4554   solver.
4555
4556   Any solution state must be extracted from the optimiser in this procedure,
4557   as it modifies the problem by first adding the cutpool constraints before
4558   calling the optimiser and then removing them before exiting.
4559*/
4560
4561int
4562p_cpx_optimise(value vhandle, type thandle, value vmeths, type tmeths,
4563         value vtimeout, type ttimeout, value vdump, type tdump,
4564         value vmipstart, type tmipstart,
4565	 value vout, type tout, value vres, type tres, value vstat, type tstat,
4566	 value vworst, type tworst, value vbest, type tbest)
4567{
4568    lp_desc *lpd;
4569    int res, oldmar;
4570    int solspos, pispos, slackspos, djspos, cbasepos, rbasepos, cpcondmappos;
4571    int iis_rowspos, iis_colspos, iis_colstatspos;
4572    pword * pw, outsols, outpis, outslacks, outdjs, outcbase, outrbase;
4573    /* outdjs: when adding a solver, check to make sure that the reduced
4574       cost is of the same sign as what we defined (and what CPLEX and
4575       XPress uses). Reverse the signs before returning to ECLiPSe if required!
4576    */
4577    struct lp_meth meth;
4578    struct lp_sol sol;
4579    char has_cp = 0; /* has cutpool constraints added */
4580    char add_cp_cstr = 0;
4581    char *file = NULL;
4582    char *format = NULL;
4583    double bestbound, worstbound;
4584    int * cp_unadded, last_violated_idx, violated_cnt, unadded_cnt = 0;
4585    int * cp_map2;
4586    pword * old_tg;
4587
4588    /*********************************************************************
4589     *     Type Checking and Initialisation                              *
4590     *********************************************************************/
4591
4592    Prepare_Requests
4593
4594    Check_Structure(thandle);
4595    Check_Structure(tmeths);
4596    Check_Integer(tout);
4597    Check_Integer(tmipstart);
4598    Check_Number(ttimeout);
4599
4600    LpDesc(vhandle.ptr[HANDLE_CPH].val, vhandle.ptr[HANDLE_CPH].tag, lpd);
4601
4602    if (lpd->descr_state == DESCR_EMPTY)
4603    {
4604	Fprintf(Current_Error, "Eplex optimise: empty handle\n");
4605	(void) ec_flush(Current_Error);
4606	Bip_Error(EC_EXTERNAL_ERROR);
4607    }
4608
4609    /* m(Method,AuxMeth,NodeMeth,NodeAuxMeth) */
4610    pw = &vmeths.ptr[1];
4611    Dereference_(pw);
4612    Check_Integer(pw->tag);
4613    meth.meth = pw->val.nint;
4614    pw = &vmeths.ptr[2];
4615    Dereference_(pw);
4616    Check_Integer(pw->tag);
4617    meth.auxmeth = pw->val.nint;
4618    pw = &vmeths.ptr[3];
4619    Dereference_(pw);
4620    Check_Integer(pw->tag);
4621    meth.node_meth = pw->val.nint;
4622    pw = &vmeths.ptr[4];
4623    Dereference_(pw);
4624    Check_Integer(pw->tag);
4625    meth.node_auxmeth = pw->val.nint;
4626
4627    /* positions for output arrays in the Prolog handle */
4628    solspos = vout.nint + HANDLE_S_SOLS;
4629    pispos = vout.nint + HANDLE_S_PIS;
4630    slackspos = vout.nint + HANDLE_S_SLACKS;
4631    djspos = vout.nint + HANDLE_S_DJS;
4632    cbasepos = vout.nint + HANDLE_S_CBASE;
4633    rbasepos = vout.nint + HANDLE_S_RBASE;
4634    cpcondmappos = vout.nint + HANDLE_S_CPCM;
4635    iis_rowspos = vout.nint + HANDLE_S_IISR;
4636    iis_colspos = vout.nint + HANDLE_S_IISC;
4637    iis_colstatspos = vout.nint + HANDLE_S_IISCS;
4638
4639    if (IsStructure(tdump))
4640    { /* write_before_solve(Format,File) */
4641	pw = &vdump.ptr[1];
4642	Dereference_(pw);
4643	Get_Name(pw->val, pw->tag, format);
4644	pw = &vdump.ptr[2];
4645	Dereference_(pw);
4646	Get_Name(pw->val, pw->tag, file);
4647    }
4648
4649    SetPreSolve(lpd->presolve);
4650
4651    oldmar = lpd->mar;
4652    if (lpd->cp_nr2 > 0)
4653    {
4654
4655	pword map;
4656
4657	_create_result_iarray(vhandle, cpcondmappos, lpd->cp_nr2, &map, &cp_map2);
4658
4659	cp_unadded = (int *)Malloc(lpd->cp_nr2*sizeof(int));
4660
4661	if (_setup_initial_cp_constraints(lpd, 0, &unadded_cnt, cp_unadded, cp_map2) == -1)
4662	{
4663	    reset_rowcols(lpd, oldmar, lpd->mac);
4664	    Bip_Error(RANGE_ERROR);
4665	}
4666	if (cp_map2)
4667	    ec_assign(vhandle.ptr+cpcondmappos, map.val, map.tag);
4668	has_cp = 1;
4669    }
4670
4671    /* initialise the lp_sol structure
4672    */
4673    pw = &vhandle.ptr[solspos];
4674    Dereference_(pw);
4675    sol.oldmac = IsArray(pw->tag) ? DArraySize(pw->val.ptr) : 0;
4676    sol.oldsols = IsArray(pw->tag) ? DArrayStart(pw->val.ptr) : NULL;
4677
4678    _create_result_darray(vhandle, solspos, lpd->mac, &outsols, &sol.sols);
4679#ifdef HAS_LIMITED_MIP_RESULTS
4680    if (IsMIPProb(lpd->prob_type)) {
4681	sol.djs = NULL;
4682	sol.cbase = NULL;
4683    } else
4684#endif
4685    {/* djs, basis, pis are available for non-MIP problems only for CPLEX;
4686        for XPRESS, the returned values are for the optimal LP node
4687     */
4688	_create_result_darray(vhandle,   djspos, lpd->mac, &outdjs, &sol.djs);
4689	_create_result_iarray(vhandle, cbasepos, lpd->mac, &outcbase, &sol.cbase);
4690    }
4691
4692    /* allocate the row-wise arrays later as these may need to be expanded
4693       with the addition cutpool constraints
4694    */
4695    old_tg = TG;
4696    _create_result_darray(vhandle, slackspos, lpd->mar, &outslacks, &sol.slacks);
4697#ifdef HAS_LIMITED_MIP_RESULTS
4698    if (IsMIPProb(lpd->prob_type)) {
4699	sol.pis = NULL;
4700	sol.rbase = NULL;
4701    } else
4702#endif
4703    {
4704	_create_result_iarray(vhandle, rbasepos, lpd->mar, &outrbase, &sol.rbase);
4705	_create_result_darray(vhandle,   pispos, lpd->mar, &outpis, &sol.pis);
4706    }
4707    sol.mac = lpd->mac;
4708
4709    Log6({
4710	sol.sols   = (double *) malloc(sizeof(double) * %d);\n\
4711	sol.pis    = (double *) malloc(sizeof(double) * %d);\n\
4712	sol.slacks = (double *) malloc(sizeof(double) * %d);\n\
4713	sol.djs    = (double *) malloc(sizeof(double) * %d);\n\
4714	sol.cbase   = (int *) malloc(sizeof(int) * %d);
4715	sol.rbase   = (int *) malloc(sizeof(int) * %d);
4716    }, lpd->mac, lpd->mar, lpd->mar, lpd->mac, lpd->mac, lpd->mar);
4717
4718
4719    /* configure solver with timeout and solution methods
4720    */
4721    if (cpx_prepare_solve(lpd, &meth,
4722#ifdef XPRESS
4723	    &sol,
4724#endif
4725	    DoubleVal(vtimeout,ttimeout)))
4726    {
4727	reset_rowcols(lpd, oldmar, lpd->mac);
4728	Bip_Error(EC_EXTERNAL_ERROR);
4729    }
4730    meth.option_mipstart = vmipstart.nint;
4731
4732    /* if solution values are unavailable, and there are unadded cutpool
4733       constraints, abort with RANGE_ERROR as we can't check for violations
4734    */
4735    if (unadded_cnt > 0 && sol.sols == NULL)
4736    {
4737	reset_rowcols(lpd, oldmar, lpd->mac);
4738	Bip_Error(RANGE_ERROR);
4739    }
4740
4741    /*********************************************************************
4742     *     Solve Problem with the External Solver                        *
4743     *        depending on problem type, call the appropriate routine    *
4744     *        may solve multiple times with cutpool constraints          *
4745     *********************************************************************/
4746
4747    do
4748    {
4749	int i;
4750
4751	violated_cnt = 0;
4752	if (IsStructure(tdump))
4753	{
4754	    cpx_write(lpd, file, format); /* ignore any errors here */
4755	}
4756
4757	/* Run the solver
4758	*/
4759	if (cpx_solve(lpd, &meth, &sol, &bestbound, &worstbound))
4760	{
4761	    if (has_cp) reset_rowcols(lpd, oldmar, lpd->mac);
4762	    Bip_Error(EC_EXTERNAL_ERROR);
4763	}
4764
4765#ifdef LOG_CALLS
4766	Fprintf(log_output_, "\n}\nvoid step_%d() {\n", log_ctr++);
4767	ec_flush(log_output_);
4768	current_matno = -1; /* no current mat, exited from procedure */
4769#endif
4770
4771	/*********************************************************************
4772	 *     Get Result from External Solver                               *
4773	 *       Get the result for the optimisation from the external       *
4774	 *       solver if there is one                                      *
4775	 *********************************************************************/
4776
4777	switch (lpd->descr_state)
4778	{
4779	case DESCR_SOLVED_SOL:
4780	case DESCR_ABORTED_SOL:
4781	    add_cp_cstr = 1;
4782	    if (cpx_get_soln_state(lpd, &sol))
4783	    {
4784		if (has_cp) reset_rowcols(lpd, oldmar, lpd->mac);
4785		Bip_Error(EC_EXTERNAL_ERROR);
4786	    }
4787	    break;
4788
4789	case DESCR_SOLVED_NOSOL:
4790	    add_cp_cstr = 0;
4791	    /* no solution; state always fail */
4792#ifdef SUPPORT_IIS
4793	    {
4794		pword *argp = &vhandle.ptr[iis_rowspos];
4795
4796		Dereference_(argp);
4797
4798		if (!IsRef(argp->tag))
4799		{
4800		    int iis_nrows, iis_ncols;
4801		    int err;
4802		    pword * old_tg1;
4803
4804		    pword iis_rowidxs, iis_colidxs, iis_colstats;
4805
4806		    Find_Conflict(err, lpd->lp, iis_nrows, iis_ncols);
4807		    if (err)
4808		    {/* we can't simply abort here if an error occurs, just create dummy arrays
4809                        and do not proceed to try to get the IIS */
4810			iis_nrows = 0;
4811			iis_ncols = 0;
4812		    }
4813		    old_tg1 = TG;
4814		    iis_rowidxs.val.ptr = _create_iarray(iis_nrows);
4815		    iis_rowidxs.tag.kernel = TSTRG;
4816		    iis_colidxs.val.ptr = _create_iarray(iis_ncols);
4817		    iis_colidxs.tag.kernel = TSTRG;
4818		    iis_colstats.val.ptr = _create_carray(iis_ncols);
4819		    iis_colstats.tag.kernel = TSTRG;
4820
4821
4822		    if (!err && (_get_iis(lpd, &iis_nrows, &iis_ncols,
4823				     IArrayStart(iis_rowidxs.val.ptr), IArrayStart(iis_colidxs.val.ptr),
4824					  CArrayStart(iis_colstats.val.ptr))
4825			    != 0)
4826			)
4827		    {
4828			/* something went wrong; reallocate iis arrays with 0 size */
4829			TG = old_tg1;
4830			iis_nrows = 0;
4831			iis_ncols = 0;
4832			iis_rowidxs.val.ptr = _create_iarray(0);
4833			iis_colidxs.val.ptr = _create_iarray(0);
4834			iis_colstats.val.ptr = _create_carray(0);
4835		    }
4836
4837		    ec_assign(vhandle.ptr+iis_rowspos, iis_rowidxs.val, iis_rowidxs.tag);
4838		    ec_assign(vhandle.ptr+iis_colspos, iis_colidxs.val, iis_colidxs.tag);
4839		    ec_assign(vhandle.ptr+iis_colstatspos, iis_colstats.val, iis_colstats.tag);
4840
4841		}
4842	    }
4843#endif
4844	    break;
4845
4846	default:
4847	{
4848#ifdef HAS_POSTSOLVE
4849	    int presolve; /* postsolve prob. if it is left in a presolved state */
4850
4851	    if (XPRSgetintattrib(lpd->lp, XPRS_PRESOLVESTATE, &presolve))
4852	    {
4853		if (presolve & 2) /* is in a presolve state */
4854		    CallN(XPRSpostsolve(lpd->lp)); /* post-solve problem if possible */
4855	    }
4856#endif
4857	    if (lpd->descr_state == DESCR_UNBOUNDED_NOSOL ||
4858		lpd->descr_state == DESCR_UNKNOWN_NOSOL)
4859	    {/* no result this time, but add all cutpool constraints
4860                and resolve may give a solution
4861	     */
4862		add_cp_cstr = 2;
4863	    } else
4864	    {/* no results, and adding more constraints will not improve the
4865		situation */
4866		add_cp_cstr = 0;
4867	    }
4868	}}
4869#ifdef COIN
4870	coin_reset_prob(lpd);
4871#endif
4872
4873	if (add_cp_cstr)
4874	{
4875	    int zerobeg = 0, offset, nzcount, j;
4876	    double ftol;
4877
4878	    Get_Feasibility_Tolerance(cpx_env, lpd, &ftol);
4879	    i = 0;
4880	    last_violated_idx = -1;
4881	    while (i < unadded_cnt)
4882	    {
4883		if ((j = cp_unadded[i]) >= 0)
4884		{
4885		    switch (cp_map2[j] = _cstr_state(lpd,j,add_cp_cstr,sol.sols,ftol))
4886		    {
4887		    case CSTR_STATE_VIOLATED:
4888			violated_cnt++;
4889			offset = lpd->cp_rmatbeg2[j];
4890			nzcount = ( j <  lpd->cp_nr2-1 ? lpd->cp_rmatbeg2[j+1] - offset : lpd->cp_nnz2 - offset);
4891
4892			CPXaddrows(cpx_env, lpd->lp, 0, 1, nzcount,
4893				   &(lpd->cp_rhsx2[j]), &(lpd->cp_senx2[j]),
4894				   &zerobeg, /* only one row */
4895				   &(lpd->cp_rmatind2[offset]),
4896				   &(lpd->cp_rmatval2[offset]), NULL, NULL);
4897			lpd->mar++;
4898			cp_map2[j] = lpd->cp_nact2++;
4899			/* set last_violated_idx if it is not valid */
4900			if (last_violated_idx < 0) last_violated_idx = i;
4901			break;
4902		    case CSTR_STATE_SAT: /* satisfied, but not binding */
4903		    case CSTR_STATE_BINDING: /* satisfied and binding */
4904			if (last_violated_idx >= 0)
4905			{
4906			    cp_unadded[last_violated_idx] = last_violated_idx - i;
4907			    last_violated_idx = -1;
4908			}
4909			break;
4910		    case CSTR_STATE_INVALID: /* error */
4911			Bip_Error(RANGE_ERROR);
4912			break;
4913		    }
4914		    i++;
4915		} else
4916		{/* j < 0 : j is -displacement to unadded cstr */
4917		    i -= j;
4918		}
4919	    }
4920	    if (last_violated_idx >= 0) cp_unadded[last_violated_idx] = last_violated_idx- i;
4921	    if (violated_cnt > 0)
4922	    {
4923		Mark_Copy_As_Modified(lpd);
4924		TG = old_tg; /* reallocate row-wise result arrays */
4925		if (sol.slacks != NULL)
4926		    _create_result_darray(vhandle, slackspos, lpd->mar, &outslacks, &sol.slacks);
4927		if (sol.rbase != NULL)
4928		    _create_result_iarray(vhandle, rbasepos, lpd->mar, &outrbase, &sol.rbase);
4929		if (sol.pis != NULL)
4930		    _create_result_darray(vhandle,   pispos, lpd->mar, &outpis, &sol.pis);
4931	    }
4932	} /* if (add_cp_cstr) */
4933
4934    } while (violated_cnt > 0); /* do ... */
4935
4936    Request_Unify_Integer(vres, tres, lpd->descr_state);
4937    Request_Unify_Integer(vstat, tstat, lpd->sol_state);
4938
4939    /* (-)HUGE_VAL is used for the maximum best/worst bound instead of
4940       (-)CPX_INFBOUND because:
4941          1) The objective value can exceed CPX_INFBOUND
4942          2) We use 1.0Inf at the ECLiPSe level for unbounded objective
4943             value
4944       Note worst and best bounds are unified even for failure case
4945       (for use in any future failure handler)
4946    */
4947    Request_Unify_Float(vworst, tworst, worstbound);
4948    Request_Unify_Float(vbest, tbest, bestbound);
4949
4950    if (add_cp_cstr == 1)
4951    {/* have results */
4952	if (sol.sols != NULL)
4953	    ec_assign(vhandle.ptr+solspos, outsols.val, outsols.tag);
4954	if (sol.pis != NULL)
4955	    ec_assign(vhandle.ptr+pispos,  outpis.val, outpis.tag);
4956	if (sol.slacks != NULL)
4957	    ec_assign(vhandle.ptr+slackspos, outslacks.val, outslacks.tag);
4958	if (sol.djs != NULL)
4959	    ec_assign(vhandle.ptr+djspos, outdjs.val, outdjs.tag);
4960	if (sol.cbase != NULL)
4961	    ec_assign(vhandle.ptr+cbasepos, outcbase.val, outcbase.tag);
4962	if (sol.rbase != NULL)
4963	    ec_assign(vhandle.ptr+rbasepos, outrbase.val, outrbase.tag);
4964    }
4965    else
4966    {
4967	pword pw;
4968	/* no solution; reset arrays as these states might not fail */
4969	Make_Nil(&pw);
4970	if (sol.sols != NULL)
4971	    ec_assign(vhandle.ptr+solspos, pw.val, pw.tag);
4972	if (sol.pis != NULL)
4973	    ec_assign(vhandle.ptr+pispos, pw.val, pw.tag);
4974	if (sol.slacks != NULL)
4975	    ec_assign(vhandle.ptr+slackspos, pw.val, pw.tag);
4976	if (sol.djs != NULL)
4977	    ec_assign(vhandle.ptr+djspos, pw.val, pw.tag);
4978	if (sol.cbase != NULL)
4979	    ec_assign(vhandle.ptr+cbasepos, pw.val, pw.tag);
4980	if (sol.rbase != NULL)
4981	    ec_assign(vhandle.ptr+rbasepos, pw.val, pw.tag);
4982    }
4983
4984    if (has_cp) reset_rowcols(lpd, oldmar, lpd->mac);
4985
4986    Return_Unify;
4987}
4988
4989
4990int
4991p_cpx_loadbase(value vlp, type tlp, value vcarr, type tcarr, value vrarr, type trarr)
4992{
4993    lp_desc *lpd;
4994    int res;
4995    Check_Array(tcarr);
4996    Check_Array(trarr);
4997    LpDesc(vlp, tlp, lpd);
4998    SetPreSolve(lpd->presolve);
4999    if (lpd->mac == IArraySize(vcarr.ptr) && lpd->mar == IArraySize(vrarr.ptr)) {
5000	/* Finx b58: only load basis if current row/col == array sizes */
5001#ifdef LOG_CALLS
5002	Fprintf(log_output_, "\niloadbasis(...);");
5003#endif
5004	res = CPXcopybase(cpx_env, lpd->lp, IArrayStart(vcarr.ptr), IArrayStart(vrarr.ptr));
5005	if (res != 0) { Bip_Error(EC_EXTERNAL_ERROR); }
5006    }
5007    Succeed;
5008}
5009
5010#ifdef COIN
5011int
5012p_cpx_loadorder(value vlp, type tlp, value vn, type tn, value vl, type tl)
5013{
5014    Succeed;
5015}
5016#else
5017
5018int
5019p_cpx_loadorder(value vlp, type tlp, value vn, type tn, value vl, type tl)
5020{
5021    lp_desc *lpd;
5022    int *idx, *prio;
5023#ifdef CPLEX
5024    int *bdir;
5025#endif
5026#ifdef XPRESS
5027    char *bdir;
5028#endif
5029    int i, res;
5030    pword *buf = TG;
5031
5032    Check_Integer(tn);
5033    if (vn.nint <= 0) Succeed; /* no need to load anything */
5034
5035    LpDesc(vlp, tlp, lpd);
5036    Push_Buffer(vn.nint*3*sizeof(int));
5037    idx = (int*) BufferStart(buf);
5038    prio = (int*) (BufferStart(buf) + vn.nint*sizeof(int));
5039#ifdef CPLEX
5040    bdir = (int*) (BufferStart(buf) + vn.nint*2*sizeof(int));
5041#endif
5042#ifdef XPRESS
5043    bdir = (char*) (BufferStart(buf) + vn.nint*2*sizeof(int));
5044#endif
5045    i = 0;
5046    while (IsList(tl))
5047    {
5048	pword *car = vl.ptr;
5049	pword *cdr = car + 1;
5050	pword *pw;
5051
5052	Dereference_(car);
5053	Check_Structure(car->tag);
5054	if (DidArity(car->val.ptr->val.did) != 3)
5055	    { Bip_Error(RANGE_ERROR); }
5056
5057	pw = car->val.ptr + 1;		/* colindex */
5058	Dereference_(pw);
5059	idx[i] = pw->val.nint;
5060	pw = car->val.ptr + 2;		/* priority */
5061	Dereference_(pw);
5062	prio[i] = pw->val.nint;
5063	pw = car->val.ptr + 3;		/* direction */
5064	Dereference_(pw);
5065#ifdef CPLEX
5066	bdir[i] = pw->val.nint;
5067#endif
5068#ifdef XPRESS
5069	bdir[i] = pw->val.nint == 1 ? 'D' : pw->val.nint == 2 ? 'U' : 'N';
5070#endif
5071	++i;
5072
5073	Dereference_(cdr);
5074	tl = cdr->tag;
5075	vl = cdr->val;
5076    }
5077    Check_List(tl);
5078    if (i != vn.nint)
5079	{ Bip_Error(RANGE_ERROR); }
5080
5081#ifdef LOG_CALLS
5082    Fprintf(log_output_, "\nloaddir(...);"); ec_flush(log_output_);
5083#endif
5084    SetPreSolve(lpd->presolve);
5085    res = CPXcopyorder(cpx_env, lpd->lp, i, idx, prio, bdir);
5086    TG = buf;				/* pop aux arrays */
5087    if (res != 0) { Bip_Error(EC_EXTERNAL_ERROR); }
5088    Succeed;
5089}
5090
5091#endif
5092
5093
5094/*
5095 * Add SOSs from descriptor arrays to solver
5096 */
5097
5098int
5099p_cpx_flush_sos(value vhandle, type thandle)
5100{
5101#ifdef HAS_NO_ADDSOS
5102    Bip_Error(UNIMPLEMENTED);
5103#else
5104    lp_desc *lpd;
5105    untrail_data udata;
5106
5107    Check_Structure(thandle);
5108    LpDesc(vhandle.ptr[HANDLE_CPH].val, vhandle.ptr[HANDLE_CPH].tag, lpd);
5109
5110    if (lpd->nsos <= lpd->nsos_added)
5111    	Succeed;
5112
5113#if defined(CPLEX) && CPLEX < 10
5114    if (CPXaddsos(cpx_env, lpd->lp, lpd->nsos, lpd->nsosnz, lpd->sostype,
5115	    NULL, lpd->sosbeg, lpd->sosind, lpd->sosref))
5116#else
5117    if (CPXaddsos(cpx_env, lpd->lp, lpd->nsos, lpd->nsosnz, lpd->sostype,
5118	    lpd->sosbeg, lpd->sosind, lpd->sosref, NULL))
5119#endif
5120    {
5121	Bip_Error(EC_EXTERNAL_ERROR);
5122    }
5123    udata.oldmac = lpd->macadded;
5124    udata.oldmar = lpd->mar;
5125    udata.oldsos = lpd->nsos_added;
5126    udata.oldidc = lpd->nidc;
5127    ec_trail_undo(_cpx_del_rowcols, vhandle.ptr, vhandle.ptr+HANDLE_STAMP, (word*) &udata, NumberOfWords(untrail_data), TRAILED_WORD32);
5128    lpd->nsos_added = lpd->nsos;
5129    /* could free/resize arrays here */
5130    Succeed;
5131#endif
5132}
5133
5134
5135/*
5136 * Set up SOS arrays in descriptor
5137 */
5138
5139int
5140p_cpx_add_new_sos(value vlp, type tlp,
5141	      value vsostype, type tsostype, 	/* 1 or 2 */
5142	      value vn, type tn,		/* member count */
5143	      value vl, type tl)		/* member list */
5144{
5145    lp_desc *lpd;
5146    double weight;
5147    int i, nnewsos;
5148
5149    Check_Integer(tsostype);
5150    Check_Integer(tn);
5151
5152    LpDescOnly(vlp, tlp, lpd);
5153    if (vn.nint <= vsostype.nint)
5154    	Succeed; /* return immediately if sos set is trivial */
5155
5156    /* the temporary array index of the new SOS */
5157    nnewsos = lpd->nsos - lpd->nsos_added;
5158    ++lpd->nsos;
5159    if (nnewsos+1 >= lpd->sossz)
5160    {
5161	/* allocate enough space for the new sos */
5162	/* CAUTION: in this array must have at least nnewsos+1 elements !!! */
5163	if (lpd->sossz == 0)
5164	{
5165	    lpd->sossz = NEWSOS_INCR;
5166	    lpd->sosbeg = (int *) Malloc(NEWSOS_INCR*sizeof(int));
5167	}
5168	else
5169	{
5170	    lpd->sossz += NEWSOS_INCR;
5171	    lpd->sosbeg = (int *) Realloc(lpd->sosbeg, lpd->sossz*sizeof(int));
5172	}
5173    }
5174    lpd->sosbeg[nnewsos] = lpd->nsosnz;
5175    lpd->sosbeg[nnewsos+1] = lpd->nsosnz + vn.nint;
5176
5177    /* allocate enough space for the sos members */
5178    i = lpd->nsosnz;
5179    lpd->nsosnz += vn.nint;
5180    if (lpd->nsosnz > lpd->sosnzsz)
5181    {
5182	if (lpd->sosnzsz == 0)
5183	{
5184	    lpd->sosnzsz = RoundTo(lpd->nsosnz, 512);
5185	    lpd->sostype = (sostype_t *) Malloc(lpd->sosnzsz*sizeof(sostype_t));
5186	    lpd->sosind = (int *) Malloc(lpd->sosnzsz*sizeof(int));
5187	    lpd->sosref = (double *) Malloc(lpd->sosnzsz*sizeof(double));
5188	}
5189	else
5190	{
5191	    lpd->sosnzsz = RoundTo(lpd->nsosnz, 512);
5192	    lpd->sostype = (sostype_t *) Realloc(lpd->sostype, lpd->sosnzsz*sizeof(sostype_t));
5193	    lpd->sosind = (int *) Realloc(lpd->sosind, lpd->sosnzsz*sizeof(int));
5194	    lpd->sosref = (double *) Realloc(lpd->sosref, lpd->sosnzsz*sizeof(double));
5195	}
5196    }
5197
5198    for (weight = 1.0; IsList(tl); weight += 1.0, ++i)
5199    {
5200	pword *car = vl.ptr;
5201	pword *cdr = car + 1;
5202
5203	Dereference_(car);
5204	if (!IsInteger(car->tag))
5205	    { Bip_Error(TYPE_ERROR); }
5206
5207	lpd->sostype[i] = vsostype.nint==1 ? SOLVER_SOS_TYPE1 : SOLVER_SOS_TYPE2;
5208	lpd->sosind[i] = (int) car->val.nint;
5209	lpd->sosref[i] = weight;
5210
5211	Dereference_(cdr);
5212	tl = cdr->tag;
5213	vl = cdr->val;
5214    }
5215    Check_List(tl);
5216    if (i != lpd->nsosnz)
5217	{ Bip_Error(RANGE_ERROR); }
5218
5219    Succeed;
5220}
5221
5222
5223int
5224p_cpx_get_objval(value vlp, type tlp, value v, type t)
5225{
5226    lp_desc *lpd;
5227
5228    LpDescOnly(vlp, tlp, lpd);
5229    Return_Unify_Float(v, t, lpd->objval);
5230}
5231
5232
5233#if 0 /* not used */
5234int
5235p_cpx_get_coef(value vlp, type tlp,
5236	       value vi, type ti,
5237	       value vj, type tj,
5238	       value vc, type tc)
5239{
5240#ifdef CPLEX
5241    lp_desc *lpd;
5242    double d;
5243    LpDesc(vlp, tlp, lpd);
5244    SetPreSolve(lpd->presolve);
5245    Check_Integer(ti);
5246    Check_Integer(tj);
5247    if (vi.nint >= lpd->mar || vj.nint >= lpd->mac) { Bip_Error(RANGE_ERROR); }
5248    CPXupdatemodel(lpd->lp);	/* before CPXget... */
5249    if (CPXgetcoef(cpx_env, lpd->lp, vi.nint, vj.nint, &d) != 0)
5250	{ Bip_Error(EC_EXTERNAL_ERROR); }
5251    Return_Unify_Float(vc, tc, d);
5252#else
5253    Bip_Error(UNIMPLEMENTED);
5254#endif
5255}
5256
5257#endif
5258
5259/*
5260 * Retrieve the matrix coefficients:
5261 * - First call cplex_get_row(+CPH, +CType, +I, -Base) which
5262 *   prepares for retrieval of row i of constraint type CType:
5263 *       normal constraints:
5264 *          retrieves the coefficients of row I into the rmatind/rmatval
5265 *          arrays
5266 *       cutpool constraints:
5267 *          setup lpd->nnz to the non-zero size for row i, and set Base
5268 *          to the offset to coefficients for row i in the appropriate
5269 *          rmatind/rmatval  arrays
5270 * - Then call cplex_get_col_coef(+CPH, +CType, +Base, -J, -C) which returns
5271 *   one nonzero column J and the corresponding coefficient C. Successive
5272 *   calls return the other nonzero columns in decreasing order. When no
5273 *   nonzero column is left, cplex_get_col_coef/3 fails. The row number is
5274 *   the one given in the preceding cpx_get_row/2 call.  */
5275
5276int
5277p_cpx_get_row(value vlp, type tlp, value vpool, type tpool,
5278	      value vi, type ti, value vbase, type tbase)
5279{
5280    lp_desc *lpd;
5281    int base;
5282    LpDesc(vlp, tlp, lpd);
5283
5284    Check_Integer(ti);
5285
5286    CPXupdatemodel(lpd->lp);	/* before CPXget... */
5287    switch (vpool.nint)
5288    {
5289    case CSTR_TYPE_NORM:
5290    {
5291	int ncols;
5292	int rmatbeg[2];
5293#ifdef CPLEX
5294	int surplus;
5295#endif
5296	base = 0; /* read one constraint only */
5297	ncols = lpd->mac;
5298	if (ncols > lpd->nnz_sz)	/* allocate/grow arrays */
5299	{
5300	    if (lpd->nnz_sz == 0)
5301	    {
5302		lpd->nnz_sz = ncols;
5303		lpd->rmatind = (int *) Malloc(ncols*sizeof(int));
5304		lpd->rmatval = (double *) Malloc(ncols*sizeof(double));
5305	    }
5306	    else
5307	    {
5308		lpd->nnz_sz = ncols;
5309		lpd->rmatind = (int *) Realloc(lpd->rmatind, ncols*sizeof(int));
5310		lpd->rmatval = (double *) Realloc(lpd->rmatval, ncols*sizeof(double));
5311	    }
5312	}
5313	SetPreSolve(lpd->presolve);
5314	/* note that for COIN, CPXgetrows maps to coin_getrow, which gets
5315	   one row only
5316	*/
5317	if (
5318	    CPXgetrows(cpx_env, lpd->lp, &lpd->nnz, rmatbeg, lpd->rmatind,
5319	        lpd->rmatval, lpd->nnz_sz, &surplus, vi.nint, vi.nint)
5320	    )
5321	{ Bip_Error(EC_EXTERNAL_ERROR); }
5322	break;
5323    }
5324/*
5325    case CSTR_TYPE_PERMCP:
5326	base = lpd->cp_rmatbeg[vi.nint];
5327	lpd->nnz = (vi.nint == lpd->cp_nr-1 ? lpd->cp_nnz-base : lpd->cp_rmatbeg[vi.nint+1]-base);
5328	break;
5329*/
5330    case CSTR_TYPE_CONDCP:
5331	base = lpd->cp_rmatbeg2[vi.nint];
5332	lpd->nnz = (vi.nint == lpd->cp_nr2-1 ? lpd->cp_nnz2-base : lpd->cp_rmatbeg2[vi.nint+1]-base);
5333
5334	break;
5335    default:
5336	Bip_Error(RANGE_ERROR);
5337	break;
5338    }
5339
5340    Return_Unify_Integer(vbase, tbase, base);
5341}
5342
5343
5344/* returns the coeff vc for vj'th argument of the Prolog variable array
5345   NOTE: assumes variable array created from a list in reverse column order
5346*/
5347int
5348p_cpx_get_col_coef(value vlp, type tlp, value vpool, type tpool,
5349		   value vbase, type tbase,
5350		   value vj, type tj, value vc, type tc)
5351{
5352    int i;
5353    lp_desc *lpd;
5354    Prepare_Requests
5355    LpDescOnly(vlp, tlp, lpd);
5356    if (lpd->nnz == 0)
5357	{ Fail; }
5358    --lpd->nnz;
5359    i = vbase.nint + lpd->nnz;
5360    switch (vpool.nint)
5361    {
5362/*
5363    case CSTR_TYPE_PERMCP:
5364	Request_Unify_Integer(vj, tj, (lpd->mac+SOLVER_MAT_BASE - lpd->cp_rmatind[i]));
5365	Request_Unify_Float(vc, tc, lpd->cp_rmatval2[i]);
5366	break;
5367*/
5368    case CSTR_TYPE_CONDCP:
5369	Request_Unify_Integer(vj, tj, (lpd->mac+SOLVER_MAT_BASE - lpd->cp_rmatind2[i]));
5370	Request_Unify_Float(vc, tc, lpd->cp_rmatval2[i]);
5371	break;
5372    case CSTR_TYPE_NORM:
5373	Request_Unify_Integer(vj, tj, (lpd->mac+SOLVER_MAT_BASE - lpd->rmatind[i]));
5374	Request_Unify_Float(vc, tc, lpd->rmatval[i]);
5375	break;
5376    default:
5377	Bip_Error(RANGE_ERROR);
5378    }
5379    Return_Unify;
5380}
5381
5382int
5383p_cpx_get_obj_coef(value vlp, type tlp, value vj, type tj, value vc, type tc)
5384{
5385    lp_desc *lpd;
5386    double d[1];
5387    Check_Integer(tj);
5388    LpDesc(vlp, tlp, lpd);
5389    if (vj.nint >= lpd->mac) { Bip_Error(RANGE_ERROR); }
5390    SetPreSolve(lpd->presolve);
5391    CPXupdatemodel(lpd->lp);	/* before CPXget... */
5392    if (CPXgetobj(cpx_env, lpd->lp, d, (int) vj.nint, (int) vj.nint) != 0)
5393	{ Bip_Error(EC_EXTERNAL_ERROR); }
5394    Return_Unify_Float(vc, tc, d[0]);
5395}
5396
5397
5398/*----------------------------------------------------------------------*
5399 * Global stack arrays (used for answer vectors)
5400 *----------------------------------------------------------------------*/
5401
5402
5403int
5404p_create_darray(value vi, type ti, value varr, type tarr)
5405{
5406    pword *pbuf;
5407    Check_Integer(ti);
5408    pbuf = _create_darray(vi.nint);
5409    Return_Unify_String(varr, tarr, pbuf);
5410}
5411
5412static pword *
5413_create_carray(int i)
5414{
5415    pword *pbuf = TG;
5416    Push_Buffer(i*sizeof(char) + 1);
5417    return pbuf;
5418}
5419
5420static pword *
5421_create_darray(int i)
5422{
5423    pword *pbuf = TG;
5424    Push_Buffer(i*sizeof(double) + 1);
5425    return pbuf;
5426}
5427
5428static pword *
5429_create_iarray(int i)
5430{
5431    pword *pbuf = TG;
5432    Push_Buffer(i*sizeof(int) + 1);
5433    return pbuf;
5434}
5435
5436int
5437p_darray_size(value varr, type tarr, value vi, type ti)
5438{
5439    Check_Array(tarr);
5440    Return_Unify_Integer(vi, ti, DArraySize(varr.ptr));
5441}
5442
5443int
5444p_get_darray_element(value varr, type tarr, value vi, type ti, value vel, type tel)
5445{
5446    double f;
5447    Check_Array(tarr);
5448    Check_Integer(ti);
5449    /* RANGE_ERROR if vi.nint is negative -- as it is a large unsigned */
5450    if ((unsigned) vi.nint >= DArraySize(varr.ptr))
5451	{ Bip_Error(RANGE_ERROR); }
5452    f = ((double *) BufferStart(varr.ptr))[vi.nint];
5453    Return_Unify_Float(vel, tel, f);
5454}
5455
5456int
5457p_set_darray_element(value varr, type tarr, value vi, type ti, value vel, type tel)
5458{
5459    Check_Array(tarr);
5460    Check_Integer(ti);
5461    Check_Float(tel);
5462    Check_Number(tel);
5463
5464    if ((unsigned) vi.nint >= DArraySize(varr.ptr))
5465	{ Bip_Error(RANGE_ERROR); }
5466    if (GB <= varr.ptr  &&  varr.ptr < TG)
5467    {
5468	((double *) BufferStart(varr.ptr))[vi.nint] = DoubleVal(vel, tel);
5469	Succeed;
5470    }
5471    else	/* nondeterministic */
5472    {
5473    	Bip_Error(UNIMPLEMENTED);
5474    }
5475}
5476
5477int
5478p_darray_list(value varr, type tarr, value vmr, type tmr, value vlst, type tlst)
5479{
5480    pword	list;
5481    pword	*car;
5482    pword	*cdr = &list;
5483    unsigned	i;
5484
5485    Check_Array(tarr);
5486    Check_Integer(tmr);
5487    if (vmr.nint > DArraySize(varr.ptr)) Bip_Error(RANGE_ERROR);
5488    for (i = 0; i < vmr.nint; ++i)
5489    {
5490	car = TG;
5491	Push_List_Frame();
5492	Make_List(cdr, car);
5493	Make_Float(car, ((double *) BufferStart(varr.ptr))[i]);
5494	cdr = car + 1;
5495    }
5496    Make_Nil(cdr);
5497    Return_Unify_Pw(vlst, tlst, list.val, list.tag);
5498}
5499
5500/* returns the base (start) of the solver matrix (0 or 1) */
5501int
5502p_cpx_matrix_base(value vbase, type tbase)
5503{
5504    Return_Unify_Integer(vbase, tbase, SOLVER_MAT_BASE);
5505}
5506
5507int
5508p_cpx_matrix_offset(value voff, type toff)
5509{
5510    Return_Unify_Integer(voff, toff, SOLVER_MAT_OFFSET);
5511}
5512
5513
5514/*----------------------------------------------------------------------*
5515 * CutPools
5516 *----------------------------------------------------------------------*/
5517
5518/* cutpools implemented using the rowwise representation of a problem
5519   used by CPLEX and Xpress. These are then added to the problem before
5520   optimisation. There two types of cutpools: unconditional and
5521   conditional, each representing by its own data structures in the handle.
5522   Rows in the conditional cutpool have a `name' associated with them, which
5523   group the rows into different virtual pools. These virtual pools correspond
5524   conceptually to the different cutpools in a MP solver
5525*/
5526
5527int
5528p_cpx_get_cutpool_size(value vlp, type tlp,  value vnr, type tnr, value vnnz, type tnnz)
5529{
5530    lp_desc *lpd;
5531    Prepare_Requests
5532    LpDescOnly(vlp, tlp, lpd);
5533
5534    Request_Unify_Integer(vnr, tnr, lpd->cp_nr2);
5535    Request_Unify_Integer(vnnz, tnnz, lpd->cp_nnz2);
5536    Return_Unify;
5537}
5538
5539int
5540p_cpx_reset_cutpool_size(value vlp, type tlp,
5541     value vnr, type tnr, value vnnz, type tnnz)
5542{
5543    lp_desc *lpd;
5544    LpDescOnly(vlp, tlp, lpd);
5545
5546    Check_Integer(tnr);
5547    Check_Integer(tnnz);
5548
5549    if (vnr.nint > lpd->cp_nr2 || vnr.nint < 0) {Bip_Error(RANGE_ERROR);}
5550    lpd->cp_nr2 = vnr.nint;
5551    if (vnnz.nint > lpd->cp_nnz2 || vnnz.nint < 0) {Bip_Error(RANGE_ERROR);}
5552    lpd->cp_nnz2 = vnnz.nint;
5553    Succeed;
5554}
5555
5556int
5557p_cpx_set_cpcstr_cond(value vlp, type tlp, value vidx, type tidx,
5558		      value vtype, type ttype, value vc, type tc)
5559{
5560    int i;
5561    lp_desc *lpd;
5562
5563    LpDescOnly(vlp, tlp, lpd);
5564    Check_Integer(tidx);
5565    Check_Integer(ttype);
5566    Check_Integer(tc);
5567
5568    if (vidx.nint < 0 || vidx.nint >= lpd->cp_nr2) { Bip_Error(RANGE_ERROR); }
5569    switch (vtype.nint)
5570    {
5571    case CP_ACTIVE: /* active state */
5572	if (vc.nint != 0 && vc.nint != 1) { Bip_Error(RANGE_ERROR); }
5573	lpd->cp_active2[vidx.nint] = (char) vc.nint;
5574	break;
5575    case CP_ADDINIT: /* add initially */
5576	if (vc.nint != 0 && vc.nint != 1) { Bip_Error(RANGE_ERROR); }
5577	lpd->cp_initial_add2[vidx.nint] = (char) vc.nint;
5578	break;
5579    default:
5580	Bip_Error(RANGE_ERROR);
5581	break;
5582    }
5583
5584    Succeed;
5585}
5586
5587int
5588p_cpx_init_cpcstr(value vlp, type tlp, value vidx, type tidx, value vgrp, type tgrp,
5589		   value vact, type tact, value vinit_add, type tinit_add)
5590{
5591    lp_desc *lpd;
5592    LpDescOnly(vlp, tlp, lpd);
5593
5594    Check_Integer(tidx);
5595    Check_Integer(tgrp);
5596    Check_Integer(tact);
5597    Check_Integer(tinit_add);
5598
5599    lpd->cp_initial_add2[vidx.nint] = (char) vinit_add.nint;
5600    lpd->cp_active2[vidx.nint] = (char) vact.nint;
5601    if (lpd->cp_pools_max2[vgrp.nint] >= lpd->cp_pools_sz2[vgrp.nint])
5602    {
5603	lpd->cp_pools_sz2[vgrp.nint] += NEWROW_INCR;
5604	lpd->cp_pools2[vgrp.nint] = (int *) Realloc(lpd->cp_pools2[vgrp.nint],lpd->cp_pools_sz2[vgrp.nint]*sizeof(int));
5605    }
5606    lpd->cp_pools2[vgrp.nint][lpd->cp_pools_max2[vgrp.nint]++] = vidx.nint;
5607
5608    return PSUCCEED;
5609}
5610
5611#define Expand_Named_CutPool_Arrays(lpd, n) { \
5612    int sz = lpd->cp_npools_sz2 += CUTPOOL_INCR; \
5613    /* expand the arrays */\
5614    lpd->cp_pools2 = (int **) Realloc(lpd->cp_pools2,sz*sizeof(int *));\
5615    lpd->cp_pools_max2 = (int *) Realloc(lpd->cp_pools_max2,sz*sizeof(int));\
5616    lpd->cp_pools_sz2 = (int *) Realloc(lpd->cp_pools_sz2,sz*sizeof(int));\
5617    lpd->cp_names2 = (char **) Realloc(lpd->cp_names2,sz*sizeof(char *));\
5618    for (i = n+1; i < sz; i++) \
5619    { /* initialise the new elements (except n - which will be filled \
5620         by Create_New_CutPool) */\
5621	lpd->cp_pools2[i] = NULL;\
5622        lpd->cp_names2[i] = NULL; \
5623	lpd->cp_pools_max2[i] = 0;\
5624	lpd->cp_pools_sz2[i] = 0;\
5625    }\
5626}
5627
5628#define Create_New_CutPool(lpd, n, name) {                      \
5629    lpd->cp_pools_sz2[n] = NEWROW_INCR;                        \
5630    lpd->cp_pools_max2[n] = 0;                                  \
5631    lpd->cp_pools2[n] = Malloc(NEWROW_INCR*sizeof(int));        \
5632    lpd->cp_names2[n] = Malloc((strlen(name)+1)*sizeof(char));  \
5633    strcpy(lpd->cp_names2[n], name);                            \
5634}
5635
5636
5637int
5638p_cpx_get_named_cp_index(value vlp, type tlp, value vname, type tname,
5639		         value vnew, type tnew, value vidx, type tidx)
5640{
5641    int i, n;
5642    lp_desc *lpd;
5643    LpDescOnly(vlp, tlp, lpd);
5644
5645    Check_Integer(tnew);
5646
5647    if (lpd->cp_npools_sz2 == 0)
5648    {/* create the default group the first time we use the named groups */
5649	lpd->cp_npools2 = 1;
5650	Expand_Named_CutPool_Arrays(lpd, 0);
5651	Create_New_CutPool(lpd, 0, "[]");
5652    }
5653    if (IsNil(tname)) i = 0; /* default group */
5654    else
5655    {/* user defined named group */
5656	Check_Atom(tname);
5657	for(i=1; i < lpd->cp_npools2; ++i)
5658	{
5659	    if (strcmp(lpd->cp_names2[i], DidName(vname.did)) == 0) break;
5660	}
5661	if (i == lpd->cp_npools2)
5662	{/* name was not found */
5663	    if (vnew.nint == 0) { Fail; } /* don't create new group */
5664	    else if ((n = lpd->cp_npools2++) >= lpd->cp_npools_sz2)
5665	    {
5666		Expand_Named_CutPool_Arrays(lpd, n);
5667	    }
5668	    Create_New_CutPool(lpd, n, DidName(vname.did));
5669	}
5670
5671    }
5672
5673    Return_Unify_Integer(vidx, tidx, i);
5674}
5675
5676
5677int
5678p_cpx_get_cpcstr_info(value vlp, type tlp, value vidx, type tidx,
5679		      value vitype, type titype, value vval, type tval)
5680{
5681    int i, val;
5682
5683    lp_desc *lpd;
5684    LpDescOnly(vlp, tlp, lpd);
5685
5686    Check_Integer(titype);
5687    Check_Integer(tidx);
5688
5689    if (vidx.nint < 0 || vidx.nint >= lpd->cp_nr2) { Bip_Error(RANGE_ERROR);}
5690
5691    switch (vitype.nint)
5692    {
5693    case CP_ACTIVE: /* active state */
5694	val = (int) lpd->cp_active2[vidx.nint];
5695	break;
5696    case CP_ADDINIT:
5697	val = (int) lpd->cp_initial_add2[vidx.nint];
5698	break;
5699    default:
5700	Bip_Error(RANGE_ERROR);
5701	break;
5702     }
5703
5704    Return_Unify_Integer(vval, tval, val);
5705}
5706
5707int
5708p_cpx_get_named_cpcstr_indices(value vlp, type tlp, value vpidx, type tpidx,
5709				value vilst, type tilst)
5710{
5711    int i;
5712    pword list;
5713    pword * head, * next = &list;
5714
5715    lp_desc *lpd;
5716    LpDescOnly(vlp, tlp, lpd);
5717
5718    Check_Integer(tpidx);
5719
5720    if (vpidx.nint < 0 || vpidx.nint >= lpd->cp_npools2) Bip_Error(RANGE_ERROR);
5721    for (i=0; i < lpd->cp_pools_max2[vpidx.nint]; i++)
5722    {
5723	head = TG;
5724	Push_List_Frame();
5725	Make_List(next, head);
5726	Make_Integer(head, lpd->cp_pools2[vpidx.nint][i]);
5727	next = head + 1;
5728    }
5729    Make_Nil(next);
5730    Return_Unify_Pw(vilst, tilst, list.val, list.tag);
5731}
5732
5733
5734/*----------------------------------------------------------------------*
5735 * Extending basis matrices
5736 *----------------------------------------------------------------------*/
5737
5738
5739int
5740p_create_extended_iarray(value varr, type tarr, value vi, type ti, value vxarr, type txarr)
5741{
5742    pword *pbuf;
5743
5744    Check_Integer(ti);
5745    Check_Array(tarr);
5746    pbuf = _create_iarray(vi.nint + IArraySize(varr.ptr));
5747    Return_Unify_String(vxarr, txarr, pbuf);
5748}
5749
5750int
5751p_create_extended_darray(value varr, type tarr, value vi, type ti, value vxarr, type txarr)
5752{
5753    pword *pbuf;
5754
5755    Check_Integer(ti);
5756    Check_Array(tarr);
5757    pbuf = _create_darray(vi.nint + DArraySize(varr.ptr));
5758    Return_Unify_String(vxarr, txarr, pbuf);
5759}
5760
5761int
5762p_decode_basis(value varr, type tarr, value vout, type tout)
5763{
5764    int i,n;
5765    int *v;
5766    pword *pw = TG;
5767    Check_Array(tarr);
5768    v = IArrayStart(varr.ptr);
5769    n = IArraySize(varr.ptr);
5770    Push_Struct_Frame(Did("[]",n));
5771    for (i = 0; i < n; ++i)
5772    {
5773	Make_Integer(&pw[i+1], v[i]);
5774    }
5775    Return_Unify_Structure(vout, tout, pw);
5776}
5777
5778int
5779p_copy_extended_column_basis(value varr, type tarr, value vlos, type tlos,
5780	value vhis, type this, value vxarr, type txarr)
5781{
5782    unsigned i;
5783    int      *v;
5784    int      *vx;
5785
5786    Check_Array(tarr);
5787    Check_Array(txarr);
5788    Check_List(tlos);
5789    Check_List(this);
5790
5791    v = IArrayStart(varr.ptr);
5792    vx = IArrayStart(vxarr.ptr);
5793
5794    /*
5795     * Note that this assumes the basis status array contains
5796     * the status of the problem variables (columns) only
5797     * and not the status of the slack variables (rows).
5798     * We want to add status for the new columns so we must:
5799     * 1) copy the existing variables' status
5800     * 2) add the CPX_COL_FREE_SUPER status for the new columns
5801     */
5802    for (i = 0; i < IArraySize(varr.ptr); ++i)
5803    {
5804      vx[i] = v[i];
5805    }
5806    for (i = IArraySize(varr.ptr); i < IArraySize(vxarr.ptr); ++i)
5807    {
5808      if (IsList(tlos) && IsList(this))
5809      {
5810	double lo, hi;
5811	pword *car = vlos.ptr;
5812	pword *cdr = car + 1;
5813	Dereference_(car); Check_Double(car->tag);
5814	lo = Dbl(car->val);
5815	Dereference_(cdr); tlos = cdr->tag; vlos = cdr->val;
5816	car = vhis.ptr;
5817	cdr = car + 1;
5818	Dereference_(car); Check_Double(car->tag);
5819	hi = Dbl(car->val);
5820	Dereference_(cdr); this = cdr->tag; vhis = cdr->val;
5821	/* The following are the settings determined by experimentation */
5822	if (hi <= 0.0)
5823	    vx[i] = CPX_COL_AT_UPPER;
5824	else if (lo > -CPX_INFBOUND)
5825	    vx[i] = CPX_COL_AT_LOWER;
5826	else
5827	    vx[i] = CPX_COL_FREE_SUPER;
5828      }
5829      else { Bip_Error(TYPE_ERROR); }
5830    }
5831    Check_Nil(tlos);
5832    Succeed;
5833}
5834
5835int
5836p_copy_extended_arrays(value vbarr, type tbarr, value vsarr, type tsarr, value vdarr, type tdarr, value vxbarr, type txbarr, value vxsarr, type txsarr, value vxdarr, type txdarr)
5837{
5838    int     i;
5839    int     *vb;
5840    int     *vxb;
5841    double  *vs;
5842    double  *vxs;
5843    double  *vd;
5844    double  *vxd;
5845
5846    Check_Array(tbarr);
5847    Check_Array(tsarr);
5848    Check_Array(tdarr);
5849    Check_Array(txbarr);
5850    Check_Array(txsarr);
5851    Check_Array(txdarr);
5852
5853    vb = IArrayStart(vbarr.ptr);
5854    vxb = IArrayStart(vxbarr.ptr);
5855    vs = DArrayStart(vsarr.ptr);
5856    vxs = DArrayStart(vxsarr.ptr);
5857    vd = DArrayStart(vdarr.ptr);
5858    vxd = DArrayStart(vxdarr.ptr);
5859
5860    /*
5861     * Note that this assumes the basis status array contains
5862     * the status of the problem variables (columns) only
5863     * and not the status of the slack variables (rows).
5864     * We want to add status for the new columns so we must:
5865     * 1) copy the existing variables' status, solution value
5866     * 2) add the CPX_COL_FREE_SUPER status, 0.0 solution value
5867     *    and 0.0 reduced cost for the new columns
5868     */
5869    for (i = 0; i < IArraySize(vbarr.ptr); ++i)
5870    {
5871      vxb[i] = vb[i];
5872      vxs[i] = vs[i];
5873      vxd[i] = vd[i];
5874    }
5875    for (i = IArraySize(vbarr.ptr); i < IArraySize(vxbarr.ptr); ++i)
5876    {
5877      vxb[i] = CPX_COL_FREE_SUPER;
5878      vxs[i] = 0.0;
5879      vxd[i] = 0.0;
5880    }
5881    Succeed;
5882}
5883
5884
5885/*----------------------------------------------------------------------*
5886 * Accessing iarrays
5887 *----------------------------------------------------------------------*/
5888
5889
5890int
5891p_create_iarray(value vi, type ti, value varr, type tarr)
5892{
5893    pword *pbuf;
5894    Check_Integer(ti);
5895    pbuf = _create_iarray(vi.nint);
5896    Return_Unify_String(varr, tarr, pbuf);
5897}
5898
5899int
5900p_iarray_size(value varr, type tarr, value vi, type ti)
5901{
5902    Check_Array(tarr);
5903    Return_Unify_Integer(vi, ti, IArraySize(varr.ptr));
5904}
5905
5906int
5907p_get_iarray_element(value varr, type tarr, value vi, type ti, value vel, type tel)
5908{
5909    int i;
5910    Check_Array(tarr);
5911    Check_Integer(ti);
5912    if ((unsigned) vi.nint >= IArraySize(varr.ptr))
5913	{ Bip_Error(RANGE_ERROR); }
5914    i = ((int *) BufferStart(varr.ptr))[vi.nint];
5915    Return_Unify_Integer(vel, tel, i);
5916}
5917
5918int
5919p_set_iarray_element(value varr, type tarr, value vi, type ti, value vel, type tel)
5920{
5921    Check_Array(tarr);
5922    Check_Integer(ti);
5923    Check_Integer(tel);
5924    if ((unsigned) vi.nint >= IArraySize(varr.ptr))
5925	{ Bip_Error(RANGE_ERROR); }
5926    if (GB <= varr.ptr  &&  varr.ptr < TG)
5927    {
5928	((int *) BufferStart(varr.ptr))[vi.nint] = vel.nint;
5929	Succeed;
5930    }
5931    else	/* nondeterministic */
5932    {
5933    	Bip_Error(UNIMPLEMENTED);
5934    }
5935}
5936
5937int
5938p_iarray_list(value varr, type tarr, value vlst, type tlst)
5939{
5940    pword      list;
5941    pword      *car;
5942    pword      *cdr = &list;
5943    unsigned   i;
5944
5945    Check_Array(tarr);
5946    for (i = 0; i < IArraySize(varr.ptr); ++i)
5947      {
5948	car = TG;
5949	Push_List_Frame();
5950	Make_List(cdr, car);
5951	Make_Integer(car, ((int *) BufferStart(varr.ptr))[i]);
5952	cdr = car + 1;
5953      }
5954    Make_Nil(cdr);
5955    Return_Unify_Pw(vlst, tlst, list.val, list.tag);
5956}
5957
5958#ifdef DUMPMAT
5959
5960int
5961dump_problem(lp_desc * lpd)
5962{
5963    int i;
5964    Fprintf(log_output_, "\n\
5965    	lpd->macsz = %d;\n\
5966    	lpd->marsz = %d;\n\
5967    	lpd->matnz = %d;\n\
5968    	lpd->mac = %d;\n\
5969    	lpd->mar = %d;\n\
5970	lpd->rhsx = a_rhsx;\n\
5971	lpd->senx = a_senx;\n\
5972	lpd->matbeg = a_matbeg;\n\
5973	lpd->matcnt = a_matcnt;\n\
5974	lpd->matind = a_matind;\n\
5975	lpd->matval = a_matval;\n\
5976	lpd->bdl = a_bdl;\n\
5977	lpd->bdu = a_bdu;\n\
5978	lpd->objx = a_objx;\n\
5979	lpd->ctype = a_ctype;",
5980      lpd->macsz, lpd->marsz, lpd->matnz,
5981      lpd->mac, lpd->mar);
5982
5983    Fprintf(log_output_, "\n\
5984	/*\n\
5985	 * Problem data\n\
5986	 */\n\
5987	 ");
5988    Fprintf(log_output_, "double a_objx[%d] ={\n", lpd->mac);
5989    for (i=0; i<lpd->mac; ++i)
5990	Fprintf(log_output_, "%.15e,\n", lpd->objx[i]);
5991    Fprintf(log_output_, "};\n\n");
5992
5993    Fprintf(log_output_, "double a_bdl[%d] ={\n", lpd->mac);
5994    for (i=0; i<lpd->mac; ++i)
5995	Fprintf(log_output_, "%.15e,\n", lpd->bdl[i]);
5996    Fprintf(log_output_, "};\n\n");
5997
5998    Fprintf(log_output_, "double a_bdu[%d] ={\n", lpd->mac);
5999    for (i=0; i<lpd->mac; ++i)
6000	Fprintf(log_output_, "%.15e,\n", lpd->bdu[i]);
6001    Fprintf(log_output_, "};\n\n");
6002
6003    Fprintf(log_output_, "int a_matbeg[%d] ={\n", lpd->mac);
6004    for (i=0; i<lpd->mac; ++i)
6005	Fprintf(log_output_, "%d,\n", lpd->matbeg[i]);
6006    Fprintf(log_output_, "};\n\n");
6007
6008    Fprintf(log_output_, "int a_matcnt[%d] ={\n", lpd->mac);
6009    for (i=0; i<lpd->mac; ++i)
6010	Fprintf(log_output_, "%d,\n", lpd->matcnt[i]);
6011    Fprintf(log_output_, "};\n\n");
6012
6013    Fprintf(log_output_, "char a_ctype[%d] ={\n", lpd->mac);
6014    for (i=0; i<lpd->mac; ++i)
6015	Fprintf(log_output_, "'%c',\n", lpd->ctype[i]);
6016    Fprintf(log_output_, "};\n\n");
6017
6018    Fprintf(log_output_, "double a_rhsx[%d] ={\n", lpd->mar);
6019    for (i=0; i<lpd->mar; ++i)
6020	Fprintf(log_output_, "%.15e,\n", lpd->rhsx[i]);
6021    Fprintf(log_output_, "};\n\n");
6022
6023    Fprintf(log_output_, "char a_senx[%d] ={\n", lpd->mar);
6024    for (i=0; i<lpd->mar; ++i)
6025	Fprintf(log_output_, "'%c',\n", lpd->senx[i]);
6026    Fprintf(log_output_, "};\n\n");
6027
6028    Fprintf(log_output_, "int a_matind[%d] ={\n", lpd->matnz);
6029    for (i=0; i<lpd->matnz; ++i)
6030	Fprintf(log_output_, "%d,\n", lpd->matind[i]);
6031    Fprintf(log_output_, "};\n\n");
6032
6033    Fprintf(log_output_, "double a_matval[%d] ={\n", lpd->matnz);
6034    for (i=0; i<lpd->matnz; ++i)
6035	Fprintf(log_output_, "%.15e,\n", lpd->matval[i]);
6036    Fprintf(log_output_, "};\n\n");
6037
6038    Fprintf(log_output_, "\n\
6039	/*\n\
6040	 * End data\n\
6041	 */\n\
6042	 ");
6043
6044# if 0
6045    Fprintf(log_output_, "\n\
6046    	lpd->cb_sz = %d;\n\
6047	lpd->cb_index = a_cb_index;\n\
6048	lpd->cb_index2 = a_cb_index2;\n\
6049	lpd->cb_value = a_cb_value;\n\
6050    	lpd->cb_cnt = %d;",
6051	lpd->cb_cnt, lpd->cb_cnt);
6052    Fprintf(log_output_, "int a_cb_index[%d] ={\n", lpd->cb_cnt);
6053    for (i=0; i<lpd->cb_cnt; ++i)
6054	Fprintf(log_output_, "%d,\n", lpd->cb_index[i]);
6055    Fprintf(log_output_, "};\n\n");
6056
6057    Fprintf(log_output_, "int a_cb_index2[%d] ={\n", lpd->cb_cnt);
6058    for (i=0; i<lpd->cb_cnt; ++i)
6059	Fprintf(log_output_, "%d,\n", lpd->cb_index2[i]);
6060    Fprintf(log_output_, "};\n\n");
6061
6062    Fprintf(log_output_, "double a_cb_value[%d] ={\n", lpd->cb_cnt);
6063    for (i=0; i<lpd->cb_cnt; ++i)
6064	Fprintf(log_output_, "%.15e,\n", lpd->cb_value[i]);
6065    Fprintf(log_output_, "};\n\n");
6066# endif
6067}
6068#endif
6069