import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.lang.Math;
import org.gnu.glpk.GLPK;
import org.gnu.glpk.GLPKConstants;
import org.gnu.glpk.SWIGTYPE_p_double;
import org.gnu.glpk.SWIGTYPE_p_int;
import org.gnu.glpk.glp_prob;
import org.gnu.glpk.glp_smcp;

public class UTAgms {

	public static List main(String[] Alternatives_name, String[] Objectives_name, double[][] Perf_table, String[][] Preference, String[][] Indifference) {
		/*String[] Alternatives = { "A", "B", "C", "D", "E" };
		String[] Objectives = { "C1", "C2" };
		// TODO implementer la direction des obj (pour l'instant max)
		double[][] Perf_table = { { 0.0, 1.0 }, { 0.5, 0.5 }, { 1.0, 0.0 },
				{ 1.0, 1.0 }, { 0.0, 0.0 } };
		String[][] Preference = { { "B", "A" }, { "B", "C" } };
		
		String[][] Indifference = {"A","C"}; //  */
		
		int[][] Preference_int = build_preference_int_table(Preference,Alternatives_name);
		int[][] Indifference_int = build_preference_int_table(Indifference,Alternatives_name);
		System.out.println("PERFORMANCE TABLE");
		print_table(Perf_table);
		System.out.println("COMPUTING DOMINANCE INDEXES...");
		double[][] dominance_indexes = compute_dominance_indexes(Alternatives_name,
				Objectives_name, Perf_table, Preference, Preference_int, Indifference, Indifference_int);
		System.out.println("Dominance_indexes : ");
		print_table(dominance_indexes);
		System.out.println("");
		System.out.println("COMPUTING DOMINANCE RELATIONS...");
		char[][] dominance_relations = compute_dominance_relations(
				Alternatives_name, dominance_indexes);// P=preference, N=inverse
													// preference,
													// I=indifference, R=
													// incomparability
		System.out.println("Dominance_relations : ");
		print_table(dominance_relations);
		System.out.println("COMPUTING DOMINANCE RANKS...");
		int[] dominance_rank = compute_dominance_rank(Alternatives_name,
				dominance_indexes);
		System.out.println("COMPUTING MOST REPRESENTATIVE VALUE FUNCTION...");
		double[][] mrvf = compute_mrvf(Alternatives_name, Objectives_name, Perf_table, Preference,
				Preference_int, Indifference, Indifference_int, dominance_relations);
		print_table(mrvf);
		System.out.println("SELECTING ALTERNATIVES...");
		List next_population = select_alternatives(Alternatives_name, Objectives_name,
				Perf_table, dominance_rank, mrvf, 3);
		System.out.println("Population size : " + next_population.size());
		for (int i=0;i<next_population.size();i++){
			System.out.print(Alternatives_name[(Integer) next_population.get(i)]+ " ");
		}
		System.out.println();
		return next_population;
	}

	private static int[][] build_preference_int_table(String[][] preference,
			String[] alternatives_name) {
		int[][] preference_int=new int[preference.length][2];
		
		for (int p=0;p<preference.length;p++){
			preference_int[p][0]=Arrays.binarySearch(alternatives_name, preference[p][0]);
			preference_int[p][1]=Arrays.binarySearch(alternatives_name, preference[p][1]);
		}
		return preference_int;
	}

	static double[][] compute_dominance_indexes(String[] Alternatives,
			String[] Objectives, double[][] Perf_table, String[][] Preference,
			int[][] Preference_int, String[][] Indifference, int[][] Indifference_int) {

		glp_smcp parm;
		SWIGTYPE_p_int ind;
		SWIGTYPE_p_double val;
		int[][] Ref_Table = new int[Alternatives.length][Objectives.length];
		double[][] dominance_indexes = new double[Alternatives.length][Alternatives.length];

		// Create problem
		glp_prob lp = GLPK.glp_create_prob();
		System.out.println("Problem created");
		GLPK.glp_set_prob_name(lp, "myProblem");

		// Define columns
		GLPK.glp_add_cols(lp, Alternatives.length * Objectives.length + 2);
		int i = 1;
		for (int alt = 0; alt < Alternatives.length; alt++) {
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.glp_set_col_name(lp, i, "u" + Alternatives[alt]
						+ Objectives[obj]);
				GLPK.glp_set_col_kind(lp, i, GLPKConstants.GLP_CV);
				GLPK.glp_set_col_bnds(lp, i, GLPKConstants.GLP_DB, 0, 1);
				// System.out.println("u" + Alternatives[alt]+Objectives[obj] +
				// " "+ i);
				Ref_Table[alt][obj] = i;
				i = i + 1;
			}
		}

