#include "Hpief.h"

Hpief::Hpief(const Instance& instance, const string& fileName, double time_limit) : instance(instance) {

	this->fileName = fileName;
	this->time_limit = time_limit;

	this->variable_number = -1;
	this->constraint_number = -1;

	this->env = new GRBEnv();
	this->env->start();
	this->model = new GRBModel(*env);

	string gurobi_log = ::solutionPath + this->fileName + "_gurobi_log.txt";
	this->model->set(GRB_StringParam_LogFile, gurobi_log);
	this->model->set(GRB_IntParam_LogToConsole, 1);
	this->model->set(GRB_IntParam_Threads, 1);
	this->model->set(GRB_DoubleParam_MemLimit, 16.0);
	this->model->set(GRB_DoubleParam_IntFeasTol, 1e-9);
	this->model->set(GRB_DoubleParam_MIPGap, 0);

	this->arcs.clear();
	this->ad_arcs_size = 0;
	for (int i = 0; i < this->instance.getAltruisticDonors().size(); i++) {
		int ad = this->instance.getAltruisticDonors().at(i);
		for (const auto& arc : this->instance.getVertices().at(ad).getOutGoingArcs()) {
			this->arcs.push_back(make_pair(ad, arc.first));
			this->ad_arcs_size++;
		}
	}
	for (int i = 0; i < this->instance.getIncompatiblePairs().size(); i++) {
		int pair = this->instance.getIncompatiblePairs().at(i);
		for (const auto& arc : this->instance.getVertices().at(pair).getOutGoingArcs()) {
			this->arcs.push_back(make_pair(pair, arc.first));
		}
	}
	this->shortest_paths_from_ad = vector<int>(this->instance.getIncompatiblePairs().size(), ::OMEGA);
	for (int i = 0; i < this->instance.getIncompatiblePairs().size(); i++) {
		int pair = this->instance.getIncompatiblePairs().at(i);
		for (int j = 0; j < this->instance.getAltruisticDonors().size(); j++) {
			int ad = this->instance.getAltruisticDonors().at(j);
			int length = this->instance.get_shortest_path_FW(ad, pair);
			if (this->shortest_paths_from_ad.at(i) > length) {
				this->shortest_paths_from_ad.at(i) = length;
			}
		}
	}

	//arcs cycles
	/*int pair_size = this->instance.getIncompatiblePairs().size();
	int v_size = this->instance.getVertices().size();
	this->sorted_pairs_per_decr_deg = vector<int>(pair_size, -1);
	vector<pair<int, int>> pair_degs(pair_size);
	for (int i = 0; i < pair_size; i++) {
		int pair = this->instance.getIncompatiblePairs().at(i);
		int tot_deg = this->instance.getVertices().at(pair).getInGoingArcs().size() + this->instance.getVertices().at(pair).getOutGoingArcs().size();
		pair_degs.at(i) = make_pair(tot_deg, pair);
	}
	sort(pair_degs.rbegin(), pair_degs.rend());
	vector<int> sort_dictionary(v_size, -1);
	for (int i = 0; i < pair_size; i++) {
		this->sorted_pairs_per_decr_deg.at(i) = pair_degs.at(i).second;
		sort_dictionary.at(pair_degs.at(i).second) = i;
	}*/

	//this->arcs_cycles = vector<vector<vector<int>>>(this->instance.getIncompatiblePairs().size());
	//int cycle_length = this->instance.getCyclesLength();
	//for (int i = 0; i < pair_size; i++) {

	//	vector<vector<vector<bool>>> ee(pair_size, vector<vector<bool>>(pair_size, vector<bool>(cycle_length, false)));
	//	// Back
	//	vector<vector<int>> pe(cycle_length, vector<int>(this->arcs.size()));
	//	vector<vector<bool>> heads(cycle_length, vector<bool>(pair_size, false));
	//	heads[cycle_length - 1][i] = true;
	//	for (int j = cycle_length - 1; j >= 0; j--) {
	//		for (int k = this->ad_arcs_size; k < this->arcs.size(); k++) {
	//			int origin = this->arcs.at(k).first;
	//			int dest = this->arcs.at(k).second;
	//			int origin_pos = sort_dictionary.at(origin);
	//			int dest_pos = sort_dictionary.at(dest);
	//			if (heads.at(j).at(dest_pos) && origin_pos >= i) {
	//				for (int jp = j - 1; jp >= 0; jp--) {
	//					heads.at(jp).at(origin_pos) = true;
	//				}
	//				for (int jp = j; jp >= 0; jp--) {
	//					pe.at(jp).at(k) = true;
	//				}
	//			}
	//		}
	//	}

	//	// Front 
	//	vector<vector<bool>> tails(cycle_length, vector<bool>(v_size, false));
	//	tails[0][i] = true;
	//	for (int j = 0; j < cycle_length; j++) {
	//		for (int k = this->ad_arcs_size; k < this->arcs.size(); k++) {
	//			int origin = this->arcs.at(k).first;
	//			int dest = this->arcs.at(k).second;
	//			int origin_pos = sort_dictionary.at(origin);
	//			int dest_pos = sort_dictionary.at(dest);
	//			int weight = this->instance.getVertices().at(origin).getOutGoingArcs().at(dest);
	//			if (pe.at(j).at(k) && tails.at(j).at(origin_pos)) {
	//				if (j < cycle_length - 1) {
	//					tails.at(j + 1).at(dest_pos) = true;
	//				}
	//				if (ee.at(origin_pos).at(dest_pos).at(j) == false) {
	//					ee.at(origin_pos).at(dest_pos).at(j) = true;
	//					cout << "ADD " << i << " " << origin << " " << dest << " pos " << j << endl;
	//					this->arcs_cycles.at(i).push_back({ origin_pos,dest_pos,j,weight });
	//				}
	//			}
	//		}
	//	}

	//}

	vector<unordered_map<int, int>> dict_pair_graph_copy;

	this->arcs_cycles = vector<vector<vector<int>>>(this->instance.getPairsToProcess().size());
	int cycle_length = this->instance.getCyclesLength();
	for (int i = 0; i < this->instance.getPairsToProcess().size(); i++) {
		int graph_copy = this->instance.getPairsToProcess().at(i);
		for (const auto& pair : this->instance.getNodesPerPairToProcess().at(graph_copy)) {

			for (int k = 0; k < this->instance.getArcsPerPairToProcess().at(graph_copy).at(pair).size(); k++) {
				int dest = this->instance.getArcsPerPairToProcess().at(graph_copy).at(pair).at(k);
				int weight = this->instance.getVertices().at(pair).getOutGoingArcs().at(dest);
				
				if (pair == graph_copy) {
					//cout << "ADD " << graph_copy << " " << pair << " " << dest << " pos " << 1 << " i " << i << " size " << this->arcs_cycles.at(i).size() << endl;
					this->arcs_cycles.at(i).push_back({ pair,dest,1,weight });
				}
				else {
					for (int l = 2; l < this->instance.getCyclesLength(); l++) {
						/*if (pair > graph_copy && dest > graph_copy) {
							cout << "ADD " << graph_copy << " " << pair << " " << dest << " pos " << l << endl;
							this->arcs_cycles.at(i).push_back({ pair,dest,l,weight });
						}*/
						//cout << "ADD " << graph_copy << " " << pair << " " << dest << " pos " << l << " i " << i << " size " << this->arcs_cycles.at(i).size() << endl;
						this->arcs_cycles.at(i).push_back({ pair,dest,l,weight });
					}
					if (dest == graph_copy) {
						//cout << "ADD " << graph_copy << " " << pair << " " << dest << " pos " << this->instance.getCyclesLength() << " i " << i << " size " << this->arcs_cycles.at(i).size() << endl;
						this->arcs_cycles.at(i).push_back({ pair,dest,this->instance.getCyclesLength(),weight });
					}
				}
			}
		}

	}


}

