/********************************************************
 * Xpress-BCL Java Example Problems
 * ================================
 *
 * file xbcontr2.java
 * ``````````````````
 * Contract allocation example.
 * Combining BCL problem input with problem solving
 * and callbacks in Xpress-Optimizer.
 *
 * (c) 2008-2024 Fair Isaac Corporation
 * author: S.Heipcke, 2005, rev. Dec. 2011
 ********************************************************/

import com.dashoptimization.*;
import java.io.*;

public class xbcontr2 {
  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;
        bprob.beginCB(oprob);
        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());
          }
        bprob.endCB();
      } catch (XPRSprobException e) {
        System.out.println("Error " + e.getCode() + ": " + e.getMessage());
      }
    }
  }

  /***********************************************************************/

  public static void main(String[] args) throws XPRSexception {
    try (XPRBprob p = new XPRBprob("Contract2"); /* Initialize BCL and create a new problem BCL */
         XPRBexprContext context =
         new XPRBexprContext(); /* Release XPRBexpr instances at end of block. */
         XPRS xprs = new XPRS() /* Initialize Xpress-Optimizer */) {
      int d, c, i, stat, ncol, len;
      double[] sol;
      double val;
      java.lang.String[] names;
      XPRSprob op;
      IntSolCallback cb;
      XPRBvar[][] x; /* Variables indicating whether a project
                        is chosen */
      XPRBvar[][] y; /* Quantities allocated to contractors */
      XPRBexpr l1, l2, lobj;

      /**** 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****/
      cb = new IntSolCallback();
      p.getXPRSprob().addIntSolListener(cb, p);
      /* Define an integer solution callback */
      p.mipOptimize(""); /* Solve the MIP problem */
    }
  }
}
