/********************************************************
 * Xpress-BCL Java Example Problems
 * ================================
 *
 * file xbdlvriis2iso.java
 * ```````````````````````
 * Transportation problem (infeasible data).
 * Retrieving and printing IIS.
 * - Using Optimizer functions to retrieve detailed
 * IIS information including isolation rows/bounds -
 *
 * (c) 2008-2024 Fair Isaac Corporation
 * author: S.Heipcke, Jan. 2008, rev. Mar. 2011
 ********************************************************/

import com.dashoptimization.*;
import java.io.*;
import java.text.DecimalFormat;
import java.util.*;

public class xbdlvriis2iso {
  static final int NSupp = 10; /* Number of suppliers */
  static final int NCust = 7; /* Number of customers */
  static final int MaxArcs = 100; /* Max. num. of non-zero cost values */

  static final String VANFILE = System.getProperty("XPRBDATA") + "/delivery/ifvan.dat";
  static final String COSTFILE = System.getProperty("XPRBDATA") + "/delivery/cost.dat";

  /****DATA****/
  /* Supplier:                    London  Luton  B'ham Bristl  Derby Stckpt */
  static final double SUPPLY[] = {
    140.0,
    200.0,
    50.0,
    10.0,
    400.0,
    200.0,
    /* Supplier:                     York  Derby Soton Scnthp */
    20.0,
    90.0,
    30.0,
    12.0
  };

  /* Customer:                     London Livpol Doncst   York   Hull  Manchr */
  static final double DEMAND[] = {
    1230.3,
    560.4,
    117.1,
    592.8,
    310.0,
    1247.0,
    /* Customer:                     Shffld */
    86.0
  };

  static double[][] COST; /* Cost per supplier-customer pair */
  static double[][] IFVAN; /* Non-zero if route uses vans instead
                                        of lorries */
  static final double VANCAP = 40.0; /* Capacity on routes that use vans */

  static XPRSprob op;

  private static DecimalFormat form = new DecimalFormat("####.0");

  /***********************************************************************/

  static void modDelivery() {
    try (XPRBprob p =
            new XPRBprob("Delivery"); /* Initialize BCL and create a new problem in BCL */
        XPRBexprContext context =
            new XPRBexprContext(); /* Release XPRBexpr instances at end of block. */
        XPRS xprs = new XPRS()) {
        /* Initialize Xpress-Optimizer */
      XPRBexpr lobj, lc;
      int s, c, i;
      XPRBvar[][] x;
      XPRBctr[] CSupply, CDemand;
      int numv, numc, numiis, ncol, nrow;
      IntHolder inumv, inumc, inumiis;
      int[] viis, ciis;
      double[] duals, rdcs, bnd, rhs;
      byte[] ctrtype, bndtype, isolationrows, isolationbnds;
      String[] cnames, vnames;
      String isotype[] = {"N/A", "No ", "Yes"};

      /****VARIABLES****/
      x = new XPRBvar[NSupp][NCust];
      for (s = 0; s < NSupp; s++)
        for (c = 0; c < NCust; c++) x[s][c] = p.newVar("x_s" + s + "_" + c);

      /****OBJECTIVE****/
      lobj = new XPRBexpr();
      for (s = 0; s < NSupp; s++) /* Objective: Minimize total cost */
        for (c = 0; c < NCust; c++) lobj.add(x[s][c].mul(COST[s][c]));
      p.setObj(lobj); /* Set objective function */

      /****CONSTRAINTS****/
      CDemand = new XPRBctr[NCust];
      for (c = 0; c < 4; c++) {
          /* Satisfy demand of each customer */
        lc = new XPRBexpr();
        for (s = 0; s < 5; s++) lc.add(x[s][c]);
        CDemand[c] = p.newCtr("Demand", lc.gEql(DEMAND[c]));
      }
      for (c = 4; c < NCust; c++) {
          /* Satisfy demand of each customer */
        lc = new XPRBexpr();
        for (s = 5; s < NSupp; s++) lc.add(x[s][c]);
        CDemand[c] = p.newCtr("Demand", lc.gEql(DEMAND[c]));
      }

      CSupply = new XPRBctr[NSupp];
      for (s = 0; s < 5; s++) {
          /* Keep within supply at each supplier*/
        lc = new XPRBexpr();
        for (c = 0; c < 4; c++) lc.add(x[s][c]);
        CSupply[s] = p.newCtr("Supply", lc.lEql(SUPPLY[s]));
      }
      for (s = 5; s < NSupp; s++) {
          /* Keep within supply at each supplier*/
        lc = new XPRBexpr();
        for (c = 4; c < NCust; c++) lc.add(x[s][c]);
        CSupply[s] = p.newCtr("Supply", lc.lEql(SUPPLY[s]));
      }

      /****BOUNDS****/
      for (s = 0; s < NSupp; s++)
        for (c = 0; c < NCust; c++) if (IFVAN[s][c] != 0) x[s][c].setUB(VANCAP);

      /****SOLVING + OUTPUT****/
      p.setSense(XPRB.MINIM); /* Set objective sense to minimization */
      p.lpOptimize(""); /* Solve the LP-problem */

      System.out.println("LP status: " + p.getLPStat());
      if (p.getLPStat() == XPRB.LP_OPTIMAL)
        System.out.println("Objective: " + p.getObjVal()); /* Get objective value */
      else if (p.getLPStat() == XPRB.LP_INFEAS) {
        op = p.getXPRSprob(); /* Retrieve the Optimizer problem */

        /**** Get all IIS ****/
        op.IISAll(); /* Generate all IIS */
        /* Get the number of independent IIS  */
        numiis = op.getIntAttrib(XPRS.NUMIIS);
        System.out.println("Number of IIS: " + numiis);

        /**** Obtain variable and constraint names for later use in printout ****/
        /* Retrieve variable names */
        ncol = op.getIntAttrib(XPRS.ORIGINALCOLS);
        vnames = new java.lang.String[ncol];
        op.getNames(XPRS.NAMES_COLUMN, vnames, 0, ncol - 1);
        /* Retrieve constraint names */
        nrow = op.getIntAttrib(XPRS.ORIGINALROWS);
        cnames = new java.lang.String[nrow];
        op.getNames(XPRS.NAMES_ROW, cnames, 0, nrow - 1);

        /**** Retrieve detailed IIS info (incl. isolations) ****/
        inumc = new IntHolder();
        inumv = new IntHolder();
        for (s = 1; s <= numiis; s++) {
          op.getIISData(s, inumc, inumv, null, null, null, null, null, null, null, null);
          op.IISIsolations(s);

          numv = inumv.value;
          numc = inumc.value;
          ciis = new int[numc];
          viis = new int[numv];
          duals = new double[numc];
          rdcs = new double[numv];
          ctrtype = new byte[numc];
          bndtype = new byte[numv];
          isolationrows = new byte[numc];
          isolationbnds = new byte[numv];

          op.getIISData(
              s,
              inumc,
              inumv,
              ciis,
              viis,
              ctrtype,
              bndtype,
              duals,
              rdcs,
              isolationrows,
              isolationbnds);
          System.out.println("IIS " + s + ":  " + numv + " variables, " + numc + " constraints");
          System.out.println("  Name    Type   Sense   Bound   Dual values   In iso. ");
          if (numv > 0) {
              /* Print all variables in the IIS */
            bnd = new double[1];
            for (i = 0; i < numv; i++) {
              if (bndtype[i] == 'L') op.getLB(bnd, viis[i], viis[i]);
              else op.getUB(bnd, viis[i], viis[i]);
              System.out.print(vnames[viis[i]] + "   column    " + bndtype[i] + "     ");
              System.out.println(
                  bnd[0] + "       " + rdcs[i] + "         " + isotype[1 + (int) isolationbnds[i]]);
            }
          }
          if (numc > 0) {
              /* Print all constraints in the IIS */
            rhs = new double[1];
            for (i = 0; i < numc; i++) {
              op.getRHS(rhs, ciis[i], ciis[i]);
              System.out.print(cnames[ciis[i]] + "   row     " + ctrtype[i] + "     ");
              System.out.println(
                  form.format(rhs[0])
                      + "     "
                      + form.format(duals[i])
                      + "         "
                      + isotype[1 + (int) isolationrows[i]]);
            }
          }
        }
      }
    }
  }