Hpief::Hpief(const Hpief& p) : instance(p.instance) {
	this->env = p.env;
	this->model = p.model;
	this->arcs = p.arcs;
	this->ad_arcs_size = p.ad_arcs_size;
	this->time_limit = p.time_limit;
}

Hpief::~Hpief() {
}

void Hpief::solve() {

	clock_t	start = clock();

	try {

		this->model->set(GRB_DoubleParam_TimeLimit, this->time_limit);
		add_variables();
		add_constraints_kep();
		add_objective();

		//export_model(::instanceName + "_W_check.lp");
		mycallback_hpief cb = mycallback_hpief(this->incumbent_value, this->bound, this->bab_nodes);
		this->model->setCallback(&cb);

		start = clock();
		this->model->optimize();
		clock_t	end = clock();
		this->time = (double)(end - start) / CLOCKS_PER_SEC;

		int solCount = this->model->get(GRB_IntAttr_SolCount);
		this->status = this->model->get(GRB_IntAttr_Status);
		this->variable_number = this->model->get(GRB_IntAttr_NumVars);
		this->constraint_number = this->model->get(GRB_IntAttr_NumConstrs);
		this->bab_nodes = 0;

		if (solCount > 0) {
			extract_solution_stats();
		}
		write_stats();

		delete this->model;
		delete this->env;

		
	}
	catch (GRBException e) {
		cout << "[Hpief::solve()] - Error code = " << e.getErrorCode() << endl;
		cout << e.getMessage() << endl;
		this->status = this->model->get(GRB_IntAttr_Status);
		if (e.getErrorCode() == 10001) {
			clock_t	end = clock();
			this->time = (double)(end - start) / CLOCKS_PER_SEC;
			int solCount = this->model->get(GRB_IntAttr_SolCount);
			this->status = this->model->get(GRB_IntAttr_Status);
			this->variable_number = this->model->get(GRB_IntAttr_NumVars);
			this->constraint_number = this->model->get(GRB_IntAttr_NumConstrs);
			this->bab_nodes = 0;

			if (solCount > 0) {
				//extract_solution_stats();
			}
			write_stats();

			delete this->model;
			delete this->env;
		}
		else {
			exit(EXIT_FAILURE);
		}
	}
	catch (...) {
		cout << "[Hpief::solve()] - Error during optimization" << endl;
	}

}

