/********************************************************
  Xpress-BCL Java Example Problems
  ================================

  file xbcoco3.java
  `````````````````
  Coco Problem Phase 3.
  Introduce time periods and inventory.

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, Jan. 2000, rev. Mar. 2011
********************************************************/

import java.io.*;
import com.dashoptimization.*;

public class xbcoco3 {
    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 int NT = 4;    /*           time periods (t) */

    static final String REVFILE     = System.getProperty("XPRBDATA") +
        "/coco/revt.dat";
    static final String CMAKEFILE   = System.getProperty("XPRBDATA") +
        "/coco/cmake.dat";
    static final String CBUYFILE    = System.getProperty("XPRBDATA") +
        "/coco/cbuyt.dat";
    static final String REQFILE     = System.getProperty("XPRBDATA") +
        "/coco/req.dat";
    static final String MXSELLFILE  = System.getProperty("XPRBDATA") +
        "/coco/maxsellt.dat";
    static final String MXMAKEFILE  = System.getProperty("XPRBDATA") +
        "/coco/mxmake.dat";
    static final String PSTOCK0FILE = System.getProperty("XPRBDATA") +
        "/coco/pstock0.dat";
    static final String RSTOCK0FILE = System.getProperty("XPRBDATA") +
        "/coco/rstock0.dat";

    /****TABLES****/
    static double[][] REV;     /* Unit selling price of product p in period t */
    static double[][] CMAK;    /* Unit cost to make product p at factory f */
    static double[][] CBUY;    /* Unit cost to buy raw material r in period t */
    static double[][] REQ;     /* Requirement by unit of prod. p for raw mat. r */
    static double[][] MXSELL;  /* Max. amount of p that can be sold in period t */
    static double[] MXMAKE;    /* Max. amount factory f can make over all prod.s */
    static double[][] PSTOCK0; /* Initial product p stock level at factory f */
    static double[][] RSTOCK0; /* Initial raw material r stock level at factory f*/

    /****DATA****/
    static final double CPSTOCK = 2.0;   /* Unit cost to store any product p */
    static final double CRSTOCK = 1.0;   /* Unit cost to store any raw mat. r */
    static final double MXRSTOCK = 300;  /* Max. amount of r that can be stored
                                            each f and t */
    /***********************************************************************/

