/********************************************************
  Xpress-BCL Java Example Problems
  ================================

  file xbcontr2s.java
  ```````````````````
  Contract allocation example.
  Combining BCL problem input with problem solving
  and callbacks in Xpress-Optimizer.
  -- Single MIP thread --

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, 2005, rev. Dec. 2011
********************************************************/

import java.io.*;
import com.dashoptimization.*;

public class xbcontr2s {
    static final int District = 6;   /* Number of districts */
    static final int Contract = 10;  /* Number of contracts */

    /**** DATA ****/
    static final int[] OUTPUT = {50, 40, 10, 20, 70, 50};
    /* Max. output per district */
    static final int[] COST   = {50, 20, 25, 30, 45, 40};
    /* Cost per district */
    static final int[] VOLUME = {20, 10, 30, 15, 20, 30, 10, 50, 10, 20};
    /* Volume of contracts */

    /***********************************************************************/

    static class IntSolCallback implements XPRSintSolListener {
        public void XPRSintSolEvent(XPRSprob oprob, Object data) {
            int num, d, c;
            XPRBprob bprob;
            XPRBvar y;

            try {
                bprob = (XPRBprob)data;
                num = oprob.getIntAttrib(XPRS.MIPSOLS);  /* Get number of the solution */
                bprob.sync(XPRB.XPRS_SOL);               /* Update BCL solution values */

                System.out.println("Solution " + num + ": Objective value: " +
                                   bprob.getObjVal());

                for(d=0;d<District;d++)
                    for(c=0;c<Contract;c++) {
                        y = bprob.getVarByName("q_d"+ (d+1) + "_c" + (c+1));
                        if( (y.getColNum()>-1) && (y.getSol() != 0))
                            System.out.println(y.getName() + ": " + y.getSol());
                    }
            }
            catch(XPRSprobException e) {
                System.out.println("Error " + e.getCode() + ": " + e.getMessage());
            }
        }
    }

    /***********************************************************************/

    public static void main(String[] args) throws XPRSexception {
        int d, c, i, stat, ncol, len;
        double [] sol;
        double val;
        java.lang.String [] names;
        XPRSprob op;
        IntSolCallback cb;
        XPRBexpr l1,l2,lobj;
        XPRBvar[][] x;                  /* Variables indicating whether a project
                                           is chosen */
        XPRBvar[][] y;                  /* Quantities allocated to contractors */

        try (XPRBprob p = new XPRBprob("Contract2"); /* Initialize BCL and create a new problem */
             XPRS xprs = new XPRS()) {               /* Initialize Xpress-Optimizer */

            /**** VARIABLES ****/
            x = new XPRBvar[District][Contract];
            y = new XPRBvar[District][Contract];
            for(d=0;d<District;d++)
                for(c=0;c<Contract;c++) {
                    x[d][c] = p.newVar("x_d"+ (d+1) +"_c" +(c+1), XPRB.BV);
                    y[d][c] = p.newVar("q_d"+ (d+1) +"_c" +(c+1), XPRB.SC, 0, OUTPUT[d]);
                    y[d][c].setLim(5);
                }

            /****OBJECTIVE****/
            lobj = new XPRBexpr();
            for(d=0;d<District;d++)
                for(c=0;c<Contract;c++)
                    lobj.add(y[d][c].mul(COST[d]));

            p.setObj(lobj);                  /* Set the objective function */

            /**** CONSTRAINTS ****/
            for(c=0;c<Contract;c++) {
                l1 = new XPRBexpr();
                l2 = new XPRBexpr();
                for(d=0;d<District;d++) {
                    l1.add(y[d][c]);
                    l2.add(x[d][c]);
                }
                p.newCtr("Size", l1.gEql(VOLUME[c]));  /* "Size": cover the req. volume */
                p.newCtr("Min", l2.gEql(2) );  /* "Min": at least 2 districts / contract */
            }

            for(d=0;d<District;d++) {          /* Do not exceed max. output  */
                l1 = new XPRBexpr();
                for(c=0;c<Contract;c++)
                    l1.add(y[d][c]);
                p.newCtr("Output", l1.lEql(OUTPUT[d]) );
            }

            for(d=0;d<District;d++)         /* If a contract is allocated to a district,
                                               then at least 1 unit is allocated to it */
                for(c=0;c<Contract;c++)
                    p.newCtr("XY", x[d][c].lEql(y[d][c]) );

            /****SOLVING + OUTPUT****/
            /* Desactivate parallel MIP (for synchronization of BCL and Optimizer) */
            p.getXPRSprob().setIntControl(XPRS.MIPTHREADS, 1);

            cb = new IntSolCallback();
            p.getXPRSprob().addIntSolListener(cb,p);
            /* Define an integer solution callback */
            p.mipOptimize("");              /* Solve the MIP problem */

        }

    }
}
