#include "test.h"
//#include <iostream>
//#include <fstream>

using namespace std;

const int nb_item = 3;
const int nb_priod = 3;

double D[nb_item][nb_priod];
float s[nb_item][nb_priod];
float c[nb_item][nb_priod];
float M[nb_item][nb_priod];
float p[nb_item];
float h[nb_item];
float C[nb_priod];

int i,j,t;

void Data_Input()
{

	ifstream in("data.txt");
    
//	fstream in;
//	in.open("data.txt");

	for (i = 0; i < nb_item; i++) 
	{
		for (t = 0; t < nb_priod; t++) 
		{
			in >> D[i][t];
		}
	}

	for (i = 0; i < nb_item; i++) 
	{
		for (j = 0; j < nb_priod; j++) 
		{
			in >> s[i][j];
		}
	}

	for (i = 0; i < nb_item; i++) 
	{
		for (j = 0; j < nb_priod; j++) 
		{
			in >> c[i][j];
		}
	}

	for (i = 0; i < nb_item; i++) 
	{
		in >> p[i];
	}

	for (i = 0; i < nb_item; i++) 
	{
		in >> h[i];
	}

	for (t = 0; t < nb_priod; t++) 
	{
		in >> C[i];
	}

	for (i = 0; i < nb_item; i++) 
	{
		for (j = 0; j < nb_item; j++) 
		{
			in >> M[i][j];
		}
	}

//	in.close();

}