		// Create constraints
		int cst_i = 1;
		
		// min=0
				double min = 1e9;

		for (int obj = 0; obj < Objectives.length; obj++) {
			for (int alt = 0; alt < Alternatives.length; alt++) {
				if (Perf_table[alt][obj] < min) {
					min = Perf_table[alt][obj];
				}
			}

			for (int alt = 0; alt < Alternatives.length; alt++) {
				if (Perf_table[alt][obj] == min) {
					GLPK.glp_add_rows(lp, 1);
					GLPK.glp_set_row_name(lp, cst_i, "Constraint" + cst_i);
					GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_FX, 0, 0);
					ind = GLPK.new_intArray(2);
					GLPK.intArray_setitem(ind, 1, Ref_Table[alt][obj]);
					val = GLPK.new_doubleArray(2);
					GLPK.doubleArray_setitem(val, 1, 1.);
					GLPK.glp_set_mat_row(lp, cst_i, 1, ind, val);
					// System.out.println("Minimum constraint " +cst_i+
					// " : Alt= "+ (alt+1)+ "; Obj= " + (obj+1) );
					cst_i++;
				}
			}
			ind=null;
		}

		// sum(max)=1
		double[] max = new double[Objectives.length];
		int[] maxs = new int[Objectives.length];

		for (int obj = 0; obj < Objectives.length; obj++) {
			max[obj] = -1e9;
			for (int alt = 0; alt < Alternatives.length; alt++) {
				if (Perf_table[alt][obj] > max[obj]) {
					maxs[obj] = Ref_Table[alt][obj];
					max[obj] = Perf_table[alt][obj];
				}
			}
		}
		// System.out.print("Maximum constraint " +cst_i+ " : ");
		GLPK.glp_add_rows(lp, 1);
		GLPK.glp_set_row_name(lp, cst_i, "Sum_max");
		GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_FX, 1, 1);
		ind = GLPK.new_intArray(Objectives.length);
		val = GLPK.new_doubleArray(Objectives.length);
		for (int obj = 0; obj < Objectives.length; obj++) {
			GLPK.intArray_setitem(ind, obj + 1, maxs[obj]);
			// System.out.print("Alt= "+ maxs[obj]+ "; Obj= "+ (obj+1)+ "; ");
			GLPK.doubleArray_setitem(val, obj + 1, 1.);
		}
		GLPK.glp_set_mat_row(lp, cst_i, 2, ind, val);
		// System.out.println("");
		cst_i++;

		// preferences
		for (int pref = 0; pref < Preference_int.length; pref++) {
			// System.out.print("Preference constraint " +cst_i+ " : " +
			// Preference[pref][0]+" > " + Preference[pref][1]);
			GLPK.glp_add_rows(lp, 1);
			GLPK.glp_set_row_name(lp, cst_i, "Constraint" + cst_i);
			GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_LO, 0, 0);
			ind = GLPK.new_intArray(Objectives.length * 2 + 1);
			val = GLPK.new_doubleArray(Objectives.length * 2 + 1);
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.intArray_setitem(ind, obj + 1,	Ref_Table[Preference_int[pref][0]][obj]); //ERROR
				GLPK.doubleArray_setitem(val, obj + 1, 1.);
				// System.out.print(Ref_Table[Preference_int[pref][0]][obj]+
				// " ");
			}
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.intArray_setitem(ind, Objectives.length + obj + 1,
						Ref_Table[Preference_int[pref][1]][obj]);
				GLPK.doubleArray_setitem(val, Objectives.length + obj + 1, -1.);
				// System.out.print(" "+Ref_Table[Preference_int[pref][1]][obj]);
			}
			GLPK.glp_set_mat_row(lp, cst_i, 4, ind, val);
			 //System.out.println(""+cst_i);
			cst_i++;
		}
		
		//indifferences
