/********************************************************
  Xpress-BCL Java Example Problems
  ================================

  file xbexpl1i.java
  ``````````````````
  BCL user guide example.
  Version using index sets.

  (c) 2008 Fair Isaac Corporation
      author: S.Heipcke, 2003, rev. Dec. 2011
********************************************************/

import java.io.*;
import com.dashoptimization.*;

public class xbexpl1i
{
 static final String DATAFILE = System.getProperty("XPRBDATA") + 
   "/jobs/durations.dat";

 static final int MAXNJ = 4;     /* Max. number of jobs */   
 static final int NT = 10;       /* Time limit */   

/**** DATA ****/
 static int NJ = 0;              /* Number of jobs read in */
 static double[] DUR;            /* Durations of jobs   */ 

 static XPRBindexSet Jobs;	 /* Job names */
 static XPRBvar[] start;         /* Start times of jobs  */ 
 static XPRBvar[][] delta;       /* Binaries for start times */  
 static XPRBvar z;               /* Maximum completion time (makespan) */ 

 static XPRB bcl;
 static XPRBprob p; 
             
/*************************************************************************/

    /**** 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 data from files ****/
 static void readData() throws IOException
 {
  FileReader datafile=null;
  StreamTokenizer st;
  int i;
 
                                   /* Create a new index set */
  Jobs = p.newIndexSet("Jobs", MAXNJ);
  DUR = new double[MAXNJ];
  
        /* Read the durations data file */
  datafile = new FileReader(DATAFILE);
  st = initST(datafile);
  do
  {
   do
   { 
    st.nextToken();
   } while(st.ttype==st.TT_EOL);    /* Skip empty lines */
   if(st.ttype != st.TT_WORD) break; 
   i=Jobs.addElement(st.sval);
   if(st.nextToken() != ',') break;
   if(st.nextToken() != st.TT_NUMBER) break;
   DUR[i] = st.nval;
   NJ+=1;
  } while( st.nextToken() == st.TT_EOL && NJ<MAXNJ);
  datafile.close();
  System.out.println("Number of jobs read: " + Jobs.getSize());
 }

/***********************************************************************/
 static void jobsModel()
 {
  XPRBexpr le;
  int j,t;

/****VARIABLES****/
  start = new XPRBvar[NJ];    /* Create start time variables (incl. bounds) */
  for(j=0;j<NJ;j++) start[j] = p.newVar("start", XPRB.PL, 0, NT-DUR[j]+1);
  z = p.newVar("z",XPRB.PL,0,NT);  /* Declare the makespan variable */

  delta = new XPRBvar[NJ][NT];
  for(j=0;j<NJ;j++)              /* Declare binaries for each job  */
   for(t=0;t<(NT-DUR[j]+1);t++)
    delta[j][t] = p.newVar("delta"+Jobs.getIndexName(j)+"_"+(t+1), XPRB.BV);

/****CONSTRAINTS****/
  for(j=0;j<NJ;j++)              /* Calculate maximal completion time */
   p.newCtr("Makespan", start[j].add(DUR[j]).lEql(z) );
  
  p.newCtr("Prec", start[0].add(DUR[0]).lEql(start[2]) );
                                 /* Precedence rel. between jobs */

  for(j=0;j<NJ;j++)              /* Linking start times and binaries  */ 
  { 
   le = new XPRBexpr();
   for(t=0;t<(NT-DUR[j]+1);t++)  le.add(delta[j][t].mul((t+1))); 
   p.newCtr("Link_"+(j+1), le.eql(start[j]) );
  } 
               
  for(j=0;j<NJ;j++)              /* One unique start time for each job  */
  { 
   le = new XPRBexpr();
   for(t=0;t<(NT-DUR[j]+1);t++)  le.add(delta[j][t]); 
   p.newCtr("One_"+(j+1), le.eql(1));
  }      
              
/****OBJECTIVE****/
  p.setObj(z);                   /* Define and set objective function */ 

/****BOUNDS****/
  for(j=0;j<NJ;j++) start[j].setUB(NT-DUR[j]+1); 
                                 /* Upper bounds on start time variables */
 } 

/*************************************************************************/
 static void jobsSolve()             
 { 
  int j,t,statmip; 

  for(j=0;j<NJ;j++)    
   for(t=0;t<NT-DUR[j]+1;t++)
    delta[j][t].setDir(XPRB.PR, 10*(t+1)); 
            /* Give highest priority to variables for earlier start times */

  p.setSense(XPRB.MINIM);
  p.mipOptimize("");              /* Solve the problem as MIP */
  statmip = p.getMIPStat();       /* Get the MIP problem status */    
              
  if((statmip == XPRB.MIP_SOLUTION) || (statmip == XPRB.MIP_OPTIMAL))
                                  /* An integer solution has been found */
  {  
   System.out.println("Objective: "+ p.getObjVal()); 
   for(j=0;j<NJ;j++) 
   {                              /* Print the solution for all start times */
    System.out.println(start[j].getName() + ": "+ start[j].getSol()); 
    for(t=0;t<NT-DUR[j]+1;t++) 
     System.out.print(delta[j][t].getName() + ": "+ delta[j][t].getSol() + " ");
    System.out.println();
   }
  } 
 }

/*************************************************************************/

 public static void main(String[] args)
 {         
  bcl = new XPRB();              /* Initialize BCL */
  p = bcl.newProb("Jobs");       /* Create a new problem */

  try
  {
   readData();                   /* Data input from file */
  }
  catch(IOException e)
  {
   System.err.println(e.getMessage());
   System.exit(1);
  }
  jobsModel();                   /* Problem definition */

  jobsSolve();                   /* Solve and print solution */

  p.finalize();                  /* Delete the problem */      
  p=null;
 }
}