void Hpief::extract_solution_stats() {
	this->incumbent_value = this->model->get(GRB_DoubleAttr_ObjVal);
	this->bound = this->model->get(GRB_DoubleAttr_ObjBound);
	this->bab_nodes = this->model->get(GRB_DoubleAttr_NodeCount);

	this->solution.clear();
	for (int i = 0; i < this->instance.getPairsToProcess().size(); i++) {
		int graph_id = this->instance.getPairsToProcess().at(i);
		vector<int> seq = { graph_id };
		//cout << "GRAPH " << graph_id << endl;
		int weight = 0;
		int length_cycle = -1;
		for (int l = 1; l < this->instance.getCyclesLength() + 1; l++) {
			for (int j = 0; j < this->arcs_cycles.at(i).size(); j++) {
				int lenght = this->arcs_cycles.at(i).at(j).at(2);
				if (lenght == l) {
					if (this->var_cycles_arcs.at(i).at(j).get(GRB_DoubleAttr_X) > 0.5) {
						int ori = this->arcs_cycles.at(i).at(j).at(0);
						int dest = this->arcs_cycles.at(i).at(j).at(1);
						weight += this->instance.getVertices().at(ori).getOutGoingArcs().at(dest);
						seq.push_back(dest);
						length_cycle = l;
						//cout << l << "\t" << j << "\t" << ori << "\t" << dest << "\t" << this->var_cycles_arcs.at(i).at(j).get(GRB_DoubleAttr_X) << "\n";
					}
				}
				//cout << endl;
			}
		}
		if (length_cycle > 1) {
			Exchange cycle = Exchange("cycle", weight, length_cycle, seq, this->instance.getVertexNumber());
			this->solution.push_back(cycle);
			//cout << cycle.toString() << endl;
		}
	}

	for (int i = 0; i < this->ad_arcs_size; i++) {
		if (this->var_chains_arcs.at(i).at(0).get(GRB_DoubleAttr_X) > 0.5) {
			//cout << "ad " << this->arcs.at(i).first << "\t" << this->arcs.at(i).second << endl;
			vector<int> seq = { 0, this->arcs.at(i).first,this->arcs.at(i).second };
			int length = 1;
			const Vertex& v = this->instance.getVertices().at(this->arcs.at(i).first);
			int weight = v.getOutGoingArcs().at(this->arcs.at(i).second);
			int current = this->arcs.at(i).second;
			int next = -1;
			do {

				int l_conv = length - 1;
				const Vertex& u = this->instance.getVertices().at(current);
				next = get_next_in_chain(u, l_conv);
				if (next != -1) {
					length++;
					weight += u.getOutGoingArcs().at(next);
					seq.push_back(next);
					current = next;
				}

			} while (next != -1 && length < this->instance.getChainsLength());
			length++;
			Exchange chain = Exchange("chain", weight, length, seq, this->instance.getVertexNumber());
			//cout << chain.toString() << endl;
			this->solution.push_back(chain);
		}
	}

	/*for (int i = this->ad_arcs_size; i < this->arcs.size(); i++) {
		for (int l = 0; l < this->var_chains_arcs.at(i).size(); l++) {
			if (this->var_chains_arcs.at(i).at(l).get(GRB_DoubleAttr_X) > 0.5) {
				cout << "pair " << this->arcs.at(i).first << "\t" << this->arcs.at(i).second << "\t" << l+1 << "\n";
			}
		}
	}*/
	this->transplant_number = 0;
	for (const auto& ex : this->solution) {
		for (int i = 1; i < ex.getSequence().size(); i++) {
			int id = ex.getSequence().at(i);
			if (instance.getVertices().at(id).getType() == 'I') {
				this->transplant_number++;
			}
		}
	}

	solution_check();
	//cout << toString() << endl;
}