for (int indif=0;indif<Indifference_int.length;indif++){
			GLPK.glp_add_rows(lp, 1);
			GLPK.glp_set_row_name(lp, cst_i, "Constraint" + cst_i);
			GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_FX, 0, 0);
			ind = GLPK.new_intArray(Objectives.length * 2 + 1);
			val = GLPK.new_doubleArray(Objectives.length * 2 + 1);
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.intArray_setitem(ind, obj + 1,
						Ref_Table[Indifference_int[indif][0]][obj]);
				GLPK.doubleArray_setitem(val, obj + 1, 1.);
				//System.out.print(Ref_Table[Indifference_int[indif][0]][obj]+
				//"+");
			}
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.intArray_setitem(ind, Objectives.length + obj + 1,
						Ref_Table[Indifference_int[indif][1]][obj]);
				GLPK.doubleArray_setitem(val, Objectives.length + obj + 1, -1.);
				//System.out.print("-"+Ref_Table[Indifference_int[indif][1]][obj]);
			}
			GLPK.glp_set_mat_row(lp, cst_i, 4, ind, val);
			//System.out.println("");
			cst_i++;
		}

		

		// monotone functions
		for (int obj = 0; obj < Objectives.length; obj++) {
			for (int alt = 0; alt < Alternatives.length; alt++) {
				for (int alt2 = 0; alt2 < Alternatives.length; alt2++) {
					if (Perf_table[alt][obj] > Perf_table[alt2][obj]) {
						GLPK.glp_add_rows(lp, 1);
						GLPK.glp_set_row_name(lp, cst_i, "Constraint" + cst_i);
						GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_LO,
								0, 0);
						ind = GLPK.new_intArray(3);
						GLPK.intArray_setitem(ind, 1, Ref_Table[alt][obj]);
						GLPK.intArray_setitem(ind, 2, Ref_Table[alt2][obj]);
						val = GLPK.new_doubleArray(3);
						GLPK.doubleArray_setitem(val, 1, 1.);
						GLPK.doubleArray_setitem(val, 2, -1.);
						GLPK.glp_set_mat_row(lp, cst_i, 2, ind, val);
						/* System.out.println("Monotone > constraint " +cst_i+
						" : " + Ref_Table[alt][obj]+ " "+
						 Ref_Table[alt2][obj]);*/
						cst_i++;
					}
					else if (Perf_table[alt][obj] == Perf_table[alt2][obj]& alt!=alt2){
						GLPK.glp_add_rows(lp, 1);
						GLPK.glp_set_row_name(lp, cst_i, "MonConstraint"
								+ cst_i);
						GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_FX,
								0, 0);
						ind = GLPK.new_intArray(3);
						GLPK.intArray_setitem(ind, 1, Ref_Table[alt][obj]);
						GLPK.intArray_setitem(ind, 2, Ref_Table[alt2][obj]);
						val = GLPK.new_doubleArray(3);
						GLPK.doubleArray_setitem(val, 1, 1.);
						GLPK.doubleArray_setitem(val, 2, -1.);
						GLPK.glp_set_mat_row(lp, cst_i, 2, ind, val);
						/*System.out.println("Monotone = constraint " +cst_i+
						" : " + Ref_Table[alt][obj]+ " "+
						Ref_Table[alt2][obj]);*/
						cst_i++;
					}
				}
			}
		}
		
		// //////////////////////////////////////////////////////////////////////////////
		// Define objective
		for (int alt = 0; alt < Alternatives.length; alt++) {
			for (int alt2 = 0; alt2 < Alternatives.length; alt2++) {
				if (alt == alt2) {
					dominance_indexes[alt][alt2] = 0; // diagonale
				} else {
					GLPK.glp_set_obj_name(lp, "d" + Alternatives[alt]
							+ Alternatives[alt2]);
					GLPK.glp_set_obj_dir(lp, GLPKConstants.GLP_MIN);
					for (int index = 0; index < Objectives.length
							* Alternatives.length; index++) { // reinit obj
						GLPK.glp_set_obj_coef(lp, index, 0);
					}
					for (int obj = 0; obj < Objectives.length; obj++) {
						GLPK.glp_set_obj_coef(lp, Ref_Table[alt][obj], 1);
						GLPK.glp_set_obj_coef(lp, Ref_Table[alt2][obj], -1);
					}

					// Solve model
					parm = new glp_smcp();
					GLPK.glp_init_smcp(parm);
					int ret = GLPK.glp_simplex(lp, parm);

					// Retrieve solution
					if (ret == 0) {
						// write_lp_solution(lp);
						dominance_indexes[alt][alt2] = GLPK.glp_get_obj_val(lp);
					} else {
						System.out.println("The problem could not be solved");
					}
				}
			}
		}
		// Free memory
		GLPK.glp_delete_prob(lp);
		GLPK.delete_intArray(ind);
		GLPK.delete_doubleArray(val);
		return dominance_indexes;
	}

	static List<Object> select_alternatives(String[] Alternatives, String[] Objectives,
			double[][] perf, int[] ranks, double[][] mrvf, int pop) {
		List<Object> temp_pop = new ArrayList<Object>();
		List<Object> current_pop = new ArrayList<Object>();
		
		int rank = 0;
		while (current_pop.size() < pop) {
			for (int alt = 0; alt < Alternatives.length; alt++) {
				if (ranks[alt] == rank) {
					temp_pop.add(alt);
				}
			}
			double[] cd = new double[temp_pop.size()];
			if (temp_pop.size() <= (pop - current_pop.size())) {
				// add temp_pop to current_pop
				current_pop.addAll(temp_pop);
				rank++;
				temp_pop.clear();
			} else {
				for (int alt = 0; alt < temp_pop.size(); alt++) {
					//cd[alt]=compute_crowding_distance(alt, Alternatives, Objectives, mrvf);
					cd[alt]=Math.random();
				}
						// rank

for (int i = 0; i < temp_pop.size(); i++) {
		for (int j = 0; j < temp_pop.size(); j++) {
			if (cd[i]>cd[j]){ //ranking in descending order of crowding distance (cd)
				double temp_cd = cd[i];
				cd[i]=cd[j];
				cd[j]=temp_cd;
				Object temp_alt = temp_pop.get(i);
				temp_pop.set(i, temp_pop.get(j));
				temp_pop.set(j, temp_alt);
				
			} 
		}

}

				
				// add pop-current_pop.size first alt to current_pop
int i=0;
while (current_pop.size()!=pop){
	current_pop.add(temp_pop.get(i));
	i++;
			}
						}
		
	}

		return current_pop;
	}

	static char[][] compute_dominance_relations(String[] Alternatives,
			double[][] dominance_indexes) {
		char[][] dominances = new char[Alternatives.length][Alternatives.length];
		int[] dominance_rank = new int[Alternatives.length];
		for (int alt = 0; alt < Alternatives.length; alt++) {
			// System.out.println("");
			for (int alt2 = 0; alt2 < Alternatives.length; alt2++) {
				// System.out.print(" "+dominance_indexes[alt][alt2]+ " ");
				if (dominance_indexes[alt][alt2] >= 0
						& dominance_indexes[alt2][alt] < 0) {
					dominances[alt][alt2] = 'P';
					dominances[alt2][alt] = 'N';
					// System.out.println(Alternatives[alt]+
					// " dominates "+Alternatives[alt2] );
				} else if (dominance_indexes[alt][alt2] == 0
						& dominance_indexes[alt2][alt] == 0) {
					dominances[alt][alt2] = 'I';
					dominances[alt2][alt] = 'I';
				} else if (dominance_indexes[alt][alt2] < 0
						& dominance_indexes[alt2][alt] < 0) {
					dominances[alt][alt2] = 'R';
					dominances[alt2][alt] = 'R';
				}

			}
		}

		return dominances;
	}

	static int[] compute_dominance_rank(String[] Alternatives,
			double[][] dominance_indexes) {
		char[][] dominances = new char[Alternatives.length][Alternatives.length];
		int[] dominance_rank = new int[Alternatives.length];
		for (int alt = 0; alt < Alternatives.length; alt++) {
			// System.out.println("");
			for (int alt2 = 0; alt2 < Alternatives.length; alt2++) {
				// System.out.print(" "+dominance_indexes[alt][alt2]+ " ");
				if (dominance_indexes[alt][alt2] >= 0
						& dominance_indexes[alt2][alt] < 0) {
					dominances[alt][alt2] = 'P';
					dominances[alt][alt2] = 'N';
					dominance_rank[alt2]++;
					// System.out.println(Alternatives[alt]+
					// " dominates "+Alternatives[alt2] );
				}
			}
		}
		
		for (int alt=0;alt<Alternatives.length;alt++){
			System.out.print(Alternatives[alt]+ " ");
			System.out.println(dominance_rank[alt]);
		}
		
		return dominance_rank;
	}

	static double[][] compute_mrvf(String[] Alternatives, String[] Objectives,
			double[][] Perf_table, String[][] Preference,
			int[][] Preference_int, String[][] Indifference,
			int[][] Indifference_int,char[][] dominance_relations) {
		
		glp_smcp parm;
		SWIGTYPE_p_int ind;
		SWIGTYPE_p_double val;
		int[][] Ref_Table = new int[Alternatives.length][Objectives.length];
		double[][] dominance_indexes = new double[Alternatives.length][Alternatives.length];
		double[][] mrvf=new double[Alternatives.length][Objectives.length];
		
		// Create problem
		glp_prob lp = GLPK.glp_create_prob();
		System.out.println("Problem created");
		GLPK.glp_set_prob_name(lp, "myProblem");

		// Define columns
		GLPK.glp_add_cols(lp, Alternatives.length * Objectives.length + 2);
		int i = 1;
		for (int alt = 0; alt < Alternatives.length; alt++) {
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.glp_set_col_name(lp, i, "u" + Alternatives[alt]
						+ Objectives[obj]);
				GLPK.glp_set_col_kind(lp, i, GLPKConstants.GLP_CV);
				GLPK.glp_set_col_bnds(lp, i, GLPKConstants.GLP_DB, 0, 1);
				// System.out.println("u" + Alternatives[alt]+Objectives[obj] +
				// " "+ i);
				Ref_Table[alt][obj] = i;
				i = i + 1;
			}
		}

		int eps_ref = i;
		GLPK.glp_set_col_name(lp, eps_ref, "epsilon");
		GLPK.glp_set_col_kind(lp, eps_ref, GLPKConstants.GLP_CV);
		GLPK.glp_set_col_bnds(lp, eps_ref, GLPKConstants.GLP_DB, 0, 1);
		i = i + 1;

		int delta_ref = i;
		GLPK.glp_set_col_name(lp, delta_ref, "delta");
		GLPK.glp_set_col_kind(lp, delta_ref, GLPKConstants.GLP_CV);
		GLPK.glp_set_col_bnds(lp, delta_ref, GLPKConstants.GLP_DB, 0, 1);

		// Create constraints
		int cst_i = 1;
		
		// min=0
				double min = 1e9;

		for (int obj = 0; obj < Objectives.length; obj++) {
			for (int alt = 0; alt < Alternatives.length; alt++) {
				if (Perf_table[alt][obj] < min) {
					min = Perf_table[alt][obj];
				}
			}

			for (int alt = 0; alt < Alternatives.length; alt++) {
				if (Perf_table[alt][obj] == min) {
					GLPK.glp_add_rows(lp, 1);
					GLPK.glp_set_row_name(lp, cst_i, "Constraint" + cst_i);
					GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_FX, 0, 0);
					ind = GLPK.new_intArray(2);
					GLPK.intArray_setitem(ind, 1, Ref_Table[alt][obj]);
					val = GLPK.new_doubleArray(2);
					GLPK.doubleArray_setitem(val, 1, 1.);
					GLPK.glp_set_mat_row(lp, cst_i, 1, ind, val);
					// System.out.println("Minimum constraint " +cst_i+
					// " : Alt= "+ (alt+1)+ "; Obj= " + (obj+1) );
					cst_i++;
				}
			}
		}

		// sum(max)=1
		double[] max = new double[Objectives.length];
		int[] maxs = new int[Objectives.length];

		for (int obj = 0; obj < Objectives.length; obj++) {
			max[obj] = -1e9;
			for (int alt = 0; alt < Alternatives.length; alt++) {
				if (Perf_table[alt][obj] > max[obj]) {
					maxs[obj] = Ref_Table[alt][obj];
					max[obj] = Perf_table[alt][obj];
				}
			}
		}
		// System.out.print("Maximum constraint " +cst_i+ " : ");
		GLPK.glp_add_rows(lp, 1);
		GLPK.glp_set_row_name(lp, cst_i, "Sum_max");
		GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_FX, 1, 1);
		ind = GLPK.new_intArray(Objectives.length);
		val = GLPK.new_doubleArray(Objectives.length);
		for (int obj = 0; obj < Objectives.length; obj++) {
			GLPK.intArray_setitem(ind, obj + 1, maxs[obj]);
			// System.out.print("Alt= "+ maxs[obj]+ "; Obj= "+ (obj+1)+ "; ");
			GLPK.doubleArray_setitem(val, obj + 1, 1.);
		}
		GLPK.glp_set_mat_row(lp, cst_i, 2, ind, val);
		// System.out.println("");
		cst_i++;

		// preferences
		for (int pref = 0; pref < Preference_int.length; pref++) {
			// System.out.print("Preference constraint " +cst_i+ " : " +
			// Preference[pref][0]+" > " + Preference[pref][1]);
			GLPK.glp_add_rows(lp, 1);
			GLPK.glp_set_row_name(lp, cst_i, "Constraint" + cst_i);
			GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_LO, 0, 0);
			ind = GLPK.new_intArray(Objectives.length * 2 + 1);
			val = GLPK.new_doubleArray(Objectives.length * 2 + 1);
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.intArray_setitem(ind, obj + 1,
						Ref_Table[Preference_int[pref][0]][obj]);
				GLPK.doubleArray_setitem(val, obj + 1, 1.);
				// System.out.print(Ref_Table[Preference_int[pref][0]][obj]+
				// " ");
			}
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.intArray_setitem(ind, Objectives.length + obj + 1,
						Ref_Table[Preference_int[pref][1]][obj]);
				GLPK.doubleArray_setitem(val, Objectives.length + obj + 1, -1.);
				// System.out.print(" "+Ref_Table[Preference_int[pref][1]][obj]);
			}
			GLPK.glp_set_mat_row(lp, cst_i, 4, ind, val);
			 //System.out.println(""+cst_i);
			cst_i++;
		}
		
		//indifferences