    static void modCoco3() {
        XPRBvar[][][] make, sell, pstock, buy, rstock;
        XPRBexpr lobj, lc;
        int p,f,r,t;

        try (XPRBprob pb = new XPRBprob("Coco3")) { /* Initialize BCL and create a new problem */

            /****VARIABLES****/
            make = new XPRBvar[NP][NF][NT];
            sell = new XPRBvar[NP][NF][NT];
            pstock = new XPRBvar[NP][NF][NT+1];
            buy = new XPRBvar[NR][NF][NT];
            rstock = new XPRBvar[NR][NF][NT+1];
            for(p=0;p<NP;p++)
                for(f=0;f<NF;f++) {
                    for(t=0;t<NT;t++) {
                        make[p][f][t] = pb.newVar("make_p" + (p+1) + "_f" + (f+1));
                        /* Amount of prod. p to make at factory f in period t */
                        sell[p][f][t] = pb.newVar("sell_p" + (p+1) + "_f" + (f+1));
                        /* Amount of prod. p sold from factory f in period t */
                    }
                    for(t=0;t<NT+1;t++)
                        pstock[p][f][t] = pb.newVar("pstock_p" + (p+1) + "_f" + (f+1));
                    /* Stock level of prod. p at factory f at start of period t */
                }
            for(r=0;r<NR;r++)
                for(f=0;f<NF;f++) {
                    for(t=0;t<NT;t++)
                        buy[r][f][t] = pb.newVar("buy_r" + (r+1) + "_f" + (f+1));
                    /* Amount of raw material r bought for factory f in period t */
                    for(t=0;t<NT+1;t++)
                        rstock[r][f][t] = pb.newVar("rstock_r" + (r+1) + "_f" + (f+1));
                    /* Stock level of raw mat. r at factory f at start of per. t */
                }

            /****OBJECTIVE****/
            lobj = new XPRBexpr();
            for(f=0;f<NF;f++) {          /* Objective: maximize total profit */
                for(p=0;p<NP;p++) {
                    for(t=0;t<NT;t++)
                        lobj.add(sell[p][f][t].mul(REV[p][t]))
                            .add(make[p][f][t].mul(-CMAK[p][f]));
                    for(t=1;t<NT+1;t++)  lobj.add(pstock[p][f][t].mul(-CPSTOCK));
                }
                for(r=0;r<NR;r++) {
                    for(t=0;t<NT;t++)  lobj.add(buy[r][f][t].mul(-CBUY[r][t]));
                    for(t=1;t<NT+1;t++)  lobj.add(rstock[r][f][t].mul(-CRSTOCK));
                }
            }
            pb.setObj(lobj);          /* Set objective function */

            /****CONSTRAINTS****/
            for(p=0;p<NP;p++)         /* Product stock balance */
                for(f=0;f<NF;f++)
                    for(t=0;t<NT;t++)
                        pb.newCtr("PBal", pstock[p][f][t].add(make[p][f][t])
                                  .eql(sell[p][f][t].add(pstock[p][f][t+1])) );

            for(r=0;r<NR;r++)          /* Raw material stock balance */
                for(f=0;f<NF;f++)
                    for(t=0;t<NT;t++) {
                        lc = new XPRBexpr();
                        for(p=0;p<NP;p++)  lc.add(make[p][f][t].mul(REQ[p][r]));
                        pb.newCtr("RBal", rstock[r][f][t].add(buy[r][f][t])
                                  .eql(lc.add(rstock[r][f][t+1])) );
                    }

            for(p=0;p<NP;p++)
                for(t=0;t<NT;t++)
                    {                         /* Limit on the amount of product p to be sold */
                        lc = new XPRBexpr();
                        for(f=0;f<NF;f++)  lc.add(sell[p][f][t]);
                        pb.newCtr("MxSell", lc.lEql(MXSELL[p][t]) );
                    }

            for(f=0;f<NF;f++)
                for(t=0;t<NT;t++)
                    {                         /* Capacity limit at factory f */
                        lc = new XPRBexpr();
                        for(p=0;p<NP;p++)  lc.add(make[p][f][t]);
                        pb.newCtr("MxMake", lc.lEql(MXMAKE[f]) );
                    }

            for(f=0;f<NF;f++)
                for(t=1;t<NT+1;t++)
                    {                         /* Raw material stock limit */
                        lc = new XPRBexpr();
                        for(r=0;r<NR;r++) lc.add(rstock[r][f][t]);
                        pb.newCtr("MxRStock", lc.lEql(MXRSTOCK) );
                    }

            /****BOUNDS****/
            for(p=0;p<NP;p++)
                for(f=0;f<NF;f++)
                    pstock[p][f][0].fix(PSTOCK0[p][f]);    /* Initial product levels */

            for(r=0;r<NR;r++)
                for(f=0;f<NF;f++)
                    rstock[r][f][0].fix(RSTOCK0[r][f]);    /* Initial raw mat. levels */

            /****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 */

            /* Uncomment to print out the solution values */
            /* for(p=0;p<NP;p++)
               for(f=0;f<NF;f++) {
               for(t=0;t<NT;t++)
               System.out.print(make[p][f][t].getName() + ":" + make[p][f][t].getSol()
               + " " + sell[p][f][t].getName() +":" + sell[p][f][t].getSol());
               for(t=0;t<NT+1;t++)
               System.out.print(pstock[p][f][t].getName() +":" + pstock[p][f][t].getSol() + " ");
               System.out.println();
               }

               for(r=0;r<NR;r++)
               for(f=0;f<NF;f++) {
               for(t=0;t<NT;t++)
               System.out.print(buy[r][f][t].getName() +":" + buy[r][f][t].getSol()
               + " ");
               for(t=0;t<NT+1;t++)
               System.out.print(rstock[r][f][t].getName() +":" + rstock[r][f][t].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,r;

        REV = new double[NP][NT];
        CMAK = new double[NP][NF];
        CBUY = new double[NR][NT];
        REQ = new double[NP][NR];
        MXSELL = new double[NP][NT];
        MXMAKE = new double[NF];
        PSTOCK0 = new double[NP][NF];
        RSTOCK0 = new double[NR][NF];

        /* Read the revenu data file */
        readMultiLineFile(REVFILE,REV);

        /* Read the production cost data file */
        readMultiLineFile(CMAKEFILE,CMAK);

        /* Read the raw material cost data file */
        readMultiLineFile(CBUYFILE,CBUY);

        /* Read the resource requirement data file */
        readMultiLineFile(REQFILE,REQ);

        /* Read the max. sales quantities data file */
        readMultiLineFile(MXSELLFILE,MXSELL);

        /* Read the production capacities data file */
        readOneLineFile(MXMAKEFILE,MXMAKE);

        /* Read the product stock data file */
        readMultiLineFile(PSTOCK0FILE,PSTOCK0);

        /* Read the raw material stock data file */
        readMultiLineFile(RSTOCK0FILE,RSTOCK0);
    }

    /***********************************************************************/

    public static void main(String[] args) {
        try {
            readData();           /* Data input from file */
        }
        catch(IOException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        modCoco3();            /* Model and solve the problem */
    }
}
