/********************************************************
 * Xpress-BCL Java Example Problems
 * ================================
 *
 * file xbdlvriis.java
 * ```````````````````
 * Transportation problem (infeasible data).
 * Retrieving and printing IIS.
 *
 * (c) 2008-2024 Fair Isaac Corporation
 * author: S.Heipcke, 2005, rev. Mar. 2011
 ********************************************************/

import com.dashoptimization.*;
import java.io.*;
import java.util.*;

public class xbdlvriis {
  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 void modDelivery() {
    try (XPRBprob p = new XPRBprob("Delivery"); /* Initialize BCL and create a new problem */
        XPRBexprContext context =
            new XPRBexprContext() /* Release XPRBexpr instances at end of block. */) {
      XPRBexpr lobj, lc;
      int s, c, i;
      XPRBvar[][] x;
      ArrayList iisctr, iisvar;
      int numv, numc, numiis, ct;

      /****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);

      /****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****/
      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]);
        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]);
        p.newCtr("Demand", lc.gEql(DEMAND[c]));
      }

      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]);
        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]);
        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) {
        numiis = p.getNumIIS(); /* Get the number of independent IIS */
        System.out.println("Number of IIS: " + numiis);

        iisvar = new ArrayList();
        iisctr = new ArrayList();

        for (s = 1; s <= numiis; s++) {
          p.getIIS(iisvar, iisctr, s);
          numv = iisvar.size();
          numc = iisctr.size();
          System.out.println("IIS " + s + ":  " + numv + " variables, " + numc + " constraints");
          if (numv > 0) {
              /* Print all variables in the IIS */
            System.out.print("        Variables: ");
            for (i = 0; i < numv; i++) System.out.print(((XPRBvar) iisvar.get(i)).getName() + " ");
            System.out.println();
          }
          if (numc > 0) {
              /* Print all constraints in the IIS */
            System.out.print("        Constraints: ");
            for (i = 0; i < numc; i++) System.out.print(((XPRBctr) iisctr.get(i)).getName() + " ");
            System.out.println();
          }
        }

        /* Alternative way of enumerating IIS: */
        p.getIIS(iisvar, iisctr, 0);
        numv = iisvar.size();
        numc = iisctr.size();
        System.out.println(
            "IIS approximation:  "
                + numv
                + " variables, "
                + numc
                + " constraints with non-zero reduced cost/dual values");
        ct = 0;
        while (numv + numc > 0) {
          if (ct > 0)
            System.out.println("IIS " + ct + ":  " + numv + " variables, " + numc + " constraints");
          ct++;
          p.getIIS(iisvar, iisctr, ct);
          numv = iisvar.size();
          numc = iisctr.size();
        }

        /* Retrieve variables only */
        System.out.println("Variables");
        for (s = 1; s <= numiis; s++) {
          p.getIIS(iisvar, null, s);
          numv = iisvar.size();
          System.out.print("  IIS " + s + ":  " + numv + " variables  ( ");
          for (i = 0; i < numv; i++) System.out.print(((XPRBvar) iisvar.get(i)).getName() + " ");
          System.out.println(")");
        }

        /* Retrieve constraints only */
        System.out.println("Constraints");
        for (s = 1; s <= numiis; s++) {
          p.getIIS(null, iisctr, s);
          numc = iisctr.size();
          System.out.print("  IIS " + s + ":  " + numc + " constraints  ( ");
          for (i = 0; i < numc; i++) System.out.print(((XPRBctr) iisctr.get(i)).getName() + " ");
          System.out.println(")");
        }
      }
    }
  }

  /***********************************************************************/

  /**** 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 */
  }
}
