/********************************************************
  Xpress-BCL Java Example Problems
  ================================

  file xbcoco2.java
  `````````````````
  Coco Problem Phase 2.
  Use parameters, data tables and subscripted variables
  to separate the model structure from the data.
  Read data tables in from text data files.

  (c) 2008-2023 Fair Isaac Corporation
      author: S.Heipcke, Jan. 2000, rev. Mar. 2011
********************************************************/

import java.io.*;
import com.dashoptimization.*;

public class xbcoco2 {
    static final int NP = 2;        /* Number of products (p) */
    static final int NF = 2;        /*           factories (f) */
    static final int NR = 2;        /*           raw materials (r) */

    static final String REVFILE   = System.getProperty("XPRBDATA") +
        "/coco/rev.dat";
    static final String CMAKEFILE = System.getProperty("XPRBDATA") +
        "/coco/cmake.dat";
    static final String CBUYFILE  = System.getProperty("XPRBDATA") +
        "/coco/cbuy.dat";
    static final String REQFILE   = System.getProperty("XPRBDATA") +
        "/coco/req.dat";
    static final String MXSELLFILE= System.getProperty("XPRBDATA") +
        "/coco/maxsell.dat";
    static final String MXMAKEFILE= System.getProperty("XPRBDATA") +
        "/coco/mxmake.dat";

    /****TABLES****/
    static double[] REV;       /* Unit selling price of product p */
    static double[][] CMAK;    /* Unit cost to make product p at factory f */
    static double[] CBUY;      /* Unit cost to buy raw material r */
    static double[][] REQ;     /* Requirement by unit of prod. p for raw mat. r */
    static double[] MXSELL;    /* Max. amount of p that can be sold */
    static double[] MXMAKE;    /* Max. amount factory f can make over all prod.s */
    static double[][] PROFIT;  /* Profit contribution of product p at factory f */

    /***********************************************************************/

    static void modCoco2() {
        XPRBvar[][] make;
        XPRBexpr lobj, lc;
        int p,f;

        try (XPRBprob pb = new XPRBprob("Coco2")) { /* Initialize BCL and create a new problem */

            /****VARIABLES****/
            make = new XPRBvar[NP][NF];
            for(p=0;p<NP;p++)         /* Amount of prod. p to make at factory f */
                for(f=0;f<NF;f++)
                    make[p][f] = pb.newVar("make_"+ (p+1) +"f"+ (f+1));

            /****OBJECTIVE****/
            lobj = new XPRBexpr();
            for(p=0;p<NP;p++)         /* Objective: maximize total profit */
                for(f=0;f<NF;f++)  lobj.add(make[p][f].mul(PROFIT[p][f]));
            pb.setObj(lobj);

            /****CONSTRAINTS****/

            for(p=0;p<NP;p++) {          /* Limit on the amount of product p to be sold */
                lc = new XPRBexpr();
                for(f=0;f<NF;f++)  lc .add(make[p][f]);
                pb.newCtr("MxSell", lc.lEql(MXSELL[p]) );
            }

            for(f=0;f<NF;f++) {          /* Capacity limit at factory f */
                lc = new XPRBexpr();
                for(p=0;p<NP;p++) lc.add(make[p][f]);
                pb.newCtr("MxMake", lc.lEql(MXMAKE[f]) );
            }

            /****SOLVING + OUTPUT****/
            pb.setSense(XPRB.MAXIM);  /* Choose the sense of the optimization */
            pb.lpOptimize("");        /* Solve the LP-problem */
            System.out.println("Objective: " + pb.getObjVal()); /* Get objective value */

            for(p=0;p<NP;p++)         /* Print the solution values */
                for(f=0;f<NF;f++)
                    System.out.print(make[p][f].getName()+ ":"+ make[p][f].getSol() +" ");
            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 single line data file ****/
    static void readOneLineFile(String filename, double[] data) throws IOException {
        FileReader datafile=null;
        StreamTokenizer st;
        int i=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[i++] = st.nval;
                if(st.nextToken() != ',') break;
                st.nextToken();
            }
        } while( st.ttype == st.TT_EOL );
        datafile.close();
    }

    /**** 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 p,f,r;

        REV = new double[NP];
        CMAK = new double[NP][NF];
        CBUY = new double[NR];
        REQ = new double[NP][NR];
        MXSELL = new double[NP];
        MXMAKE = new double[NF];
        PROFIT = new double[NP][NF];

        /* Read the revenu data file */
        readOneLineFile(REVFILE,REV);

        /* Read the production cost data file */
        readMultiLineFile(CMAKEFILE,CMAK);

        /* Read the raw material cost data file */
        readOneLineFile(CBUYFILE,CBUY);

        /* Read the resource requirement data file */
        readMultiLineFile(REQFILE,REQ);

        /* Read the max. sales quantities data file */
        readOneLineFile(MXSELLFILE,MXSELL);

        /* Read the production capacities data file */
        readOneLineFile(MXMAKEFILE,MXMAKE);

        /* Calculate the table PROFIT */
        for(p=0;p<NP;p++)
            for(f=0;f<NF;f++) {
                PROFIT[p][f] = REV[p] - CMAK[p][f];
                for(r=0;r<NR;r++) PROFIT[p][f] -= (REQ[p][r]*CBUY[r]);
            }
    }

    /***********************************************************************/

    public static void main(String[] args) {
        try {
            readData();           /* Data input from file */
        }
        catch(IOException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        modCoco2();            /* Model and solve the problem */
    }
}