void Hpief::write_stats() {
	if (this->fileName != "") {

		string path_solution_name = this->fileName + "_sol_exchanges.txt";
		string res = "OBJECTIVE VALUE:\t" + to_string(this->incumbent_value) + "\n";
		res.append("COLUMNS:\n");
		for (const auto& col : this->solution) {
			res.append("\t" + col.toString() + "\n\n");
		}
		::removeAndWriteFile(path_solution_name, res);


		this->csv_line = to_string(this->variable_number) + "," + to_string(this->constraint_number) + "," + to_string(this->bab_nodes) + "," + to_string(this->status) + "," + to_string(this->incumbent_value) + "," +
			to_string(this->bound) + "," + to_string(this->time) + "\n";

		ofstream mystream;
		mystream.open(::solutionPath + "Solutions_HPIEF_" + ::thread + ".csv", ios::app);
		mystream << this->fileName << "," << this->csv_line;
		mystream.close();

	}
}

void Hpief::add_variables() {

	//cycle vars
	this->var_cycles_arcs = vector<vector<GRBVar>>(this->instance.getPairsToProcess().size(), vector<GRBVar>());
	for (int i = 0; i < this->instance.getPairsToProcess().size(); i++) {
		int graph_id = this->instance.getPairsToProcess().at(i);
		for (int j = 0; j < this->arcs_cycles.at(i).size(); j++) {
			int ori = this->arcs_cycles.at(i).at(j).at(0);
			int dest = this->arcs_cycles.at(i).at(j).at(1);
			int l = this->arcs_cycles.at(i).at(j).at(2);
			string name = "arc_cycle_" + to_string(graph_id) + "_" + to_string(l) + "_" + to_string(ori) + "_" + to_string(dest);
			this->var_cycles_arcs.at(i).push_back(this->model->addVar(0.0, 1.0, 0.0, GRB_INTEGER, name));
		}
	}
	//arcs ad vars
	this->var_chains_arcs = vector<vector<GRBVar>>(this->arcs.size(), vector<GRBVar>());
	for (int i = 0; i < this->ad_arcs_size; i++) {
		string name = "arcs_ad_" + to_string(this->arcs.at(i).first) + "_" + to_string(this->arcs.at(i).second);
		this->var_chains_arcs.at(i).push_back(this->model->addVar(0.0, 1.0, 0.0, GRB_INTEGER, name));
	}
	for (int i = this->ad_arcs_size; i < this->arcs.size(); i++) {
		for (int l = 1; l < this->instance.getChainsLength() + 1; l++) {
			string name = "arcs_pairs_" + to_string(this->arcs.at(i).first) + "_" + to_string(this->arcs.at(i).second) + "_" + to_string(l);
			this->var_chains_arcs.at(i).push_back(this->model->addVar(0.0, 1.0, 0.0, GRB_INTEGER, name));
		}
	}

}

