/********************************************************
 * Xpress-BCL Java Example Problems
 * ================================
 *
 * file folioqc.java
 * `````````````````
 * Modeling a small QCQP problem
 * to perform portfolio optimization.
 * -- Maximize return with limit on variance ---
 *
 * (c) 2008-2024 Fair Isaac Corporation
 * author: S.Heipcke, July 2008, rev. Dec. 2011
 ********************************************************/

import com.dashoptimization.*;
import java.io.*;

public class folioqc {
  static final String DATAFILE = System.getProperty("XPRBDATA") + "/GS/foliocppqp.dat";

  static final double MAXVAR = 0.55; /* Max. allowed variance */
  static final int NSHARES = 10; /* Number of shares */
  static final int NNA = 4; /* Number of North-American shares */

  static final double[] RET = {5, 17, 26, 12, 8, 9, 7, 6, 31, 21};
  /* Estimated return in investment */
  static final int[] NA = {0, 1, 2, 3}; /* Shares issued in N.-America */
  static double[][] VAR; /* Variance/covariance matrix of
                                            estimated returns */

  private static void readData() throws IOException {
    int s, t;
    FileReader datafile = null;
    StreamTokenizer st = null;

    VAR = new double[NSHARES][NSHARES];

    /* Read `VAR' data from file */
    datafile = new FileReader(DATAFILE); /* Open the data file */
    st = new StreamTokenizer(datafile); /* Initialize the stream tokenizer */
    st.commentChar('!'); /* Use the character '!' for comments */
    st.eolIsSignificant(true); /* Return end-of-line character */
    st.parseNumbers(); /* Read numbers as numbers (not strings) */

    for (s = 0; s < NSHARES; s++) {
      do {
        st.nextToken();
      } while (st.ttype == st.TT_EOL); /* Skip empty lines and comment lines */
      for (t = 0; t < NSHARES; t++) {
        if (st.ttype != st.TT_NUMBER) break;
        VAR[s][t] = st.nval;
        st.nextToken();
      }
    }

    datafile.close();
  }

  public static void main(String[] args) throws Exception {
    try (XPRBprob p = new XPRBprob("FolioQC"); /* Initialize BCL and create a new problem */
        XPRBexprContext context =
            new XPRBexprContext() /* Release XPRBexpr instances at end of block. */) {
      int s, t;
      XPRBexpr Risk, Na, Return, Cap, Num, Variance;
      XPRBvar[] frac; /* Fraction of capital used per share */

      readData(); /* Read data from file */

      /* Create the decision variables */
      frac = new XPRBvar[NSHARES];
      for (s = 0; s < NSHARES; s++) frac[s] = p.newVar("frac", XPRB.PL, 0, 0.3);

      /* Objective: total return */
      Return = new XPRBexpr();
      for (s = 0; s < NSHARES; s++) Return.add(frac[s].mul(RET[s]));
      p.setObj(Return); /* Set the objective function */

      /* Minimum amount of North-American values */
      Na = new XPRBexpr();
      for (s = 0; s < NNA; s++) Na.add(frac[NA[s]]);
      p.newCtr(Na.gEql(0.5));

      /* Spend all the capital */
      Cap = new XPRBexpr();
      for (s = 0; s < NSHARES; s++) Cap.add(frac[s]);
      p.newCtr(Cap.eql(1));

      /* Limit variance */
      Variance = new XPRBexpr();
      for (s = 0; s < NSHARES; s++)
        for (t = 0; t < NSHARES; t++) Variance.add(frac[s].mul(frac[t]).mul(VAR[s][t]));
      p.newCtr(Variance.lEql(MAXVAR));

      /* Solve the problem */
      p.setSense(XPRB.MAXIM);
      p.lpOptimize("");

      /* Solution printing */
      System.out.println("With a max. variance of " + MAXVAR + " total return is " + p.getObjVal());
      for (s = 0; s < NSHARES; s++) System.out.println(s + ": " + frac[s].getSol() * 100 + "%");
    }
  }
}