for (int indif=0;indif<Indifference_int.length;indif++){
			GLPK.glp_add_rows(lp, 1);
			GLPK.glp_set_row_name(lp, cst_i, "Constraint" + cst_i);
			GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_FX, 0, 0);
			ind = GLPK.new_intArray(Objectives.length * 2 + 1);
			val = GLPK.new_doubleArray(Objectives.length * 2 + 1);
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.intArray_setitem(ind, obj + 1,
						Ref_Table[Indifference_int[indif][0]][obj]);
				GLPK.doubleArray_setitem(val, obj + 1, 1.);
				//System.out.print(Ref_Table[Indifference_int[indif][0]][obj]+
				//"+");
			}
			for (int obj = 0; obj < Objectives.length; obj++) {
				GLPK.intArray_setitem(ind, Objectives.length + obj + 1,
						Ref_Table[Indifference_int[indif][1]][obj]);
				GLPK.doubleArray_setitem(val, Objectives.length + obj + 1, -1.);
				//System.out.print("-"+Ref_Table[Indifference_int[indif][1]][obj]);
			}
			GLPK.glp_set_mat_row(lp, cst_i, 4, ind, val);
			//System.out.println("");
			cst_i++;
		}

		

		// monotone functions
		for (int obj = 0; obj < Objectives.length; obj++) {
			for (int alt = 0; alt < Alternatives.length; alt++) {
				for (int alt2 = 0; alt2 < Alternatives.length; alt2++) {
					if (Perf_table[alt][obj] > Perf_table[alt2][obj]) {
						GLPK.glp_add_rows(lp, 1);
						GLPK.glp_set_row_name(lp, cst_i, "Constraint" + cst_i);
						GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_LO,
								0, 0);
						ind = GLPK.new_intArray(3);
						GLPK.intArray_setitem(ind, 1, Ref_Table[alt][obj]);
						GLPK.intArray_setitem(ind, 2, Ref_Table[alt2][obj]);
						val = GLPK.new_doubleArray(3);
						GLPK.doubleArray_setitem(val, 1, 1.);
						GLPK.doubleArray_setitem(val, 2, -1.);
						GLPK.glp_set_mat_row(lp, cst_i, 2, ind, val);
						/* System.out.println("Monotone > constraint " +cst_i+
						" : " + Ref_Table[alt][obj]+ " "+
						 Ref_Table[alt2][obj]);*/
						cst_i++;
					}
					else if (Perf_table[alt][obj] == Perf_table[alt2][obj]& alt!=alt2){
						GLPK.glp_add_rows(lp, 1);
						GLPK.glp_set_row_name(lp, cst_i, "MonConstraint"
								+ cst_i);
						GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_FX,
								0, 0);
						ind = GLPK.new_intArray(3);
						GLPK.intArray_setitem(ind, 1, Ref_Table[alt][obj]);
						GLPK.intArray_setitem(ind, 2, Ref_Table[alt2][obj]);
						val = GLPK.new_doubleArray(3);
						GLPK.doubleArray_setitem(val, 1, 1.);
						GLPK.doubleArray_setitem(val, 2, -1.);
						GLPK.glp_set_mat_row(lp, cst_i, 2, ind, val);
						/*System.out.println("Monotone = constraint " +cst_i+
						" : " + Ref_Table[alt][obj]+ " "+
						Ref_Table[alt2][obj]);*/
						cst_i++;
					}
				}
			}
		}
		// Preference maximization
		for (int alt = 0; alt < Alternatives.length; alt++) {
			for (int alt2 = 0; alt2 < Alternatives.length; alt2++) {
				if (dominance_relations[alt][alt2] == 'P') {
					GLPK.glp_add_rows(lp, 1);
					GLPK.glp_set_row_name(lp, cst_i, "PMaxConstraint" + cst_i);
					GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_LO, 0, 0);
					ind = GLPK.new_intArray(Objectives.length * 2 + 2);
					val = GLPK.new_doubleArray(Objectives.length * 2 + 2);
					i = 1;
					for (int obj = 0; obj < Objectives.length; obj++) {
						GLPK.intArray_setitem(ind, i, Ref_Table[alt][obj]);
						GLPK.doubleArray_setitem(val, i, 1.);
						i++;
						GLPK.intArray_setitem(ind, i, Ref_Table[alt2][obj]);
						GLPK.doubleArray_setitem(val, i, -1.);
						i++;
					}
					GLPK.intArray_setitem(ind, i, eps_ref);
					GLPK.doubleArray_setitem(val, i, -1.);
					GLPK.glp_set_mat_row(lp, cst_i, Objectives.length * 2 + 1,
							ind, val);
					cst_i++;
				} else if (dominance_relations[alt][alt2] == 'I'
						| dominance_relations[alt][alt2] == 'R') {
					if (alt > alt2) {
						GLPK.glp_add_rows(lp, 1);
						GLPK.glp_set_row_name(lp, cst_i, "IMinConstraint"
								+ cst_i);
						GLPK.glp_set_row_bnds(lp, cst_i, GLPKConstants.GLP_UP,
								0, 0);
						ind = GLPK.new_intArray(Objectives.length * 2 + 2);
						val = GLPK.new_doubleArray(Objectives.length * 2 + 2);
						i = 1;
						for (int obj = 0; obj < Objectives.length; obj++) {
							GLPK.intArray_setitem(ind, i, Ref_Table[alt][obj]);
							GLPK.doubleArray_setitem(val, i, 1.);
							i++;
							GLPK.intArray_setitem(ind, i, Ref_Table[alt2][obj]);
							GLPK.doubleArray_setitem(val, i, -1.);
							i++;
						}
						GLPK.intArray_setitem(ind, i, delta_ref);
						GLPK.doubleArray_setitem(val, i, -1.);
						GLPK.glp_set_mat_row(lp, cst_i,
								Objectives.length * 2 + 1, ind, val);
						cst_i++;
					}
				}
			}
		}

		// //////////////////////////////////////////////////////////////////////////////
		// Define objective
		GLPK.glp_set_obj_name(lp, "z");
		GLPK.glp_set_obj_dir(lp, GLPKConstants.GLP_MAX);
		GLPK.glp_set_obj_coef(lp, eps_ref, 1000);
		GLPK.glp_set_obj_coef(lp, delta_ref, -1);

		// Solve model
		parm = new glp_smcp();
		GLPK.glp_init_smcp(parm);
		int ret = GLPK.glp_simplex(lp, parm);

		// Retrieve solution
		if (ret == 0) {
			//write_lp_solution(lp);
			for (int alt = 0; alt < Alternatives.length; alt++) {
				for (int obj = 0; obj < Objectives.length; obj++) {
					i=Ref_Table[alt][obj];
					mrvf[alt][obj] = GLPK.glp_get_col_prim(lp, i);
				}
			}
								
		} else {
			System.out.println("The problem could not be solved");
		}

		// Free memory
		GLPK.glp_delete_prob(lp);
		GLPK.delete_intArray(ind);
		GLPK.delete_doubleArray(val);
		return mrvf;
	}

	static double compute_crowding_distance(int ref_alt,String[] Alternatives, String[] Objectives,double[][] mrvf_table){
		int no_solution_ref = 10000;
		int[] low = new int[Alternatives.length];
				Arrays.fill(low, no_solution_ref);
		int[] up = new int[Alternatives.length];
		Arrays.fill(up, no_solution_ref);
		double[] low_distance= new double[Alternatives.length];
			Arrays.fill(low_distance, Double.POSITIVE_INFINITY);
	    double[] up_distance= new double[Alternatives.length];
			Arrays.fill(up_distance, Double.POSITIVE_INFINITY);
			double crowding_distance = 0;
			
		for (int obj=0; obj<Objectives.length; obj++){
			double ref_perf=mrvf_table[ref_alt][obj];
				double current_perf;
				
				
				for (int alt=0; alt<Alternatives.length; alt++){
					if (alt!=ref_alt){
					current_perf=mrvf_table[alt][obj];
				
				if (current_perf<ref_perf & Math.abs(current_perf-ref_perf)<low_distance[obj]){
					//TODO A REFAIRE AVEC UTILITY VALUE PRISE EN COMPTE (revoir la notion de voisin)
					low[obj]=alt;
					low_distance[obj]=Math.abs(current_perf-ref_perf);
				}
				else if (current_perf>ref_perf & Math.abs(current_perf-ref_perf)<up_distance[obj]){
					up[obj]=alt;
					up_distance[obj]=Math.abs(current_perf-ref_perf);
				}
			}
		}

		}
		
		for (int obj=0; obj<Objectives.length; obj++){
		crowding_distance=crowding_distance+up_distance[obj]-low_distance[obj];
		}
		

		System.out.println("Crowding distance of "+ Alternatives[ref_alt]+ " is "+ crowding_distance);
		
		return crowding_distance;
	}

	static void write_lp_solution(glp_prob lp) {
		int i;
		int n;
		String name;
		double val;

		name = GLPK.glp_get_obj_name(lp);
		val = GLPK.glp_get_obj_val(lp);
		System.out.print(name);
		System.out.print(" = ");
		System.out.println(val);
		n = GLPK.glp_get_num_cols(lp);
		for (i = 1; i <= n; i++) {
			name = GLPK.glp_get_col_name(lp, i);
			val = GLPK.glp_get_col_prim(lp, i);
			System.out.print(name);
			System.out.print(" = ");
			System.out.println(val);
		}
	}

	static void print_table(double[][] table) {
		for (int i = 0; i<table.length; i++) {

			for (int j = 0; j < table[i].length; j++) {
				System.out.print(table[i][j] + " ");
			}
			System.out.println("");
		}
		System.out.println("");
	}
	
	static void print_table(int[][] table) {
		for (int i = 0; i<table.length; i++) {

			for (int j = 0; j < table[i].length; j++) {
				System.out.print(table[i][j] + " ");
			}
			System.out.println("");
		}
		System.out.println("");
	}

	static void print_table(char[][] table) {
		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				System.out.print(table[i][j] + " ");
			}
			System.out.println("");
		}
		System.out.println("");

	}
}