void Hpief::add_objective() {
	GRBLinExpr obj = 0;
	//KEP obj
	for (int i = 0; i < this->instance.getPairsToProcess().size(); i++) {
		for (int j = 0; j < this->arcs_cycles.at(i).size(); j++) {
			obj += this->arcs_cycles.at(i).at(j).at(3) * this->var_cycles_arcs.at(i).at(j);
		}
	}
	for (int i = 0; i < this->arcs.size(); i++) {
		const Vertex& v = this->instance.getVertices().at(this->arcs.at(i).first);
		int weight = v.getOutGoingArcs().at(this->arcs.at(i).second);
		for (int l = 0; l < this->var_chains_arcs.at(i).size(); l++) {
			obj += weight * this->var_chains_arcs.at(i).at(l);
		}
	}
	this->model->setObjective(obj, GRB_MAXIMIZE);
}

void Hpief::add_constraints_kep() {
	//KEP 
	//packing
	for (int i = 0; i < this->instance.getIncompatiblePairs().size(); i++) {
		int pair = this->instance.getIncompatiblePairs().at(i);
		GRBLinExpr sum = 0;
		for (int j = 0; j < this->arcs.size(); j++) {
			if (this->arcs.at(j).second == pair) {
				for (int l = 0; l < this->var_chains_arcs.at(j).size(); l++) {
					sum += this->var_chains_arcs.at(j).at(l);
				}
			}
		}
		for (int j = 0; j < this->instance.getPairsToProcess().size(); j++) {
			for (int k = 0; k < this->arcs_cycles.at(j).size(); k++) {
				if (this->arcs_cycles.at(j).at(k).at(0) == pair) {
					sum += this->var_cycles_arcs.at(j).at(k);
				}
			}
		}
		this->model->addConstr(sum <= 1, "packing_" + to_string(pair));
	}
	for (int i = 0; i < this->instance.getAltruisticDonors().size(); i++) {
		int ad = this->instance.getAltruisticDonors().at(i);
		GRBLinExpr sum = 0;
		for (int j = 0; j < this->ad_arcs_size; j++) {
			if (this->arcs.at(j).first == ad) {
				sum += this->var_chains_arcs.at(j).at(0);
			}
		}
		this->model->addConstr(sum <= 1, "packing_" + to_string(ad));
	}
	//Picef
	for (int i = 0; i < this->instance.getIncompatiblePairs().size(); i++) {
		int pair = this->instance.getIncompatiblePairs().at(i);
		for (int l = 1; l < this->instance.getChainsLength(); l++) {
			int l_conv = l - 1;
			GRBLinExpr sum = 0;
			if (l != 1) {
				//pairs start at level 2
				for (int j = this->ad_arcs_size; j < this->arcs.size(); j++) {
					if (this->arcs.at(j).second == pair) {
						sum += this->var_chains_arcs.at(j).at(l_conv);
					}
				}
			}
			for (int j = this->ad_arcs_size; j < this->arcs.size(); j++) {
				if (this->arcs.at(j).first == pair) {
					sum -= this->var_chains_arcs.at(j).at(l_conv + 1);
				}
			}
			if (l == 1) {
				for (int j = 0; j < this->ad_arcs_size; j++) {
					if (this->arcs.at(j).second == pair) {
						sum += this->var_chains_arcs.at(j).at(l_conv);
					}
				}
			}
			this->model->addConstr(sum >= 0, "picef_flow_" + to_string(pair) + "_" + to_string(l));
		}
	}
	//Hpief
	for (int i = 0; i < this->instance.getPairsToProcess().size(); i++) {
		int graph_id = this->instance.getPairsToProcess().at(i);
		//cout << "GRAPH " << graph_id << endl;
		for (int l = 1; l < this->instance.getCyclesLength() + 1; l++) {
			//cout << "Lenght " << l << endl;
			for (const auto& pair : this->instance.getNodesPerPairToProcess().at(graph_id)) {
				//cout << "Pair " << pair << endl;
				if (pair != graph_id) {
					GRBLinExpr sum = 0;
					bool out_exists = false;
					bool in_exists = false;
					for (int j = 0; j < this->arcs_cycles.at(i).size(); j++) {
						if (this->arcs_cycles.at(i).at(j).at(1) == pair && l == this->arcs_cycles.at(i).at(j).at(2)) {
							sum += this->var_cycles_arcs.at(i).at(j);
							in_exists = true;
						}
					}
					for (int j = 0; j < this->arcs_cycles.at(i).size(); j++) {
						if (this->arcs_cycles.at(i).at(j).at(0) == pair && l + 1 == this->arcs_cycles.at(i).at(j).at(2)) {
							sum -= this->var_cycles_arcs.at(i).at(j);
							out_exists = true;
						}
					}
					if (out_exists || in_exists) {
						this->model->addConstr(sum == 0, "pief_flow_" + to_string(graph_id) + "_" + to_string(pair) + "_" + to_string(l));
					}
				}
			}
		}
	}
	//for (int i = 0; i < this->instance.getPairsToProcess().size(); i++) {
	//	int graph_id = this->instance.getPairsToProcess().at(i);
	//	//cout << "GRAPH " << graph_id << endl;
	//	vector<int> to_zero;
	//	for (int j = 0; j < this->arcs_cycles.at(i).size(); j++) {
	//		int length = this->arcs_cycles.at(i).at(j).at(2);
	//		if (length >= 2) {
	//			bool is_found = false;
	//			int origin = this->arcs_cycles.at(i).at(j).at(0);
	//			//cout << "OR " << origin << " length " << length << endl;
	//			for (int k = 0; k < this->arcs_cycles.at(i).size(); k++) {
	//				if (this->arcs_cycles.at(i).at(k).at(2) == length - 1 && this->arcs_cycles.at(i).at(k).at(1) == origin) {
	//					is_found = true;
	//					break;
	//				}
	//			}
	//			if (!is_found) {
	//				to_zero.push_back(j);
	//			}
	//		}
	//	}
	//	if (to_zero.size() > 0) {
	//		GRBLinExpr sum_to_zero = 0;
	//		for (int j = 0; j < to_zero.size(); j++) {
	//			sum_to_zero += this->var_cycles_arcs.at(i).at(to_zero.at(j));
	//		}
	//		this->model->addConstr(sum_to_zero <= 0, "reduced_to_zero_" + to_string(i));
	//	}
	//}

	/*this->model->addConstr(this->var_cycles_arcs.at(1).at(6) == 1, "fix_" + to_string(9) + "_" + to_string(34));
	this->model->addConstr(this->var_cycles_arcs.at(1).at(9) == 1, "fix_" + to_string(34) + "_" + to_string(9));
	this->model->addConstr(this->var_cycles_arcs.at(3).at(2) == 1, "fix_" + to_string(17) + "_" + to_string(12));
	this->model->addConstr(this->var_cycles_arcs.at(3).at(0) == 1, "fix_" + to_string(12) + "_" + to_string(17));*/
	//Force to zero pairs in first position: not required but better
	/*GRBLinExpr sum = 0;
	for (int j = this->ad_arcs_size; j < this->arcs.size(); j++) {
		sum += this->var_chains_arcs.at(j).at(0);
	}
	this->model->addConstr(sum <= 0, "picef_to_zero");*/

	//reduced set by Dickerson
	GRBLinExpr sum = 0;
	for (int i = 0; i < this->instance.getIncompatiblePairs().size(); i++) {
		int pair = this->instance.getIncompatiblePairs().at(i);
		for (int j = 0; j < this->arcs.size(); j++) {
			if (this->arcs.at(j).first == pair) {
				int min_lenght = min(this->var_chains_arcs.at(j).size(), this->shortest_paths_from_ad.at(i));
				for (int l = 0; l < min_lenght; l++) {
					sum += this->var_chains_arcs.at(j).at(l);
				}
			}
		}
	}
	this->model->addConstr(sum <= 0, "reduced_to_zero");
}

