#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<ilcplex/ilocplex.h>
#include<ilcplex/ilocplex.h>


ILOSTLBEGIN

#define  E			2		//ο ּ   

#define  s			4.5		// ̵ӵ(km/h)
#define  time		0.1		// ҿ ð(h)
#define	 pallet		24


//2 Array Matrix typedef մϴ.
typedef IloArray<IloNumArray> IloNumMatrix;
typedef IloArray<IloNumVarArray> IloNumVarMatrix;




void main()
{

	IloEnv env;
	try{
		
	IloModel IP(env);


	// ȷ   ǰi ܷ  Ѵ.
//	srand(time());
/*	IloNumArray u(env, pallet);
	for (int i = 0; i <pallet; i++)
	{
		u[i] = (rand()%100)/100.00;
	}
	IloNumArray n(env, pallet);
	for ( i = 0; i <pallet; i++)
	{
		n[i] = 100*u[i];
		cout << i << " : " << n[i] << ",	";
	}
	*/
	IloNumArray u(env, pallet, 0.43, 0.66, 0.25, 0.29, 0.59, 0.63, 0.36, 0.60, 0.53, 0.23, 0.89, 0.77, 0.93, 0.17, 0.48, 0.69, 0.56, 0.82, 0.71, 0.34, 0.83, 0.26, 0.23, 0.39);
	IloNumArray n(env, pallet, 43,66,25,29,59,63,36,60,53,23,89,77,93,17,48,69,56,82,71,34,83,26,51,39);
		


		//ǻ  

			IloNumVarMatrix M(env, pallet);
			for ( int i=0; i<pallet; i++)
			{
				M[i]=IloNumVarArray(env, pallet, 0, 1, IloNumVar::Int);
			}


  //  1 ū ȷ  ȷ   .
	for( i=0; i<pallet; i++)
	{
		IloExpr X(env);
		for ( int k=0; k<pallet; k++)
		{
			if( i != k && u[i]>u[k] ) X += M[i][k];
		}
		IP.add( X<=0 );
	}

	//  2  ġ ȷ  1 ʰ  .
	for ( i=0; i<pallet; i++)
	{
		IloExpr U(env, u[i]);
		for( int k=0; k<pallet; k++)
		{
			if( i != k ) U += M[k][i]*u[k];
		}
		IP.add( U <=1 );
	}

	//  3  ġ i ٸ ġ k ̵ϴ  ǥ(ߺ ̵ )
	IloExprArray O1(env, pallet);
	for( i=0; i<pallet; i++) 
	{
		O1[i] = IloExpr(env);
		for( int k=0; k<pallet; k++)
		{
			if( i != k) O1[i] += M[i][k];
		}
		IP.add( O1[i]<=1 );
	}



	// 4
	for( i=0; i<pallet; i++)
	{
		for ( int k=0; k<pallet; k++)
		{
			IP.add( O1[i] + M[k][i] <= 1);
		}
	}


    //2    쿡 infeasible ɼ 
    //̷   ϴ°  Դϴ.
    IloNumVar Slack(env, 0, E);
	// 5    E ̻ .
	for( i=0; i<pallet; i++)
	{
		IloExpr C(env);
		for ( int k=0; k<pallet; k++)
		{
			if( i != k ) C += M[i][k];

		}
		IP.add( C >= E - Slack);
	}

	// ۾ð ּȭ
		IloExpr obj(env);
		for(i=0; i<pallet; i++)
		{
			for( int k=0; k<pallet; k++)
			{
				
				if ( IloAbs( i - k ) >= 17 && k != i )
				{
					int D = IloAbs(IloAbs( i - k ) - 18);
					obj -= M[i][k]*((D/s) + time*n[k]);
				} 
				else if( IloAbs( i - k ) >= 11 && k != i  )
				{
					int D = IloAbs(IloAbs( i - k ) - 12 );
					obj -= M[i][k]*((D/s) + time*n[k]);
				} 
				else if( IloAbs( i - k ) >= 5 && k != i )
				{
					int D = IloAbs(IloAbs( i - k ) - 6);
					obj -= M[i][k]*((D/s) + time*n[k]);
				} 
				else if( k != i )
				{
					int D = IloAbs( i - k );
					obj -= M[i][k]*((D/s) + time*n[k]);
				}

//				if( k != i ) obj -= M[i][k]*((D/s) + time*n[k]);
			}
		}
		
		//ð ϴ  ؼ   ʰ
		//˾ Ⱑ ׿. ⺻  ´ٰ ϴ.
 
    obj += Slack*1000; //Slack ּȭ ġ ũ ξ մϴ. 


	IP.add(IloMinimize(env,obj));
	
	IloCplex cplex(IP);

	if( cplex.solve() )
	
	{
		
	for(i=0; i<pallet; i++)
		{
			cout << i <<"=>"<< u[i] <<",  ";
			if( i == 10 || i==20 ) cout << endl;
		}
		cout << endl;
		//Display solution
		for(  i=0; i<pallet; i++ ) 
		{
			for( int k=0; k<pallet; k++ )  
			{
				if( cplex.getValue(M[i][k]) == 1 ) cout<<"move : "<<i<<" --> "<<k<<endl;
			}
		}

		//Display utilization

		cplex.out() << "solution status = "<< cplex.getStatus() << endl;
		cplex.out() << "solution value  = "<< cplex.getObjValue() << endl;
		for(  i=0; i<pallet; i++ ) 
		{
			for( int k=0; k<pallet; k++ )
				if( cplex.getValue(M[i][k]) == 1 ){
				env.out() << "Values M["<<i<<"]["<<k<<"] = " << cplex.getValue(M[i][k])<<endl;
				}
		}

		for(  i=0; i<pallet; i++ ) 
		{
			IloNum util = u[i];
			cout<<i<<" : "<< util;
			for( int k=0; k<pallet; k++ ) //move out
			{
				if( cplex.getValue(M[i][k]) == 1 ) util -= u[i];
			}
			for(  k=0; k<pallet; k++ ) //move in
			{
				if( cplex.getValue(M[k][i]) == 1 ) util += u[k];
			}
			cout<<" ==> "<<util<<endl;
		}
		
	}
	
	cplex.exportModel("pallet consolidation.rlp");
	}
	catch(IloException& e){
		cerr << "Concert execption caught: " << e << endl;
	}
	catch(...){
		cerr << "Unknown exception caught" << endl;
	}
	
	env.out();

	
}