void Search_Optimal()
{
	IloEnv env; // creat environment

	try 
	{
		IloModel mod(env);
	      
/*---------------------- Declare decision variable ------------------------*/ 	
			
		Matrix3_var T(env, nb_item);
		for(i=0;i<nb_item;i++)
		{
			T[i] = Matrix2_var(env, nb_item);
			for(j=0;j<nb_item;j++)
			{
				T[i][j]=IloNumVarArray(env, nb_priod, 0, 1, ILOINT);
			}
		}

		Matrix2_var X(env, nb_item);
		for(i=0;i<nb_item;i++) 
		{
			X[i]=IloNumVarArray(env, nb_priod, 0, 10000, ILOFLOAT);
		}

		Matrix2_var I(env, nb_item);
		for(i=0;i<nb_item;i++) 
		{
			I[i]=IloNumVarArray(env, nb_priod, 0, 10000, ILOFLOAT);
		}

		Matrix2_var A(env, nb_item);
		for(i=0;i<nb_item;i++) 
		{
			A[i]=IloNumVarArray(env, nb_priod, 0, 10000, ILOFLOAT);
		}

		IloNumVar a(env, 0, 10000);
			
		Matrix2_var V(env, nb_item);
		for(i=0;i<nb_item;i++) 
		{
			V[i]=IloNumVarArray(env, nb_priod, 0, 10000, ILOFLOAT);
		}

/*------------------------- Constraints ---------------------------*/

		for(i=0;i<nb_item;i++)      // Inventory balance constraint
		{
			mod.add(X[i][0] - I[i][0]  == D[i][0]);

			for(t=1;t<nb_priod;t++)
			{
				mod.add(I[i][t-1] + X[i][t] - I[i][t]  == D[i][t]);
			}
		}

		for(t=0;t<nb_priod;t++)      // Capacity constraint
		{
			IloExpr sum_setuptime(env);
			for(i=0;i<nb_item;i++)
			{
				for(j=0;j<nb_item;j++)
				{
					sum_setuptime += s[i][j]*T[i][j][t];
				}
			}

			IloExpr sum_processingtime(env);
			for(i=0;i<nb_item;i++)
			{
				sum_processingtime += p[i]*X[i][t];
			}
				
			mod.add(sum_setuptime + sum_processingtime <= C[t]);
		}

		for(i=0;i<nb_item;i++)      // production condition
		{
			for(t=0;t<nb_priod;t++)
			{
				IloExpr sum_setup_var(env);
				for(j=0;j<nb_item;j++)
				{
					sum_setup_var += T[i][j][t];
				}

				mod.add(X[i][t] <= M[i][t]*sum_setup_var + M[i][t]*A[i][t]);
			}
		}

		for(t=0;t<nb_priod;t++)      // first setup job is only one
		{
			IloExpr sum_first_setup_var(env);
			for(i=0;i<nb_item;i++)
			{
				sum_first_setup_var += A[i][t];
			}
 
			mod.add(sum_first_setup_var == 1);
		}

		IloExpr sum_outputsetup_var(env);
		IloExpr sum_inputsetup_var(env);     // network flow balance constraint 1
		for(i=0;i<nb_item;i++)      
		{
			for(t=0;t<nb_priod;t++)
			{
				for(j=0;j<nb_item;j++)
				{
					sum_inputsetup_var += T[j][i][t];
					sum_outputsetup_var += T[i][j][t];
				}
			}
		}

		for(i=0;i<nb_item;i++)     
		{
			for(t=0;t<nb_priod-1;t++)
			{
				mod.add(A[i][t] + sum_inputsetup_var == A[i][t+1] + sum_outputsetup_var);
			}
		}

		for(i=0;i<nb_item;i++)      // network flow balance constraint 2
		{
			for(t=0;t<nb_priod-1;t++)
			{
				mod.add(A[i][t] + sum_inputsetup_var == a + sum_outputsetup_var);
			}
		}


		for(i=0;i<nb_item;i++)     // machine state with auxiliary variable
		{
			for(j=0;j<nb_item;j++)     
			{
				if(i == j)
				{
					break;
				}
		
				for(t=0;t<nb_priod;t++)
				{
					mod.add(V[i][t] + nb_item+T[i][j][t] - (nb_item - 1) - nb_item+A[i][t] <= V[j][t]);
				}
			}
		}


/*------------------------- Objective function ---------------------------*/

		IloExpr sequence_dependent_setup_cost(env);
        IloExpr holding_cost(env);

		for(i=0;i<nb_item;i++)     
		{
			for(j=0;j<nb_item;j++)     
			{
				for(t=0;t<nb_priod;t++)
				{
					sequence_dependent_setup_cost += c[i][j]*T[i][j][t];
				}
			}
		}

		for(i=0;i<nb_item;i++)     
		{
			for(t=0;t<nb_priod;t++)
			{
				holding_cost += h[i]*I[i][t];
			}
		}
     
//		mod.add(IloMinimize(env,sequence_dependent_setup_cost + holding_cost));

/*------------------------- output ---------------------------*/
	
		cout << mod << endl;
        IloCplex cplex(env);
		cplex.extract(mod);
		cplex.exportModel("model.lp");

		IloNum start=env.getTime();

		if (cplex.solve()) 
		{
			switch(cplex.getStatus())
			{
			case  IloAlgorithm::Optimal :
				env.out() << " Optimal Solution Found. Total cost:" << cplex.getObjValue() << endl;
				break;

			case  IloAlgorithm::Feasible :
				env.out() << "No Optimal Solution Found. cost:" << cplex.getObjValue()<<endl;
				break;
			}
		} 
		else
		{
			env.out() << " No Solution found" << endl;
			switch(cplex.getStatus())
			{
			case  IloAlgorithm::Unbounded :
				env.out() << " Problem is Unbounded" << endl;
				break;

			case  IloAlgorithm::Infeasible :
				env.out() << "Problem is Infeasible: try the IIS fonctionalities" << endl;
				break;

			case IloAlgorithm::InfeasibleOrUnbounded :
				env.out() << "Problem is Infeasible or Unbounded: try the IIS fonctionalities" << endl;
				break;

			case IloAlgorithm::Error :
				env.out() << " An Error occured" << endl;
				break;

			case IloAlgorithm::Unknown :
				env.out() << " Status Unknown" << endl;
				break;
			}
		}

		IloNum end=env.getTime();

		env.out() << " Problem tackled in " << end-start<<" seconds"<<endl;

 }

   catch (IloException& e)
   {
	   cerr << "Error: " << e << endl;
   }

	env.end();

//        cplex.setParam(cplex.TiLim, 6000);
}// Search_Optimal end



void main()
{
	Data_Input();
	Search_Optimal();

}// main end