void Hpief::export_model(const string& name) {
	this->model->write(name);
}

void Hpief::solution_check() {
	unordered_set<int> visits;
	double obj = 0.0;
	//cout <<toString()<<"\n";
	for (const auto& ex : this->solution) {
		obj += ex.getWeight();
		for (int i = 1; i < ex.getSequence().size(); i++) {
			if (visits.find(ex.getSequence().at(i)) != visits.end()) {
				cout << "double next no good packing constraints for " << ex.getSequence().at(i) << "\n";
				export_model(::instanceName + "_W_no_good_packing.lp");
				exit(EXIT_FAILURE);
			}
		}
	}
	if (!::equal(obj, round(this->incumbent_value))) {
		cout << "wrong obj " << obj << "\t" << this->incumbent_value << "\n";
		export_model(::instanceName + "_W_no_good_obj.lp");
		exit(EXIT_FAILURE);
	}
}

int Hpief::get_next_in_chain(const Vertex& v, const int& length) {
	int next = -1;
	for (int i = this->ad_arcs_size; i < this->arcs.size(); i++) {
		if (this->arcs.at(i).first == v.getId()) {
			//cout << "v " << v.getId() << " " << i << " " << length << endl;
			if (this->var_chains_arcs.at(i).at(length + 1).get(GRB_DoubleAttr_X) > 0.5) {
				if (next != -1) {
					cout << "double next no good packing constraints for " << v.getId() << "\n";
					export_model(::instanceName + "_W_no_good_packing.lp");
					exit(EXIT_FAILURE);
				}
				next = this->arcs.at(i).second;
			}
		}

	}
	return next;
}

const int& Hpief::get_status() const {
	return this->status;
}

const int& Hpief::get_transplant_number() const {
	return this->transplant_number;
}

const int& Hpief::get_variable_number() const {
	return this->variable_number;
}

const int& Hpief::get_constraint_number() const {
	return this->constraint_number;
}

const double& Hpief::get_incumbent_value() const {
	return this->incumbent_value;
}

const double& Hpief::get_bound() const {
	return this->bound;
}

const double& Hpief::get_time() const {
	return this->time;
}

const bool& Hpief::get_is_optimal() const {
	return this->is_optimal;
}

const string& Hpief::get_csv_line() const {
	return this->csv_line;
}

const vector<Exchange>& Hpief::get_solution() const {
	return this->solution;
}

string Hpief::toString() const {
	string res = "OBJ: " + to_string(this->incumbent_value) + "\tBOUND: " + to_string(this->bound) + "\tTIME: " + to_string(this->time) + "\n\tSOLUTION:\n";
	for (const auto& ex : this->solution) {
		res += "\t" + ex.toString() + "\n";
	}
	return res;
}

Hpief& Hpief::operator=(const Hpief& p) {
	this->time_limit = p.time_limit;
	return *this;
}
