/********************************************************
 * Xpress-BCL Java Example Problems
 * ================================
 *
 * file foliomip1.java
 * ```````````````````
 * Modeling a small LP problem
 * to perform portfolio optimization.
 * -- Limiting the total number of assets --
 *
 * (c) 2008-2024 Fair Isaac Corporation
 * author: S.Heipcke, 2003, rev. Dec. 2011
 ********************************************************/

import com.dashoptimization.*;

public class foliomip1 {
  static final int MAXNUM = 4; /* Max. number of different assets */
  static final int NSHARES = 10; /* Number of shares */
  static final int NRISK = 5; /* Number of high-risk 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[] RISK = {1, 2, 3, 8, 9}; /* High-risk values among shares */
  static final int[] NA = {0, 1, 2, 3}; /* Shares issued in N.-America */

  static final String[] MIPSTATUS = {
    "not loaded",
    "not optimized",
    "LP optimized",
    "unfinished (no solution)",
    "unfinished (solution found)",
    "infeasible",
    "optimal",
    "unbounded"
  };

  public static void main(String[] args) {
    try (XPRBprob p = new XPRBprob("FolioMIP1"); /* Initialize BCL and create a new problem */
        XPRBexprContext context =
            new XPRBexprContext() /* Release XPRBexpr instances at end of block. */) {
      int s;
      XPRBexpr Risk, Na, Return, Cap, Num;
      XPRBvar[] frac; /* Fraction of capital used per share */
      XPRBvar[] buy; /* 1 if asset is in portfolio, 0 otherwise */

      /* Create the decision variables */
      frac = new XPRBvar[NSHARES];
      buy = new XPRBvar[NSHARES];
      for (s = 0; s < NSHARES; s++) {
        frac[s] = p.newVar("frac", XPRB.PL, 0, 0.3);
        buy[s] = p.newVar("buy", XPRB.BV);
      }

      /* 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 */

      /* Limit the percentage of high-risk values */
      Risk = new XPRBexpr();
      for (s = 0; s < NRISK; s++) Risk.add(frac[RISK[s]]);
      p.newCtr(Risk.lEql(1.0 / 3));

      /* 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 the total number of assets */
      Num = new XPRBexpr();
      for (s = 0; s < NSHARES; s++) Num.add(buy[s]);
      p.newCtr(Num.lEql(MAXNUM));

      /* Linking the variables */
      for (s = 0; s < NSHARES; s++) p.newCtr(frac[s].lEql(buy[s]));

      /* Solve the problem */
      p.setSense(XPRB.MAXIM);
      p.mipOptimize("");

      System.out.println("Problem status: " + MIPSTATUS[p.getMIPStat()]);

      /* Solution printing */
      System.out.println("Total return: " + p.getObjVal());
      for (s = 0; s < NSHARES; s++)
        System.out.println(s + ": " + frac[s].getSol() * 100 + "% (" + buy[s].getSol() + ")");
    }
  }
}