  /***********************************************************************/

  /**** Initialize the stream tokenizer ****/
  static StreamTokenizer initST(FileReader file) {
    StreamTokenizer st = null;

    st = new StreamTokenizer(file); /* Initialize the stream tokenizer */
    st.commentChar('!'); /* Use the character '!' for comments */
    st.eolIsSignificant(true); /* Return end-of-line character */
    st.ordinaryChar(','); /* Use ',' as separator */
    st.parseNumbers(); /* Read numbers as numbers (not strings)*/
    return st;
  }

  /**** Read multi line data file ****/
  static void readMultiLineFile(String filename, double[][] data) throws IOException {
    FileReader datafile = null;
    StreamTokenizer st;
    int i = 0, j = 0;

    datafile = new FileReader(filename);
    st = initST(datafile);
    do {
      do {
        st.nextToken();
      } while (st.ttype == st.TT_EOL); /* Skip empty lines */
      while (st.ttype == st.TT_NUMBER) {
        data[j][i++] = st.nval;
        if (st.nextToken() != ',') {
          j++;
          i = 0;
          break;
        }
        st.nextToken();
      }
    } while (st.ttype == st.TT_EOL);
    datafile.close();
  }

  /**** Read data from files ****/
  static void readData() throws IOException {
    int s, c;

    COST = new double[NSupp][NCust];
    IFVAN = new double[NSupp][NCust];

    /* Initialize data tables to 0: in the van data file some entries that are
     * zero are simply left out (that is, the lines are incomplete) */
    for (s = 0; s < NSupp; s++)
      for (c = 0; c < NCust; c++) {
        COST[s][c] = 0;
        IFVAN[s][c] = 0;
      }
    /* Read the demand data file */
    readMultiLineFile(COSTFILE, COST);

    /* Read the van data file */
    readMultiLineFile(VANFILE, IFVAN);
  }

  /***********************************************************************/

  public static void main(String[] args) {
    try {
      readData(); /* Data input from file */
    } catch (IOException e) {
      System.err.println(e.getMessage());
      System.exit(1);
    }
    modDelivery(); /* Problem formulation and solution */
  }
}
