/********************************************************
  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-2023 Fair Isaac Corporation
      author: S.Heipcke, Jan. 2008, rev. Mar. 2011
********************************************************/

import java.io.*;
import java.util.*;
import java.text.DecimalFormat;
import com.dashoptimization.*;

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() {
        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"};

        try (XPRBprob p = new XPRBprob("Delivery"); /* Initialize BCL and create a new problem in BCL */
             XPRS xprs = new XPRS()) {              /* Initialize Xpress-Optimizer */

            /****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 */
    }
}
