/*
   Copyright: Copyright (c) MOSEK ApS, Denmark. All rights reserved.

   File:      production.cs

   Purpose:   Demonstrates how to solve a  linear
              optimization problem using the MOSEK API
              and and modify and re-optimize the problem.
*/

using System;

namespace mosek.example
{
  public class production
  {
    public static void Main ()
    {
      /*TAG:begin-setup-problem*/
      // Since the value infinity is never used, we define
      // 'infinity' symbolic purposes only
      double
      infinity = 0;

      const int numcon = 3;
      const int numvar = 3;

      double[] c            = {1.5,
                               2.5,
                               3.0
                              };
      mosek.boundkey[] bkc  = {mosek.boundkey.up,
                               mosek.boundkey.up,
                               mosek.boundkey.up
                              };
      double[] blc          = { -infinity,
                                -infinity,
                                -infinity
                              };
      double[] buc          =  {100000,
                                50000,
                                60000
                               };
      mosek.boundkey[] bkx  = {mosek.boundkey.lo,
                               mosek.boundkey.lo,
                               mosek.boundkey.lo
                              };
      double[] blx           = {0.0,
                                0.0,
                                0.0
                               };
      double[] bux           = { +infinity,
                                 +infinity,
                                 +infinity
                               };

      int[][] asub = new int[numvar][];
      asub[0] = new int[] {0, 1, 2};
      asub[1] = new int[] {0, 1, 2};
      asub[2] = new int[] {0, 1, 2};

      double[][] aval   = new double[numvar][];
      aval[0] = new double[] { 2.0, 3.0, 2.0 };
      aval[1] = new double[] { 4.0, 2.0, 3.0 };
      aval[2] = new double[] { 3.0, 3.0, 2.0 };

      double[] xx  = new double[numvar];

      mosek.Task task = null;
      mosek.Env  env  = null;

      try
      {
        // Create mosek environment.
        env  = new mosek.Env ();
        // Create a task object linked with the environment env.
        task = new mosek.Task (env, numcon, numvar);

        /* Append the constraints. */
        task.appendcons(numcon);

        /* Append the variables. */
        task.appendvars(numvar);

        /* Put C. */
        task.putcfix(0.0);
        for (int j = 0; j < numvar; ++j)
          task.putcj(j, c[j]);

        /* Put constraint bounds. */
        for (int i = 0; i < numcon; ++i)
          task.putbound(mosek.accmode.con, i, bkc[i], blc[i], buc[i]);

        /* Put variable bounds. */
        for (int j = 0; j < numvar; ++j)
          task.putbound(mosek.accmode.var, j, bkx[j], blx[j], bux[j]);

        /* Put A. */
        if ( numcon > 0 )
        {
          for (int j = 0; j < numvar; ++j)
            task.putacol(j,
                         asub[j],
                         aval[j]);
        }

        task.putobjsense(mosek.objsense.maximize);

        try
        {
          task.optimize();
        }
        catch (mosek.Warning w)
        {
          Console.WriteLine("Mosek warning:");
          Console.WriteLine (w.Code);
          Console.WriteLine (w);
        }

        task.getxx(mosek.soltype.bas, // Request the basic solution.
                   xx);

        for (int j = 0; j < numvar; ++j)
          Console.WriteLine ("x[{0}]:{1}", j, xx[j]);

        /*TAG:end-setup-problem*/
        /* Make a change to the A matrix */
        /*TAG:begin-putaij*/
        task.putaij(0, 0, 3.0);
        /*TAG:end-putaij*/
        /*TAG:begin-reoptimize1*/
        task.optimize();
        /*TAG:end-reoptimize1*/
        /*TAG:begin-addcol*/

        /* Get index of new variable. */
        int varidx;
        task.getnumvar(out varidx);

        /* Append a new varaible x_3 to the problem */
        task.appendvars(1);

        /* Set bounds on new varaible */
        task.putbound(mosek.accmode.var,
                      varidx,
                      mosek.boundkey.lo,
                      0,
                      +infinity);

        /* Change objective */
        task.putcj(varidx, 1.0);

        /* Put new values in the A matrix */
        int[] acolsub    =  new int[] {0,   2};
        double[] acolval =  new double[] {4.0, 1.0};

        task.putacol(varidx, /* column index */
                     acolsub,
                     acolval);
        /*TAG:end-addcol*/
        /*TAG:begin-reoptimize2*/
        /* Change optimizer to simplex free and reoptimize */
        task.putintparam(mosek.iparam.optimizer, mosek.optimizertype.free_simplex);
        task.optimize();
        /*TAG:end-reoptimize2*/
        /*TAG:begin-addcon*/
        /* Get index of new constraint */
        int conidx;
        task.getnumcon(out conidx);

        /* Append a new constraint */
        task.appendcons(1);

        /* Set bounds on new constraint */
        task.putbound(
          mosek.accmode.con,
          conidx,
          mosek.boundkey.up,
          -infinity,
          30000);

        /* Put new values in the A matrix */

        int[] arowsub = new int[] {0, 1, 2, 3};
        double[] arowval = new double[]  {1.0, 2.0, 1.0, 1.0};

        task.putarow(conidx, /* row index */
                     arowsub,
                     arowval);

        /*TAG:end-addcon*/
        /*TAG:begin-reoptimize3*/
        task.optimize();
        /*TAG:end-reoptimize3*/
      }
      catch (mosek.Exception e)
      {
        Console.WriteLine (e.Code);
        Console.WriteLine (e);
      }

      if (task != null) task.Dispose ();
      if (env  != null)  env.Dispose ();
    }
  }
}
